算法:
数据结构中的算法,指的是数据结构所具备的功能
解决特定问题的方法,它是前辈们的一些优秀的经验总结
算法的五大特性:
输入: 算法具有0个或多个输入。
输出: 算法至少有1个或多个输出。
有穷性: 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成。
确定性:算法中的每一步都有确定的含义,不会出现二义性。
可行性:算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完
如何评价一个算法:
时间复杂度:
由于计算机性能不同,无法准确地统计出一个算法执行所需要的时间
因此我们使用算法执行的次数来代表算法的时间复杂度,以 O(公式) 一般忽略常数
常见的时间复杂度:
// O(1)
printf("%d\n",i);
// O(logN)
for(int i=N; i>=0; 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=N; j>=0; 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(N)
二分查找:
对待查找的数据必须保证有序,从数据的中间位置开始查找,如果key比中间值小,则从左边继续二分查找,否则从右边二分查找
时间复杂度:O(logN)
块查找(权重查找):
是一种数据处理的思想,不是一种特定的算法,当数据量非常多时
可以先把数据进行分块处理,然后再进行查找,例如英文词典
哈希查找:
把数据经过哈希函数计算出该数据在哈希表中的位置,然后标记每个位置,方便之后的查找
,她的时间复杂度最好能达到O(1)
注意:该算法有很大的局限性,需要额外的存储空间,空间复杂度较高,是一种典型的以空间换时间的
算法,而且不适合浮点型、字符型数据。
最简单的哈希函数设计:
直接定址法:直接把数据作为哈希表的下标进行标记
数据分析法:对数据进行分析从而设计哈希函数,最常用
的方法是,找到最大值、最小值,用最大值-最小值+1
确定哈希表长度,用数据-最小值访问哈希表
平方取中法、折叠法、随机数法,但是都无法确保哈希数据的唯一性
会出现哈希冲突,一般使用链表法解决
排序算法:
排序算法的稳定性:
待排序的数据中,如果有值相同的数据,在排序过程中如果不会改变他们的前后顺序
则认为该算法稳定
冒泡排序:
数据左右进行比较,把最大的值交换到最后,
特点是该算法对数据的有序性敏感,在排序的
过程中一旦发现有序可以立即停止,如果待排序
的数据基本有序,则冒泡的效率非常高
时间复杂度:最优O(n) 平均:O(n^2)
稳定的
选择排序:
假定最开始的位置是最小值,并记录下标,然后与后面的数据进行比较,如果有比
min下标的数据还要小的数据,则更新min的位置,如果min的位置发生改变,则把敏位置
的数据与最开头位置的数据进行交换
虽然时间复杂度较高,但是数据交换次数较少,因此实际的运行速度并不慢
选择排序是冒泡排序的一个变种,但是没有堆数据的有序性是敏感,如果数据较混乱时
选择比冒泡快
时间复杂度:O(N^2)
不稳定的
插入排序:
把数据堪称两部分,一部分是有序的,把剩余的数据逐步插入到有序部分中
适合堆已排序后的数据,新增一些数据后再排序的情况
时间复杂度:O(n^2)
稳定的
希尔排序:
是插入排序的增强版,由于插入排序时,数据移动的跨度较小,速度较慢
因此希尔排序增加了增量的概念,以此来提高排序的速度,最后的增量一定是1
快速:
找到一个标杆pi(一般设置为中间位置),备份标杆位置的值pv,一边从pi左边
找比pv大的数据,如果找到了则把数据赋值给pi位置,更新标杆pi到该数据位置
继续从右边找到比pv小的数据,如果找到则把数据赋值给pi位置,更新标杆pi到
该数据位置,循环进行以上操作,最终左右标杆会相遇,最后把pv赋值给相遇处,这样
就能实现pv左边的数据都比它小,右边都比它大,然后按照同样的方法,对左右两边
继续快排,最终整体有序。
它的综合性能最高,因此叫做快速排序,笔试中考得最多
时间复杂度:O(nlogn)
不稳定的
归并:
先把一组数据拆分成单个的个体,然后按照从小到大的
顺序两两合并成新个体,合并结束后需要复制到临时空间
中,继续对临时空间的新个体两两合并,合并后的结果又
赋值回原空间中,此过程反复进行,最后的有序结果还需要
重新赋值给原空间
归并排序使用了额外的内存空间,因此避免了数据之间的交换的
耗时,但是是一种典型的以空间换时间的算法
时间复杂度:O(nlogn)
稳定的
堆:
把数据当做一颗完全二叉树,然后树中的数据调整成大根堆
然后把根节点的数据交换到数组的末尾,然后数量-1,接着
调整新的大根堆,依次重复以上操作,直到数量为1时结束,
可以递归实现也可以循环实现
时间复杂度:O(nlogn)
不稳定的
计数:
找出数据中的最大值和最小值,创建哈希表,用 数据-最小值 作为
下标标记哈希表中的对应位置,然后遍历整个哈希表,如果哈希表中
的值>1时,通过下标+最小值 还原数据放回数组中
是一种典型的以空间换时间的排序算法,该算法理论上的速度非常快
因为它是一种非基于交换的排序,在一定的范围内对非负整数排序时,
快于任意一种基于比较交换的排序,但是只适合非负整数,而且数据
之间的差值不宜过大,否则会非常浪费内存,反之,数据在范围内越均衡
、重复树2越多,性价比越高
时间复杂度:O(n+k) (k是待排序的整数的范围)
桶排序:
把数据根据值的特点,存储到不同的桶中,然后调用其他的排序函数
对桶中的数据进行排序,然后再按照桶的顺序拷贝到原数组中
目的是为了降低排序的的规模来提高排序的效率,也是一种用空间换时间
的排序算法
缺点:如何分桶、桶的定义的范围,这些都需要对待排序的数据有
一定的了解
时间复杂度: O(n+k)(k与桶的数量有关)
稳定的
基数排序:
是桶排序这种思想的具体实现,首先创建10个队列,然后逆序计算出
每个数据的个、十、百...位,然后根据计算出来的每位的值,放入对应的队列中,
然后再从队列中弹出数据回到数组中,这是某一位数据的值压入队列的结果,
数据的下一位继续重复以上操作,最大值的位数即循环次数
也是一种以空间换时间的排序
缺点:只适合排序正整数数据,要申请十个队列结构
时间复杂度:O(n+k)
稳定的