zoukankan      html  css  js  c++  java
  • 排序算法综合整理

    排序算法综合整理

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

    一、直接插入排序

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

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

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

    撸代码:

     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}
  • 相关阅读:
    JQuery的js写法
    Reapter 中客户端控件和服务器端控件的选择
    ASP.NET 中随时向页面输入数据
    SQL补充查询
    repeater项模版bottom事件获取该bottom所在行id为lblName的label控件的text
    微软Visual Studio 2010架构设计功能应用
    动态表格
    如何选中jsTree中已checked的Item的信息
    据说看完这21个故事的人,30岁前都成了亿万富翁。你是下一个吗?
    WebSocket编解码器
  • 原文地址:https://www.cnblogs.com/HappyKnockOnCode/p/12902041.html
Copyright © 2011-2022 走看看