简介
相信大家接刚触算法,最开始学习的就是排序了。(为了方便,这里全部从小到大排序)
觉得之前自己学的数据结构和算法不够系统,只是单纯刷了点题目。这里以刚刚学过的C++重新开 始,三天一更(当然后期也得看自己的学习能力)
这里介绍下八大排序中比较进阶的内容,希尔,归并,快速。
希尔排序和插入排序差不多,而且他不够稳定,所以一般不建议使用。
这里重点分析归并排序和快速排序。他们的时间复杂度都是 nlog2n
在力扣刷题遇到很大很恶心的数据量的时候有奇效
希尔排序
希尔排序,也称 递减增量 排序算法,是插入排序的一种更高效的改进版本。但希尔排序是 非稳定排序算法 。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
(1)插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率;
(2)但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位;
基本思想:
先分组,对每一组进行插排,最后对总体进行插排。
时间复杂度: O(n1.3)
代码
#include <iostream>
using namespace std;
int main(){
int a[] = {1,5,6,3,4,5,2,44,56,7,5,44,5,1,12,5,4,12,45};
int t = sizeof(a)/sizeof(int);//求出数组长度
for(int i = t / 2; i > 0; i /= 2){ //i是增量
for(int j = 0; j < t-i; j++){
for(int k = j; k < t- i; k += i){
if(a[k] > a[k + i]){
int temp = a[k];
a[k] = a[k + i];
a[k+i] = temp;
}
}
}
}
for(int i = 0; i < t; i++){
cout<<a[i]<<" ";
}
}
归并排序
归并排序的思想就是分组治理,然后合并。
时间复杂度
O(nlog2n):
因为用了二分的思想,看图体会
#include <iostream>
using namespace std;
//合并函数
void merge(int arr[], int l, int r, int mid){
int n = r - l + 1;
int * tmp = new int[n];
int i = 0;
int left = l;
int right = mid+1;
//合并过程
while(left <= mid && right <= r){//如果左右都有数,那么谁小就谁先进tmp
tmp[i++] = (arr[left]<= arr[right]) ? arr[left++]: arr[right++];
}
while(left > mid && right <= r){//左边数用完了
tmp[i++] = arr[right++];
}
while(left <= mid && right > r){//右边数用完了
tmp[i++] = arr[left++];
}
for(int i = 0; i < n; i++){
arr[l + i] = tmp[i]; //把排好的数导入arr[l] - arr[r]中
}
delete [] tmp; //删除堆内存
}
void Mergersort(int arr[], int l, int r){
if(l == r) return;
int mid = (r + l)>>1;
Mergersort(arr,l,mid);
Mergersort(arr,mid+1,r);
merge(arr,l,r,mid);
}
int main(){
int a[] = {1,5,6,3,4,5,2,44,56,7,5,44,5,1,12,5,4,12,45};
int t = sizeof(a)/sizeof(int);//求出数组长度
Mergersort(a,0,t-1);
for(int i = 0; i < t; i++){
cout<<a[i]<<" ";
}
}
快速排序
快速排序,顾名思义,肯定要比一般的排序速度快。先说他的时间复杂度O(nlog)
下面模拟对下列数组进行排序
5 | 3 | 9 | 2 | 8 | 12 | 7 | 1 |
5 | 3 | 9 | 2 | 8 | 12 | 9 | 1 |
left | right |
5 | 3 | 9 | 2 | 8 | 12 | 7 | 1 |
left | right |
(3)left 和right就换个位置
5 | 3 | 1 | 2 | 8 | 12 | 7 | 9 |
left | right |
(4)跑到下一个位置,最后相撞,这里记录相撞以前最后一刻。然后第一轮结束
5 | 3 | 1 | 2 | 8 | 12 | 7 | 9 |
left | right |
(5)然后 l e f t left left 和哨兵换一个位置
2 | 3 | 1 | 5 | 8 | 12 | 7 | 9 |
left right |
到此你会发现,原先哨兵左边的数字会比哨兵小,右边的数字都比哨兵大。
哇靠,真的太神奇了!!!(bushi)
我们现在只需要从原来哨兵的位置拆成两个数组,在重复之前的操作,就可以完成排序了
左边排序
2 | 3 | 1 | |||||
2 | 1 | 3 | |||||
1 | 2 | 3 | |||||
右边排序
8 | 12 | 7 | 9 |
8 | 9 | 7 | 12 |
7 | 9 | 8 | 12 |
最后合并两个数组就OK了
这里和归并排序差不多,也是用了二分的思想,只不过是二分方式不太一样
代码演示
#include <iostream>
using namespace std;
void Quick_sort(int *arr, int begin, int end){
if(begin > end){
return;
}
int temp = arr[begin];
int left = begin;
int right = end;
while(right > left){
//右边先往左跑
while(arr[right] >= temp && right > left){
right--;
}
//左边后往右跑
while(arr[left] <= temp && right > left){
left++;
}
if(right > left){
int t= arr[left];
arr[left] = arr[right];
arr[right] = t;
}
}
arr[begin] = arr[left];
arr[left] = temp;
Quick_sort(arr, begin, left-1);
Quick_sort(arr, left+1, end);
}
int main(){
int a[] = {1,5,6,3,4,5,2,44,56,7,5,44,5,1,12,5,4,12,45};
int t = sizeof(a)/sizeof(int);//求出数组长度
Quick_sort(a,0,t-1);
for(int i = 0; i < t; i++){
cout<<a[i]<<" ";
}
}
下期预告
今天学完了,时间复杂度相对比较低的排序方法,那么还有没有更牛更快的呢
敬请期待 八大排序(三)
堆排序 计数排序.