算法:
数据结构中的算法,指的是数据结构所具备的功能
解决特定问题的方法,它是前辈们的一些优秀的经验总结
算法有五大特征:可行性、有穷性、确定性、输入、输出
如何评价一个算法
时间复杂度
由于计算机性能的不同,无法准确地统计出算法执行所需要的时间
因此采用算法的执行次数来代表算法的时间复杂度,一般采用O(公式)忽略常数
常见的时间复杂度
// O(1)
printf("%d",i);
// O(logn)
for(int i=0; i>=0; i/=2)
{
printf("%d",i);
}
// O(n)
for(int i=0; i<n; i++)
{
printf("%d",i);
}
// O(nlogn)
for(int i=0; i<n; i++)
{
for(int j=n; j>=n; j/=2)
{
printf("%d",i);
}
}
// O(n^2)
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
printf("%d",i);
}
}
空间复杂度:
执行一个程序所需要的额外的内存空间大小,是对一个算法在运行过程中额外临时占用的空间大小的一个度量
通常来说,只要算法不涉及动态分配的空间以及递归,基本上空间复杂度O(1)
分治:分而治之
把一个大而复杂的问题,分解成若干个小而简单的问题,利用计算机强大的计算能力来解决所有小问题,是一种算法思想
实现分治思想的方法:循环、递归
查找:
顺序查找:
对待查找的数据没有要求,从头到尾逐一比较,在小规模的数据查找中比较常见,相比较而言效率较低
时间复杂度:O(n)
二分查找:折半查找
待查找的数据必须有序,从中间位置数据开始比较查找,如果中间值比key小,则从左部分进行二分查找,反之比key大,则从右部分进行二分查找,如果相同则查找成功,当查找的范围缩小为0,则查找失败
时间复杂度:O(logn)
块查找:
是一种数据查找处理的思想,不是特定的算法,当数据量过多时,可以把数据进行特定的分块处理,然后再进行查找,例如单词字典
哈希查找:Hash
数据先经过哈希函数计算出该数据在哈希表中的位置,然后标记该位置的数据,之后查找数据直接从哈希表中查找,它的查找时间复杂度最高能到 O(1)
但是该算法有很大的局限性,不适合浮点型、有符号负数数据,还需要额外的内存空间,空间复杂度很高,还不好确定额外的空间大小,是一种典型的空间换时间的算法
设计哈希函数的方法:
1、直接定址法:把数据当做哈希表的下标来标记哈希表
2、数据分析法:
先分析数据的特点然后设计哈希函数,常用方法是先找出最大、最小值,哈希表长度:最大-最小+1,哈希函数:数据-最小值 作为哈希表下标进行标记
平方折中法、折叠法、随机数法等方法设计哈希函数,如果出现相同数据时,哈希表会出现所谓的哈希冲突,一般使用链表解决
hash函数应用:MD5、SHA-1都属于Hash算法
排序算法:
排序算法的稳定性:
待排序数据中,如果存在值相同的数据,如果在排序的过程中不会改变它们的前后顺序,则认为该排序算法是稳定的
冒泡排序:
数据左右相比,把大的数据交换到右边,然后继续下两个数据左右相比,直到最大的数据交换到末尾,重复以上步骤。
注意:在冒泡排序中一旦发现数据已经有序,立即停止排序,称为有序性敏感
冒泡排序适合数据基本有序的情况,冒泡的效率是非常高
时间复杂度:最好 O(n) 平均 O(n^2)
稳定性: 稳定的
选择排序:
假定最开始的位置是最小值并记录下标min,然后与后面的数据进行比较,如果有比min为下标的数据更小的值,则更新min,如果比较结束后,min的位置发生了改变,则交换最开始的位置与min位置的值
虽然时间复杂度没有变化,但是数据的交换次数比较少,因此实际的运行速度并不慢(数据交换要比赋值、数据比较要耗时)
选择排序相当于冒泡排序的一种变种,但是不存在有序性敏感,比较适合数据比较混乱的情况会比冒泡快
时间复杂度:O(n^2)
稳定性:不稳定
10 10 1
a b
插入排序:
把数据看成两部分,一部分是有序的,后一部分的数据与有序部分的数据逐个比较,直到找到合适的位置后,插入到该位置
适合对已排序的数据新增数据后进行排序
时间复杂度:O(n^2)
稳定的
希尔排序:缩小增量排序
是插入排序的一个增强版,由于插入排序数据移动的速度比较慢,如果待排序数据很混乱时效率较低,因此引入增量的概念,数据间隔增量个数进行插入排序,然后不停地缩小一倍增量,直到增量为1,此时数据从很混乱变为接近有序,以此提高排序的效率
时间复杂度:O(nlogn)
不稳定的
快速排序:
在中间位置确定为标杆p,备份标杆的数据为val,从左边找比val大的数据,找到后位置为l,然后把该数据赋值给p位置,更新p为l,然后从右边找比val小的数据,找到后位置为r,然后把该数据赋值给p位置,更新p为r,重复以上过程,直到l和r相遇时,结束此次排序,最后把val还原赋值给p位置,最终p左边都比val小,右边都比val大,局部有序
继续对左部分快排,右部分快排,直到全部有序为止
注意:
快排的综合性能是最高的,因此称为快速排序,也是笔试最常考的排序算法
时间复杂度:O(nlogn)
不稳定的
归并排序:
先将数据拆分成单独的个体,然后再按照从小到大的顺序把两组数据合并到临时空间中,合并结束后,再重新拷贝回原空间的原位置中,得到数量翻倍的一组组有序的数据,继续两组两组合并,直到整体有序为止
归并排序是利用了额外的内存空间,避免了数据交换的耗时,是一种典型的以空间换时间的算法
时间复杂度:O(nlogn)
稳定的
堆排序:
先把数据看成一棵完全二叉树,然后先调整成大顶堆或小顶堆,然后把根节点与末尾交换,然后有效个数-1,重新调整成堆结构,继续以上步骤,直到有效个数为1,此时整个数组就有序了,既可以循环也可以递归实现
大顶堆:堆排序后 从小到大
小顶堆:堆排序后 从大到小
时间复杂度:O(nlogn)
不稳定 例如: 1 10 10
计数排序:
在数据中找到最大值和最小值,通过 最大值-最小值 方式创建哈希表,然后根据 数据-最小值 的值作为哈希表的下标访问并标记(累加)对应的哈希表。然后标记所有数据
排序时,遍历整个哈希表,如果表中数据非0时,把该位置下标+最小值 依次存回排序数组中,遍历结束数组有序
桶排序:
根据数据的值,存储到不同的桶中,然后调用其他的排序算法,对桶中的数据进行排序,然后再全部按桶的顺序拷贝回数组中,以此降低排序的规模来提高排序速度,以空间换时间的排序算法
缺点:如何分桶、桶有几个、桶定义多大都不一定,需要对排序数据有一定的了解和分析后才能确定
时间复杂度:Ο(n)
稳定的
基数排序:
是桶排序思想的其中一种具体实现,首先创建10个队列(桶),然后依次按照数据的个、十、百位...的顺序,入队到对应的桶中,然后依次每个出队回原来的数组中,当最高位都处理完且返回数组后,数组就有序了
缺点:只适合排序正整数数据,需要准备大量的临时空间
时间复杂度:Ο(n)
稳定的