数据结构4
算法:
数据结构中的算法,指的是数据结构所具备的功能
解决特定问题的方法。学习的前辈们的一些优秀的经验总结
算法的五大特征:
(1)有穷性。一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
(2) 确定性。对千每种情况下所应执行的操作,在算法中都有确切的规定,不会产生二义性, 使算法的执行者或阅读者都能明确其含义及如何执行。
(3) 可行性。算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现。
(4) 输入。一个算法有零个或多个输入。当用函数描述算法时,输入往往是通过形参表示的, 在它们被调用时,从主调函数获得输入值。
(5) 输出。一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的 算法没有任何意义。当用函数描述算法时,输出多用返回值或引用类型的形参表示。
如何评价一个算法:
时间复杂度:
由于计算机的性能不同,无法准确地确定一个算法的执行时间,因此使用执行算法的次数来代表算法的时间复杂度,一般用O(公式)来表示
注意:算法的时间复杂度不能完全反映一个算法的实际时间,有些算法看似时间复杂度高,但是速度也快,例如选择排序
常见的时间复杂度:
//O(1)
printf("%d\n",num);
//O(logN)
for(int i=1;i<=N;i*=2)
{
printf("%d\n",i);
}
//O(N)
for(int i=0;i<N;i++)
{
printf("%d\n",i);
}
//O(NlogN)
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j/=2)
{
printf("%d\n",i);
}
}
// O(N^2)
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
printf("%d\n",i);
}
}
空间复杂度:
执行一个程序(算法)所需要的内存空间的大小,是对一个算法在运行过程中临时占用存储空间大小的衡量
通常来说,只要这个算法不涉及动态分配内存以及递归,通常空间复杂度为O(1)
例如:求第N项斐波那契数列,空间复杂度为O(N),因为用到了递归
注意:一个算法的时间复杂度和空间复杂度,往往是相互影响的,如何选择根据实际情况
分治:
是一种算法思想,而不是某种特定的算法,分治就是把一个大而复杂的问题,分解成很多小而简单的问题,利用计算机强大的计算能力来解决所有小问题,从而解决最开始的问题
实现分治的具体方法:循环、递归
查找算法:
顺序查找:
对待查找的数据没有特殊要求,从头到尾逐一查找比较,在小规模数据的查找中较为常见。平均效率较低
时间复杂度是O(N)
二分查找(折半查找)
对待查找的数据必须有序,从中间位置开始查找,如果中间值比key要大,则从左边继续二分查找,否则从右边二分查找
时间复杂度O(logN)
块查找(权重查找):
是一种数据处理的思想,不是特定的算法实现,当待处理的数据量很巨大时,可以根据数据的特征进行分块处理,然后再对每块数据进行查找。
例如:英文词典、通讯录
哈希查找Hash:
先把待查找的数据经过哈希函数,计算出该数据在哈希表中的位置,然后做标记,后续就可以很方便地查找数据,它的时间复杂度最高能到O(1)
缺点:该算法有很大的局限性,不适合查找浮点型数据,需要额外的内存空间、空间复杂度高,是一种典型的以空间换时间的算法。
哈希函数设计方法:
1、直接定址法:直接把数据作为它在哈希表中的位置来做标记
2、数据分析法:先分析出数据的最大值、最小值,然后通过 最大值-最小值+1 来确定哈希表的长度,根据哈希函数 数据-最小值 进行标记和查找哈希表
3、平方取中法、折叠法、随机数法等
注意:无论哪种方法都可能会出现哈希冲突(有重复数据时无法确定具体数据),一般采用链表配合解决
Hash函数的应用:MD5加密算法、SHA-1都属于Hash算法
排序算法:
排序算法的稳定性:
在待排序的数据中,对于数值相同的数据,在整个排序过程中如果不会改变他们原来的先后顺序,则认为该排序算法是稳定的
冒泡排序:
数据左右比较,把较大的数据交换到右边,往后重复以上操作,直到把最大的数据交换到最后,特点是该算法对数据的有序性敏感,如果在一次的排序过程中没有发生一次交换
那么意味着数据已经有序。可以立即停止排序。
时间复杂度:平均:O(N^2) 最优:O(N)
适合待排序的数据基本有序时,则冒泡的效率非常高。
稳定的
选择排序:
假设最开始的位置是最小值并记录下标,然后与后面所有数据比较,如果有比min位置更小的数,那么更新min,结束后min的下标与开始时发生过改变,才进行交换到开始位置。
虽然时间复杂度较高,但是数据交换次数比较少,因此实际的运行速度并不慢(数据交换比数据比较耗时间)
时间复杂度:O(N^2)
不稳定 例如 10 10 1
插入排序:
把数据看成两个部分,前部分是有序的,把剩余部分的数据逐个往前比较,如果比前面的数小,前面的数往后移动一位,继续往前比较,直到遇到更小的数,那么该数字的后一个位置便是
需要插入的位置
适合对已经排序后的数据,新增数据后再排序
时间复杂度:O(N^2)
稳定的
希尔排序:
是插入排序的增强版,由于插入排序时数据移动的步长较短,都是1,所以增加了增量的概念,通过不停地缩减增量,最终步长还是变回1,可以提高排序的效率
当待排序数据远离最终位置时,希尔排序的效率高于插入排序
时间复杂度:O(NlogN)~O(N^2)
不稳定
快速排序:
在待排序数据中先找一个标杆位置p,备份p位置的值val,记录左标杆l、右标杆r,l从p的左边找比val大的数,找到后赋值给p位置,更新p到l,然后r从p的右边找比val小的数
找到后赋值给p的位置,更新p到r
循环往复,直到l和r重合,把val还原回p位置完成一次快排,然后用同样的方式对p的左右进行快排
它的综合性能最优,因此得名快速排序,笔试时考的最多
时间复杂度:O(NlogN)
不稳定
堆排序:
把待排序数据当作完全二叉树看待,先把二叉树调整成大顶堆结构,把根节点的值与末尾节点的值交换,然后数量范围-1,重新从上到下调整回大顶堆,继续以上操作,
直到数量为1时结束,此时全部数据就从小到大排序了。
既可以顺序实现,也可以递归实现
时间复杂度:O(N*logN)
不稳定
归并排序:
首先需要把数据拆分成单独的个体,然后按照从小到大的顺序比较后排序到一段临时空间中,把排序后的临时空间中的数据拷贝回原内存,然后依次把有序的个体继续以上
操作合并成更大的有序部分
归并排序不需要交换数据,减少了交换的耗时,但是需要额外的存储空间,是一种典型的以空间换时间的算法
时间复杂度:O(N*logN)
稳定的
计数排序:
找出待排序数据中的最大、最小值,创建长度为最大值-最小值+1的哈希表,根据哈希函数 数据-最小值 当做哈希表的下标并对所有数据做标记,然后从头遍历哈希表,
当某个位置的值大于0,把该位置的下标+最小值的结果依次被放回原数据内存中
是一种典型的以空间换时间的算法,理论上该算法的速度非常快的,它不是基于比较多排序算法,在一定范围内的整数排序中快于任意一种基于比较的排序算法,但是有很大的局限性
只适合整型数据排序,数据的差值不宜太大,重复数比较多的情况下性价比很高
时间复杂度:O(n+k)(其中的k是整数的范围)
稳定的
桶排序:
一般是把数据根据数值分到不同的“桶”,通过不同的,合适的其他排序算法对“桶”中的数据进行排序,然后再把各个“桶”的数据依次拷贝拷贝回原内存中
桶排序是一种降低排序规模的排序思想,也是以空间换时间的算法。
缺点:如何分桶、桶如何定义,都需要针对具体待排序的数据进行分析后才能确定
桶排序的稳定性取决于桶内排序使用的算法
有些资料上认为桶排序可以做到稳定,所以认为桶排序稳定
基数排序:
33 23 51 123 5344 1 663 346 31 78
51 1 31 33 23 123 663 5344 346 78
1 23 123 31 33 5344 346 51 663 78
1 23 31 33 51 78 123 5344 346 663
1 23 31 33 51 78 123 346 663 5344
0
1
2
3
4
5
6
7
8
9