选择排序 O ( N 2 ) O(N^2) O(N2)
选择最小值与0位置元素交换、次小值与1位置元素交换,次次小值与2位置元素交换。。。。。
时间复杂度
O
(
N
2
)
O(N^2)
O(N2) , 空间复杂度O(1) 不稳定
冒泡排序 O ( N 2 ) O(N^2) O(N2)
两个两个比较。
增续:第i个和第(i+1)个比较,若a[i] > a[i+1],交换顺序,i++
时间复杂度
O
(
N
2
)
O(N^2)
O(N2) , 空间复杂度O(1) ,稳定
异或运算:相同为0,不同为1,可视作无进位相加(二进制相加 );满足交换律、结合律
a ^ b ^ b = a (b ^ b = 0, 0 ^ a = a)
交换方法
swap(a, b){//a、b地址不一样
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
数组长度为n,有两种数a, b为奇数个,其他数偶数个,怎么得到这两个数?
1.将所有数异或,得到res = a ^ b;
2.res的第 i 位一定为1,说明a或b在第 i 位一定为1,另一个为0, 将原数组中第i位为1的数全部异或,就可以得到a或者b
(a ^ (~a + 1))= a 的最右侧的1
插入排序 O ( N 2 ) O(N^2) O(N2)
1.从第一个元素开始,该元素可以认为已经被排序
2.取下一个元素tem,从已排序的元素序列从后往前扫描
3.如果该元素大于tem,则将该元素移到下一位
4.重复步骤3,直到找到已排序元素中小于等于tem的元素
5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置
6.重复步骤2~5
—————————————-———
原文链接: https://blog.csdn.net/weixin_50886514/article/details/119045154
时间复杂度:
O
(
N
2
)
O(N^2)
O(N2) (按照算法可能遇到的最差情况估计)
空间复杂度O(1) ,稳定
二分查找
在一个有序数组中找到数num:从中间开始找
时间复杂度O(log N)
在一个无序数组中找到局部最值也可以用二分
对数测试器:判断算法写得对不对
子问题等规模时可以用master公式计算时间复杂度
a:子问题调用次数
T(N/b):子问题的规模
O(N^d):除子问题外,其他代码块的复杂度
希尔排序O(N*logN)
数组长度为n,
(1)初始选择gap=n/2为间隔,间隔为gap的数为一组,将每组的数进行组内插入排序
(2)gap = gap / 2,继续分组,每组数进行插入排序
(3)重复(2)直到gap = 1,相当于进行普通的插入排序
计算复杂度:O(N*logN),不稳定
归并排序O(N*logN)
先将前半部分排好序,再将后半部分排好序,最后将两部分合并
时间复杂度O(N*logN) ,空间复杂度O(N) ,稳定
快速排序O(N*logN)
以数组最后一个数a[N]为基准,将小于a[N]的数放左边,大于a[N]的数放右边,然后左右两边各自递归重复这一过程
i = 0; L是小于区的指针,小于区为[0,L];R是大于区的指针,大于区为[R,N-1]。(数组范围0~N-1)
a[i]小于a[N]时,将a[i]与a[L+1]交换,L++;a[i]=a[N]时,i++;a[i]>a[N]时,a[i]与a[R-1]交换,R–,i不变;只要对左右两侧两个区递归,中间等于区不用递归
O( N^2 )
- 更快的快排:
不用数组最后一个作为基准,用随机选的数作为基准
时间复杂度O(N*logN),空间复杂度:O(logN), 不稳定
堆排序O(N*logN)
- 数组从0开始连续L个数够成二叉树,第i个元素的左子树是第(2i+1),右子树是(2i+2),父节点是((i-1)/2)(二叉树层序遍历)
- 大根堆:根节点是最大的数
小根堆:根节点是最小的数
题目:一个序列,随时可以找到中位数。
算法思想:准备一个大根堆,一个小根堆,第一个数入大根堆,接下来每个数和大根堆的根比较,若小于等于大根堆的根,则入大根堆,否则入小根堆;当大、小根堆的长度差为2时,较长的堆的根移到较小的堆。若序列长度为偶数,则大、小根堆的两根平均为中位数,若奇数,较长的堆的根为中位数。
- heap insert过程:
- 每次给一个数,放入堆中,形成大根堆。将每次得到的新数a[i]和它的父节点(a[(i-1)/2])比较,若大于则交换,交换后继续和父节点比较,直到新插入的值小于等于其父节点。
- 每次给一个数,放入堆中,形成大根堆。将每次得到的新数a[i]和它的父节点(a[(i-1)/2])比较,若大于则交换,交换后继续和父节点比较,直到新插入的值小于等于其父节点。
- heapify:若要求去出堆中最大值,剩余元素仍保持大根堆结构:最大值在根节点,为a[0],将a[0]取出,并将最后一个元素放到[0]位置,并用左右孩子中的最大值和其比较,若新的a[0]比字节的小,则交换位置
O(logN)
- 堆排序
将大根堆的根节点与数组最后一个位置的元素交换,并将heapsize–,即将原来的根节点排出堆,排出堆的就是已经排好序的,然后将新的堆重新排成大根堆,重复前面的过程,最终将数组递增排序。
时间复杂度O(NlogN),空间复杂度O(1),不稳定
- Java小根堆的类:
PriorityQueue heap = new PrioiityQueue<>();//默认小根堆的结构
扩容的代价O(logN),不支持调整已经形成的堆,若有调整堆的需求,最好自己手写一个堆
无比较排序
适用于数据范围较小的情况
- 计数排序
哈希表,key为数据范围内所有值,value为该数据出现的次数,按照key的顺序和value次数打印数据 - 基数排序
一组不超过三位、十进制的数组(低于三位的高位补零),升序排列:- 将所有数按照个位数依次放入对应的桶中(个位为i就放入i号桶),按桶号升序、FILO的顺序取出,再次按照十位数将重新排列的数放入桶中、取出,最后按照百位数将重新排列的数放入桶中、取出,数组已按升序排列。
- 另一种入桶出桶:准备一个哈希表hm,key值为0~9,value计数。若第 i 个数的个位数为 x ,则哈希表中 x 对应的值加一,遍历一遍数组后,将哈希表的value值累加为个位数小于等于 x 的数有多少个,即hm.get(x) = hm.get(x) + hm.get(x - 1) + … +hm.get(0). 准备一个和原数组长度一样的空数组,从右到左遍历原数组,个位为 x 的数应放到空数组中索引为(hm.get(x) - 1)的位置,放入后,hm.get(x)–. 重复以上过程直到数组中最大值的最高位。
- 排序的稳定性
数组中含有多个值相同的元素,排序后,原来相同值的元素的先后次序是否也交换了
稳定的排序算法:冒泡排序、插入排序、归并排序(相等时先拷贝左边的)、计数排序、基数排序
不稳定的排序算法:选择排序、快速排序、堆排序 - 最常用的是快速排序;当需要稳定但对空间复杂度不设限时用归并排序;当对空间限制但不要求稳定时用堆排序(目前没有时间复杂度小于O(NlogN)以下的、基于比较的排序算法;时间复杂度为O(NlogN)的情况下,目前没有在空间复杂度O(N)以下实现稳定的排序算法)
- 工程上对排序的改进:
- 充分利用O(N*longN) 和 O(N^2)排序各自的优势
例如:综合排序,大范围时快速排序,大致排好序后,大样本中长度小的子样本用插入排序 - 稳定性的考虑
- 充分利用O(N*longN) 和 O(N^2)排序各自的优势