博客园  :: 首页  :: 新随笔  :: 管理

2.数据结构与算法绪论

Posted on 2022-05-26 16:47  wsg_blog  阅读(46)  评论(0编辑  收藏  举报

index 数据结构与算法

数据结构(Data Structure)

数据结构在某种程度上和设计模式类似,都是前辈的武功套路。不同的是,设计模式是近几十年的卓越程序员的智慧结晶,而数据结构是几百上千年的无数科学家、数学家的智慧沉淀,更加具有深厚的背景。

逻辑结构

逻辑结构是指我们通常说的软件层面,有四种结构:集合结构(set)、线性结构(vector,list)、树形结构(tree)、图形结构(graph)

物理结构

物理结构是指我们通常说的硬件层面(很多书中也叫做存储结构),存储形式有两种:顺序存储结构、链式存储结构

算法(Algorithm)

时间复杂度

好的算法需具备正确性、可读性强、健壮及时间效率高和存储量低的特点

事前分析估算方法:测试运行时间最可靠的方法就是计算运行时间有消耗的基本操作的执行次数。运行时间与这个计数成正比。
一般来说对数据结构及算法需求大的方向为大数据相关的项目,当数据量比较大时,判断一个算法的效率,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高阶项)的阶数。

推导大O阶方法

  1. 用常数1取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,只保留最高阶项。
  3. 如果最高阶项存在且不是1,则去除与这个项相乘的常数。
常数阶
//常数是可以计数的值,10、100、1000、10000...这类都可称为常数
int sum=0,n=100;           /*执行一次*/
sum=(1+n)*n/2;             /*执行一次*/
printf("%d", sum);         /*执行一次*/

这个算法的运行次数函数是\(f(n)=3\)。根据我们推导大O阶的方法,第一步就是把常数项3改为1。在保留最高阶项时发现,它根本没有最高阶项,所以这个算法的时间复杂度为\(O(1)\)

线性阶

下面这段代码,它的循环的时间复杂度为\(O(n)\),因为循环体中的代码须要执行n次。

int i;
for(i=0; i<n; i++){
  /*时间复杂度为O(1)的程序步骤序列*/
}
对数阶
int count=1;
while(count<n){
    count=count*2;
    /*时间复杂度为O(1)的程序步骤序列*/
}

由于每次count乘以2之后,就距离n更近了一分。\(2^x=n\)得到\(x=log_{2}n\)。所以这个循环的时间复杂度为\(O(logn)\)

平方阶
int i,j;
for(i=0; i<n; i++){
    for(j=0; j<n; j++){
        /*时间复杂度为O(1)的程序步骤序列*/
    }
}

时间复杂度为\(O(n^2)\)

常见的时间复杂度表

--------执行次数-------- ---函数阶--- 非正式术语
\(12\) \(O(1)\) 常数阶
\(2n+3\) \(O(n)\) 线性阶
\(3n^2+2n+1\) \(O(n^2)\) 平方阶
\(5log_{2}n+20\) \(O(logn)\) 对数阶
\(2n+3nlog_{2}n+19\) \(O(nlogn)\) \(nlogn\)
\(6n^3+2n^2+3n+4\) \(O(n^3)\) 立方阶
\(2^n\) \(O(2^n)\) 指数阶

常用的时间复杂度所耗费的时间从小到大依次是:\(O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)\)
\(O(n^3)\)过大的n都会使得结果变得不现实.同样指数阶\(O(2^n)\)和阶乘\(O(n!)\)等除非是很小的n值,否则哪怕n只是100,都是噩梦的运行时间。


很多程序员,做了很长时间的编程工作,却始终都弄不明白算法的时间复杂度的估计,这是很可悲的一件事。因为弄不清楚,所以也就从不深究自己写的代码是否效率低下,是不是可以通过优化让计算机更加快速高效。他们通常的借口是,现在CPU越来越快,根本不用考虑算法的优劣,实现功能即可,用户感觉不到算法好坏造成的快慢。可事实真是这样吗?
假设CPU在短短几年间,速度提高了100倍,这其实已经很夸张了。而我们的某个算法本可以写出时间复杂度是\(O(n)\)的程序,却写出了\(O(n^2)\)的程序,仅仅因为容易想到,也容易写。即在\(O(n^2)\)的时间复杂度算法程序下,速度其实只提高了10倍!