python学习-09(查找排序和浅谈数据结构)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python学习-09(查找排序和浅谈数据结构)相关的知识,希望对你有一定的参考价值。

查找的方法:

排序的方法:

简单的数据结构:

一、算计基础

1.1、什么是算法:

算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间、空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。

简单的说,算法就是一个计算的过程,解决问题的办法。

一个算法应该具有以下七个重要的特征:

①有穷性(Finiteness):算法的有穷性是指算法必须能在执行有限个步骤之后终止;

②确切性(Definiteness):算法的每一步骤必须有确切的定义;

③输入项(Input):一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件;

④输出项(Output):一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;

⑤可行性(Effectiveness):算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步,即每个计算步都可以在有限时间内完成(也称之为有效性);

⑥高效性(High efficiency):执行速度快,占用资源少;

⑦健壮性(Robustness):对数据响应正确。

 

1.2、时间复杂度

定义:如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数 T(n)称为这一算法的“时间复杂性”。 

当输入量n逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性”。

此外,一个问题本身也有它的复杂性,如果某个算法的复杂性到达了这个问题复杂性的下界,那就称这样的算法是最佳算法。

 

计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间,时间复杂度常用大O符号(大O符号(Big O notation)是用于描述函数渐进行为的数学符号。更确切地说,它是用另一个(通常更简单的)函数来描述一个函数数量级的渐近上界。在数学中,它一般用来刻画被截断的无穷级数尤其是渐近级数的剩余项;在计算机科学中,它在分析算法复杂性的方面非常有用。)表述,使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。

大O,简而言之可以认为它的含义是“order of”(大约是)

无穷大渐近
大O符号在分析算法效率的时候非常有用。举个例子,解决一个规模为 n 的问题所花费的时间(或者所需步骤的数目)可以被求得:T(n) = 4n^2 - 2n + 2。
当 n 增大时,n^2; 项将开始占主导地位,而其他各项可以被忽略——举例说明:当 n = 500,4n^2; 项是 2n 项的1000倍大,因此在大多数场合下,省略后者对表达式的值的影响将是可以忽略不计的。

时间复杂度的计算:

1).一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。

一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)

2.一般情况下,算法的基本操作重复执行的次数是模块n的某一个函数f(n),因此,算法的时间复杂度记做:T(n)=O(f(n))。随着模块n的增大,算法执行的时间的增长率和f(n)的增长率成正比,所以f(n)越小,算法的时间复杂度越低,算法的效率越高。

在计算时间复杂度的时候,先找出算法的基本操作,然后根据相应的各语句确定它的执行次数,再找出T(n)的同数量级(它的同数量级有以下:1,Log2n ,n ,nLog2n ,n的平方,n的三次方,2的n次方,n!),找出后,f(n)=该数量级,若T(n)/f(n)求极限可得到一常数c,则时间复杂度T(n)=O(f(n))。

3.常见的时间复杂度

按数量级递增排列,常见的时间复杂度有:

常数阶O(1),  对数阶O(log2n),  线性阶O(n),  线性对数阶O(nlog2n),  平方阶O(n2), 立方阶O(n3),..., k次方阶O(nk), 指数阶O(2n) 。

其中,

1.O(n),O(n2), 立方阶O(n3),..., k次方阶O(nk) 为多项式阶时间复杂度,分别称为一阶时间复杂度,二阶时间复杂度。。。。

2.O(2n),指数阶时间复杂度,该种不实用

3.对数阶O(log2n),   线性对数阶O(nlog2n),除了常数阶以外,该种效率最高

例:算法:

 1   for(i=1;i<=n;++i)
 2 
 3   {
 4 
 5      for(j=1;j<=n;++j)
 6 
 7      {
 8 
 9          c[ i ][ j ]=0; //该步骤属于基本操作 执行次数:n^2
10 
11           for(k=1;k<=n;++k)
12 
13                c[ i ][ j ]+=a[ i ][ k ]*b[ k ][ j ]; //该步骤属于基本操作 执行次数:n^3
14 
15      }
16 
17   }

  则有 T(n)= n^2+n^3,根据上面括号里的同数量级,我们可以确定 n^3为T(n)的同数量级

  则有f(n)= n^3,然后根据T(n)/f(n)求极限可得到常数c

  则该算法的 时间复杂度:T(n)=O(n^3)

时间复杂度为:O(1) 

1 Temp=i;
2 i=j;
3 j=temp;                 

以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。 

时间复杂度为:O(n2

交换i和j的内容

1      sum=0;                 (一次)
2      for(i=1;i<=n;i++)       (n次 )
3         for(j=1;j<=n;j++) (n^2次 )
4          sum++;       (n^2次 )

解:T(n)=2n^2+n+1 =O(n^2) 

例子    

1   for (i=1;i<n;i++)
2     {
3         y=y+1;         ①  
4         for (j=0;j<=(2*n);j++)   
5            x++;        ②     
6     }        

解: 语句1的频度是n-1

          语句2的频度是(n-1)*(2n+1)=2n^2-n-1

          f(n)=2n^2-n-1+(n-1)=2n^2-2

          该程序的时间复杂度T(n)=O(n^2).         

时间复杂度为:O(n)                                                        

1     a=0;
2     b=1;                      ①
3     for (i=1;i<=n;i++) ②
4     { 
5        s=a+b;    ③
6        b=a;     ④ 
7        a=s;     ⑤
8     }

解:语句1的频度:2,       

           语句2的频度: n,       

          语句3的频度: n-1,       

          语句4的频度:n-1,   

          语句5的频度:n-1,                                 

          T(n)=2+n+3(n-1)=4n-1=O(n).                                                                                              

时间复杂度为:O(log2n ) 

1      i=1;       ①
2     while (i<=n)
3        i=i*2; ②

解: 语句1的频度是1, 

          设语句2的频度是f(n),   则:2^f(n)<=n;f(n)<=log2n   

          取最大值f(n)= log2n,

          T(n)=O(log2n )

时间复杂度为:O(n3)   

1   for(i=0;i<n;i++)
2     { 
3        for(j=0;j<i;j++) 
4        {
5           for(k=0;k<j;k++)
6              x=x+2; 
7        }
8     }

解:当i=m, j=k的时候,内层循环的次数为k当i=m时, j 可以取 0,1,...,m-1 , 所以这里最内循环共进行了0+1+...+m-1=(m-1)m/2次所以,i从0取到n, 则循环共进行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以时间复杂度为O(n^3).                            

我们还应该区分算法的最坏情况的行为和期望行为。如快速排序的最 坏情况运行时间是 O(n^2),但期望时间是 O(nlogn)。通过每次都仔细 地选择基准值,我们有可能把平方情况 (即O(n^2)情况)的概率减小到几乎等于 0。在实际中,精心实现的快速排序一般都能以 (O(nlogn)时间运行。

下面是一些常用的记法: 

访问数组中的元素是常数时间操作,或说O(1)操作。一个算法如 果能在每个步骤去掉一半数据元素,如二分检索,通常它就取 O(logn)时间。用strcmp比较两个具有n个字符的串需要O(n)时间。常规的矩阵乘算法是O(n^3),因为算出每个元素都需要将n对 元素相乘并加到一起,所有元素的个数是n^2。

指数时间算法通常来源于需要求出所有可能结果。例如,n个元 素的集合共有2n个子集,所以要求出所有子集的算法将是O(2n)的。指数算法一般说来是太复杂了,除非n的值非常小,因为,在 这个问题中增加一个元素就导致运行时间加倍。不幸的是,确实有许多问题 (如著名的“巡回售货员问题” ),到目前为止找到的算法都是指数的。如果我们真的遇到这种情况,通常应该用寻找近似最佳结果的算法替代之。

 

时间复杂度小结:

间复杂度是用来估计算法运行时间的一个式子(单位)。

一般来说,时间复杂度高的算法比复杂度低的算法快。

常见的时间复杂度(按效率排序)

O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n2log2n)<O(n3)

不常见的时间复杂度(看看就好)

O(n!) O(2n) O(nn) …

 

如何一眼判断时间复杂度?

循环减半的过程?O(logn)

几次循环就是n的几次方的复杂度

 

3、空间复杂度

空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息。一个算法的优劣主要从算法的执行时间和所需要占用的存储空间两个方面衡量。

一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。空间复杂度(SpaceComplexity)是对一个算法在运行过程中临时占用存储空间大小的量度。一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间和算法在运行过程中临时占用的存储空间这三个方面。算法的输入输出数据所占用的存储空间是由要解决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。存储算法本身所占用的存储空间与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。算法在运行过程中临时占用的存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地\"进行的,是节省存储的算法,有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序算法就属于这种情况。

分析一个算法所占用的存储空间要从各方面综合考虑。如对于递归算法来说,一般都比较简短,算法本身所占用的存储空间较少,但运行时需要一个附加堆栈,从而占用较多的临时工作单元;若写成非递归算法,一般可能比较长,算法本身占用的存储空间较多,但运行时将可能需要较少的存储单元。

一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为 递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。算法的空间复杂度一般也以数量级的形式给出。如当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为O(log2n);当一个算法的空间复杂度与n成线性比例关系时,可表示为O(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。

时间复杂度与空间复杂度比较:

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。算法的时间复杂度和空间复杂度合称为算法的复杂度。

二、查找

列表查找:从列表中查找指定元素

输入:列表、待查找元素

输出:元素下标或未查找到元素

常见的查找方法有两种: 

2.1顺序查找

从列表第一个元素开始,顺序进行搜索,直到找到为止。

查找成功时的平均查找长度为:(假设每个数据元素的概率相等) ASL = 1/n(1+2+3+…+n) = (n+1)/2 ;

当查找不成功时,需要n+1次比较,时间复杂度为O(n);

所以, 顺序查找的时间复杂度为O(n ) 。

顺序查找的实现:

 

2.2二分查找

从有序列表的候选区data[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。

说明:元素必须是有序的,如果是无序的则要先进行排序操作。

基本思想:也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。

复杂度分析: 最坏情况下,关键词比较次数为log2(n+1),且 期望时间复杂度为O(log2n) ;

注: 折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要 频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。

 



以上是关于python学习-09(查找排序和浅谈数据结构)的主要内容,如果未能解决你的问题,请参考以下文章

Python100天学习笔记Day17 数据结构与算法

Python100天学习笔记Day17 数据结构与算法

浅谈Java中的深拷贝和浅拷贝(转载)

浅谈Java中的深拷贝和浅拷贝

浅谈深拷贝和浅拷贝及几种实现方法

代码学习PYTHON 深拷贝和浅拷贝