排序
1.插入排序
直接将值一个一个插入,然后往后调整。
2.希尔排序
1.进行预排序(使整个数组的顺序变得比较有序,大的在后面,小的在前面)
2.再进行插入排序
快排:
1. hoare版本
最快情况下时间复杂度是n * log n
最坏情况下时间复杂度是n ^ 2
如果接近有序或者是有序的情况下,可以加上三数取中的方法:
三数取中(即:找到比最大值小的,比最小值大的值)
begin mid end
加入三数取中后,快排瞬间从最坏的情况跳到最好的情况,这样下的快排几乎不会出现最坏情况,这样的情况下,快排的时间复杂度可以看做是O(N*logN)
或者可以使用随机key的方法,这里不加以演示,因为VS中自带的rand并不是真正的随机数
小区间优化,快排分割的小区间时,直接用插入排序即可。
2. 挖坑法
将第一坑位置的值先交给key,右边找小于key的值并将小于key的值交给坑位置,然后该位置形成一个新的坑。
.然后左边走,找到大于key的值并将左边的值交给坑,然后该位置形成一个新的坑。
然后重复上序操作。
3. 前后指针版本
先将第一个位置标记为key,并用prev指针指向该位置,cur指针指向该位置的后一个位置
用cur找小于key的值,找到了之后将prev的值与cur指向的指针进行交换,prev++
重复上序操作,如果cur出现越界,将key的值指向目前prev的位置,并继续进行上序操作,直至prev指针访问的最后一个值
4.非递归下的快排
需要用到栈 ,如果要先处理左,就将右先入栈(因为栈是先入后出)。
用队列也可以,就类似于层序遍历。
1.归并排序
(2)非递归
用rangeN标记每次归并的数据个数,从一开始,因为一个数据可以认为是有序的,可以直接归并,并不需要多加判断
当进行完第一次归并后,将rangeN * 2,在进行下一次归并排序,直到这组数据完全有序
可以归并一部分拷贝一部分,也可以归并完之后再进行拷贝
for(int i = 0; i <n -1; i += 2 * rangeN)
{
int begin1 = i, end1 = i + rangeN-1;
int begin2 = i + rangeN, end2 =i + rangeN * 2 - 1;
memcpy(a + i, tmp + i, sizeof(end2+i-1));
}
注意:创建的begin1不可能越界,但end1,begin2,end2都可能越界,因为这里创建的空间只有n的大小
可以向下面这样修正,添加到for循环中去
修正路线:
if(end1 >= n)
{
end1 = n - 1;
//不存在区间
begin2 = n;
end2 = n + 1;
}
else if(begin2 >= n)
{
}
直接跳过路线:
if(end1 >= n){ //修正区间 -> 归并完之后拷贝数据 或者 归并每组先拷贝再继续
break;
}
if(begin2 >= n){
break;
}
if(end2 >= n)
{
end2 = n - 1;
}
快速排序的小问题
对于快排来说,如果有大量重复的数据,就会出现性能下降的问题。
可以采用三路划分来进行操作,就可以不出现这种问题。
画图
![](https://i-blog.csdnimg.cn/blog_migrate/1a688a52612a7747186ca67b8307411f.png)
cur -- c
left -- l
right -- r
a[c] < key, 交换l和c的位置,l++,c++
a{c] == key ,c++
a[c] > key, 交换c和r的位置,r++
c > r 就结束了
int key = begin + rand % (end-begin)
![](https://i-blog.csdnimg.cn/blog_migrate/991496cd6599a147d6bfdd45a77b9469.png)
怎么判断稳定性:
相同的数,需要保证他们的相对顺序不变,那么就是稳定的。
下面是排序的稳定性的表格:
![](https://i-blog.csdnimg.cn/blog_migrate/35972cf27924043990686af130b716a7.png)