排序算法综合整理
一、直接插入排序二、希尔排序三、归并排序怎样求逆序数四、快速排序五、堆排序六、计数排序七、桶排序八、基数排序
一、直接插入排序
从小到大排序:从第二个数开始,若非递增,则插入前面有序序列中,该位置前面一位小于它,后面一位大于它;
二分插入排序:因为前面序列有序,则可用二分找到该位置;
用了二分可以减少比较大小的次数
撸代码:
1#include<stdio.h>
2int main()
3{
4 int a[10]={56,32,12,43,78,66,23,44,21,58};
5 /**从小到大 直接插入排序*/
6 for(int i=1;i<10;i++)
7 {
8 if(a[i]<a[i-1])/**若存在逆序,则向前找小于a[i]*/
9 {
10 int temp=a[i],j=i-1;
11 while(j>=0&&temp<a[j])/**在找的过程中,大于a[i]的向后推1位*/
12 {
13 a[j+1]=a[j];
14 j--;
15 }
16 a[j+1]=temp;
17 }
18 }
19 printf("直接插入排序后:
");
20 for(int i=0;i<10;i++)
21 printf("%d ",a[i]);
22 return 0;
23}
二、希尔排序
直接插入排序每次跨度为1,而希尔排序不断改变跨度(增量,在增量下“大致有序”),多次直接插入排序。
撸代码:
1#include<stdio.h>
2void shellPass(int a[],int d)
3{
4 for(int i=d;i<10;i++)
5 {
6 if(a[i]<a[i-d])
7 {
8 int temp=a[i],j=i-d;
9 while(j>=0&&temp<a[j])
10 {
11 a[j+d]=a[j];
12 j-=d;/**跨度不同*/
13 }
14 a[j+d]=temp;
15 }
16 }
17
18}
19int main()
20{
21 int a[10]={56,32,12,43,78,66,23,44,21,58};
22 int len=10;
23 while(len)
24 {
25 len=len/2;
26 shellPass(a,len);/**多次改变增量*/
27 }
28 printf("希尔排序后:
");
29 for(int i=0;i<10;i++)
30 {
31 printf("%d ",a[i]);
32 }
33 return 0;
34}
三、归并排序
给一个序列 9 1 0 5 4,进行归并排序:(从小到大)
下标 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
数值 | 9 | 1 | 0 | 5 | 4 |
单个元素为一组,两两合并为有序序列:
下标 | 0 1 | 2 3 | 4 |
---|---|---|---|
数值 | 1 9 | 0 5 | 4 |
2个元素为一组,每两组合并:
下标 | 0 1 2 3 | 4 |
---|---|---|
数值 | 0 1 5 9 | 4 |
最后两组合并:
下标 | 0 1 2 3 4 |
---|---|
数组 | 0 1 4 5 9 |
在合并的过程中,就相当于两个有序序列合成一个有序序列
那么怎么分成两组呢?
递归派上了用场!从1~n开始分,知道分不了,利用回溯进行排序,完全ok
怎样求逆序数
在两个有序序列合并的时候,一个是left序列,一个是right序列,当right序列某个元素跑到left前面了,计算一下,left的长度减去跑的位置,就是当前这个数的逆序啦
撸代码:
1#include<stdio.h>
2#define N 500050
3long long ans;
4int a[N],n,t[N];
5void Sort(int l,int mid,int r)
6{
7 int i=l,j=mid+1;/**将分开的左右两个有序链表合并*/
8 int k=l;
9 while(i<=mid&&j<=r)
10 {
11 if(a[i]<=a[j])
12 {
13 t[k++]=a[i++];
14 }
15 else
16 {
17 t[k++]=a[j++];
18 ans+=mid-i+1;/**统计 a[j]的 逆序数*/
19 }
20 }
21 while(i<=mid)
22 {
23 t[k++]=a[i++];
24 }
25 while(j<=r)
26 {
27 t[k++]=a[j++];
28 }
29 for(int i=l;i<=r;i++)
30 {
31 a[i]=t[i];
32 }
33 return ;
34}
35void departSort(int l,int r)
36{
37 if(l<r)
38 {
39 int mid=(l+r)>>1;
40 departSort(l,mid);
41 departSort(mid+1,r);/**分完之后,排序*/
42
43 Sort(l,mid,r);
44 }
45 return ;
46}
47
48int main()
49{
50 while(~scanf("%d",&n)&&n)
51 {
52 ans=0;
53 for(int i=1;i<=n;i++)
54 {
55 scanf("%d",&a[i]);
56 }
57 departSort(1,n);/**归并排序*/
58 printf("%lld
",ans);
59 }
60 return 0;
61}
四、快速排序
从小到大
思想:先2分块,确定基准元素,保证块之间有序,也就是说 左块任意数字小于右块任意数字,块内无序,再对每一块分2块,层层递归;
优化:减少基准元素值的交换次数,先找到两个位置 ,左边大于基准元素,右边小于基准元素,两个值交换,直至遍历所有数字后,才基准元素交换;
关于递归优化不是太明白,优化前是quickSort函数两次递归,优化后一次递归,应该是在这里减少了堆栈深度。
撸代码:
1#include<stdio.h>
2#include<iostream>
3using namespace std;
4int Partition(int a[],int low,int hight)
5{
6 int pivotKey=a[low];/**基准元素值(枢轴)*/
7 while(low<hight)
8 {
9 while(low<hight&&a[hight]>=pivotKey)/**从右开始,若小于基准元素,则交换位置*/
10 hight--;
11 swap(a[low],a[hight]);/**小于基准元素的值跑到左边*/
12 while(low<hight&&a[low]<=pivotKey)/**从左开始*/
13 low++;
14 swap(a[low],a[hight]);
15 }
16 return low;
17}
18
19/*******优化 不必要的交换(还可优化基准元素的选取,防止选到极大极小值)*********/
20/*数组中分三次取样,每次取三个数,三个样品中各取出中间数,然后在这三个中枢当中再取一个中间数作为枢轴*/
21int Partition1(int a[],int low,int hight)
22{
23 int startIndex=low;/**基准元素只交换一次*/
24 int pivotKey=a[low];
25 while(low<hight)
26 {
27 while(low<hight&&a[hight]>=pivotKey)
28 hight--;
29 while(low<hight&&a[low]<=pivotKey)
30 low++;
31 swap(a[low],a[hight]);/**找到左边大的,右边小的 ,交换一次*/
32 }
33 /**循环执行完,左小右大,low就是基准元素该去的位置*/
34 swap(a[low],a[startIndex]);
35 return low;
36}
37
38
39void quickSort(int a[],int low,int hight)
40{
41 int pivot;
42 if(low<hight)
43 {
44 pivot=Partition(a,low,hight);/**基准元素下标*/
45 quickSort(a,low,pivot-1);/**分块排序,保证块之间有序*/
46 quickSort(a,pivot+1,hight);
47 }
48
49 /*********递归优化:减少堆栈深度**********/
50 /*
51 while(low<hight)
52 {
53 pivot=Partition(a,low,hight);
54 quickSort(a,low,pivot-1);
55 low=pivot+1;
56 }
57 */
58
59}
60int main()
61{
62 int a[10]={5, 1, 9, 3, 7, 4, 8, 6, 2};
63 int n=9;
64 quickSort(a,0,n-1);/**从小到大排序*/
65 for(int i=0;i<n;i++)
66 printf("%d ",a[i]);
67 return 0;
68}
五、堆排序
大根堆,小根堆:所有非叶子节点大于或者小于其孩子节点。
用大根堆进行从小到大的排序
建立大根堆:从下往上,从右往左遍历非叶子节点,判断其是否符合大根堆性质,若不符合,则交换节点位置,直至建出大根堆。
大根堆根节点一定是被排序的这段数值的最大值,交换堆尾堆首数值,堆尾指针前移(有没有冒泡的感觉?最大值逐渐飘到堆尾)
当前堆只有根节点不符合大根堆性质,所以从根节点开始,向下找到合适的位置即可
1#include<stdio.h>
2#include<iostream>
3using namespace std;
4
5/**刚还奇怪,为什么从小到大排序用的是大根堆?
6仔细看,建好堆以后,根节点是最大值,可是!!!
7交换了堆尾和堆首的值,那么最大值就跑到了后面.
8交换后,这个堆尾就算最大值了 ,不用管,再排序前面 n-1 个节点
9同理,每次最大值都跑到了后面
10*/
11
12
13/**以 k 为根的完全二叉树,分别以2*K 和 2*k+1 为根的左右子树为大根堆*/
14/**使 k 为根的树满足堆的性质*/
15void sift(int a[],int k,int m)
16{
17 int x=a[k];
18 int i=k;
19 int j=k*2;
20 bool finish=false;
21 while(j<=m&&!finish)
22 {
23 if(j+1<=m&&a[j]<a[j+1])
24 j=j+1;/**此时 j 指向最大子树根节点*/
25
26 if(x>=a[j])
27 finish=true;
28 else/**若是比子树根小,交换后还得考虑下层大小关系*/
29 {
30 a[i]=a[j];
31 i=j;
32 j=2*i;
33 }
34 }
35 a[i]=x;
36}
37void crateHeap(int a[],int n)
38{
39 /**从最后一个非叶子节点往上筛选建堆*/
40 for(int i=n/2;i>=1;i--)
41 sift(a,i,n);
42}
43void heapSort(int a[],int n)
44{
45 /**建大根堆后 ,保证了下面的数字不会大于上面的数字*/
46 crateHeap(a,n);
47 /**对 a 数组进行 从小到大 堆排序*/
48 for(int i=n;i>=2;i--)
49 {
50 /**每次得到的最大值跑到了后面*/
51 swap(a[i],a[1]);
52
53
54 /**交换以后只有堆顶元素是不合法的,所以下面只是为了
55 给堆顶元素找到一个合适的位置*/
56 /**对a[1~i-1]调整成堆*/
57 sift(a,1,i-1);
58 }
59}
60int main()
61{
62 int a[12]={0,88,32,34,12,34,66,52,33,25,20};
63 heapSort(a,10);
64 for(int i=1;i<=10;i++)
65 printf("%d ",a[i]);
66 return 0;
67}
六、计数排序
排序思想: 对于数组 a[ ] 排序 ,先用数组c[ a[ i ] ] 记录其中的值出现的次数,然后计算前缀和;得出的值的意义就是 对于c[ a[i] ] 的值就是 对于所有的 a[ i ] 最后一个 a[ i ] 在数组中有序的排名,所以借助 ans[ ] 数组记录下标c[a[i] ] 的值为 a[i] ,- - c[ a [ i ] ] 就是 对于所有a[i] ,a[ i ] 的倒数第二个位置。
1/**计数排序 1~10以内的数*/
2#include<stdio.h>
3#include<time.h>
4#include<stdlib.h>
5void printArr(int a[],int n)
6{
7 for(int i=0;i<n;i++)
8 printf("%d%c",a[i],i==n-1?'
':' ');
9}
10void countSort(int a[],int n)
11{
12 int c[12];
13 for(int i=0;i<=11;i++)
14 c[i]=0;/**清空*/
15
16 for(int i=0;i<n;i++)
17 c[a[i]]++;/**计数*/
18
19 for(int i=1;i<=n;i++)
20 c[i]+=c[i-1];/**前缀和*/
21
22 /**
23 有了前缀和后,数字最后一个a[j]的排名为c[a[j]]
24 所以访问每个数字时,只是把a[j]放到对应的名次上
25 因为可能有多个a[j],所以名次可以从最后一个向前分配
26 */
27 int ans[12];
28 for(int j=n-1;j>=0;j--)
29 {
30 ans[--c[a[j]]]=a[j];
31 }
32 printf("排序后:
");
33 printArr(ans,10);
34}
35int main()
36{
37 int a[12];
38 int n=10;
39 /*srand函数设定rand函数所用随机数演算法的种子值*/
40 //srand(time(NULL));
41 /*time(t) t==NULL 返回当前时间,若非空:返回当前时间的同时,将返回值赋予t 指向的内存空间*/
42 for(int i=0;i<n;i++)
43 a[i]=rand()%7+1;
44 printArr(a,10);
45 countSort(a,10);
46 return 0;
47}
七、桶排序
排序思想: 首先通过最大最小值数据范围 maxx-minn 按照每个桶平均装的数量 得出桶的数量。然后遍历数组 a[ ] ,装入桶中,进行桶内排序。
1#include<vector>
2#include<stdio.h>
3#include<time.h>
4#include<stdlib.h>
5#include<algorithm>
6using namespace std;
7void bucketSort(int a[],int n)
8{
9 int maxx=-9999,minn=9999;
10 for(int i=0; i<n; i++)
11 {
12 if(maxx<a[i])
13 maxx=a[i];
14 if(minn>a[i])
15 minn=a[i];
16 }
17 /**根据数据范围得出桶的数量(最多10个元素1个桶)*/
18 int bucketNum=maxx/10-minn/10+1;
19
20 vector<int>v[bucketNum];
21 for(int i=0; i<n; i++)
22 {
23 int t=(a[i]-minn)/10;
24 v[t].push_back(a[i]);
25 }
26
27 printf("Sort Over:
");
28 for(int i=0; i<bucketNum; i++)
29 {
30 /**
31 桶内排序,可以插入排序,也可以用排序函数
32 */
33 if(v[i].size())
34 {
35 sort(v[i].begin(),v[i].end());
36 for(int j=0; j<v[i].size(); j++)
37 printf("%d ",v[i][j]);
38 }
39 }
40}
41int main()
42{
43 int n=10;
44 int a[12];
45 for(int i=0; i<n; i++)
46 a[i]=rand()%7+1;
47 for(int i=0;i<n;i++)
48 printf("%d%c",a[i],i==n-1?'
':' ');
49 bucketSort(a,n);
50 return 0;
51}
八、基数排序
LSD排序思想: 初始化 exp=1 ,先按照低 exp 位进行计数排序,然后按照低 exp+1 位计数排序,直至exp/max=0 ,排序完成。
1#include<stdio.h>
2#include<algorithm>
3using namespace std;
4/**LSD排序(从右向左定位)*/
5void countSort(int a[],int exp,int n)
6{
7 int c[10];
8 for(int i=0;i<10;i++)
9 c[i]=0;
10
11 /**按照位权计数*/
12 for(int i=0;i<n;i++)
13 c[(a[i]/exp)%10]++;
14
15 for(int i=1;i<10;i++)
16 c[i]+=c[i-1];
17 int b[20];
18 /**暂时按照低exp位排序*/
19 for(int i=n-1;i>=0;i--)
20 b[--c[(a[i]/exp)%10]]=a[i];
21
22 for(int i=0;i<n;i++)
23 a[i]=b[i];
24}
25void bitSort(int a[],int n)
26{
27 int maxx=a[0];
28 for(int i=1;i<n;i++)
29 maxx=max(maxx,a[i]);
30
31 for(int exp=1;maxx/exp>0;exp*=10)/**从右向左定位*/
32 {
33 /**计数排序*/
34 countSort(a,exp,n);
35 }
36}
37int main()
38{
39 int a[12]={103,9,1,7,15,25,109,209,5};
40 int n=9;
41 bitSort(a,n);
42 printf("基数排序后:
");
43 for(int i=0;i<n;i++)
44 printf("%d ",a[i]);
45
46 return 0;
47}