排序算法综合整理

排序算法综合整理

一、直接插入排序二、希尔排序三、归并排序怎样求逆序数四、快速排序五、堆排序六、计数排序七、桶排序八、基数排序

一、直接插入排序

  • 从小到大排序:从第二个数开始,若非递增,则插入前面有序序列中,该位置前面一位小于它,后面一位大于它;

  • 二分插入排序:因为前面序列有序,则可用二分找到该位置;

用了二分可以减少比较大小的次数

撸代码:

 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]={519374862};
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. 大根堆,小根堆:所有非叶子节点大于或者小于其孩子节点。

  2. 用大根堆进行从小到大的排序

  3. 建立大根堆:从下往上,从右往左遍历非叶子节点,判断其是否符合大根堆性质,若不符合,则交换节点位置,直至建出大根堆。

  4. 大根堆根节点一定是被排序的这段数值的最大值,交换堆尾堆首数值,堆尾指针前移(有没有冒泡的感觉?最大值逐渐飘到堆尾)

  5. 当前堆只有根节点不符合大根堆性质,所以从根节点开始,向下找到合适的位置即可

 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}
原文地址:https://www.cnblogs.com/HappyKnockOnCode/p/12902041.html