zoukankan      html  css  js  c++  java
  • 排序

    (1)基本思想:

    每一趟从待排序的数据元素中选出最小(或最大)的一个元素

    顺序放在待排序的数列的最前

    直到全部待排序的数据元素排完。

    (2)排序过程:

    【示例】:

    初 始 关键字 [49 38 65 97 76 13 27 49]
    第一趟排序后 13[38 65 97 76 49 27 49]
    第二趟排序后 13 27[65 97 76 49 38 49]
    第三趟排序后 13 27 38 [97 76 49 65 49]
    第四趟排序后 13 27 38 49 [76 97 65 49]
    第五趟排序后 13 27 38 49 49 [97 65 76]
    第六趟排序后 13 27 38 49 49 65 [97 76]
    第七趟排序后 13 27 38 49 49 65 76 [97]
    最后排序结果 13 27 38 49 49 65 76  97

    //选择排序
    #include<cstdio>
    using namespace std;
    const int maxn = 100005;
    int a[maxn],n,cnt;
    int main()
    {
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
            scanf("%d",&a[i]);
        for(int i = 1;i <= n;i++)
        {
            cnt = i;
            for(int j = i + 1;j <= n;j++)
                if(a[j] < a[cnt])
                    cnt = j;
            if(cnt != i)
            {
                int tmp = a[i];
                a[i] = a[cnt];
                a[cnt] = tmp;
            }
        }
        for(int i = 1;i <= n;i++)
            printf("%d ",a[i]);
        return 0;
    }

    二、冒泡排序

    (1)基本思想:

    以n个人站队为例

    从第1个开始,依次比较相邻的两个是否逆序对(高在前,矮在后)

    若逆序就交换这两人,即第1个和第2个比,若逆序就交换两人,接着第2个和第3个比,若逆序就交换两人,接着第3个和第4个比,若逆序就交换两人,……

    直到n-1和n比较,经过一轮比较后,则把最高的人排到最后,即将最高的人像冒泡一样逐步冒到相应的位置

    原n个人的排序问题,转换为n-1个人的排序问题。

    第二轮从第1个开始,依次比较相邻的两个人是否逆序对,若逆序就交换两人,直到n-2和n-1比较。

    如此,进行n-1轮后,队列为有序的队列。

    (2)排序过程

    例如有6个元素需要排序:

              6 5 3 4 1 2

    1.第一趟排序

    2.第二趟排序

    3.第三趟排序

    4.第四趟排序

    5.第五趟排序

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn = 100005;
    int n,a[maxn];
    int main()
    {
        scanf("%d",&n);
        for(int i = 0;i < n;i++)
            scanf("%d",&a[i]);
        for(int i = n-1;i >= 1;i--)
        {
            for(int j = 0;j < i;j++)
            {
                if(a[j] > a[j + 1])
                    swap(a[j],a[j + 1]);
            }
        }
        for(int i = 0;i < n;i++)
            printf("%d ",a[i]);
        return 0;
    } 

    冒泡的优化

    对于有些数据,我们发现,不一定要n-1次才能排完。

    例如1 5 2 3 4 6,我们发现只需一趟排序就可以将整个序列排完

    于是,我们可以设置一个布尔变量,判断是否有进行交换,如果没有交换,说明已经排序完成,进而减少几趟排序。

    #include<cstdio>
    #include<algorithm> 
    using namespace std;
    const int maxn = 100005;
    int n,a[maxn];
    bool opt;
    int main()
    {
        scanf("%d",&n);
        for(int i = 0;i < n;i++)
            scanf("%d",&a[i]);
        for(int i = n - 1;i >= 1;i--)
        {
            opt = true;
            for(int j = 0;j < i;j++)
            {
                if(a[j] >= a[j + 1])
                {
                    swap(a[j],a[j + 1]);
                    opt = false;
                }
            }
            if(opt == true)
                break;
        }
        for(int i = 0;i < n;i++)
            printf("%d ",a[i]);
        return 0;
    }

    三、插入排序

    (1)基本思想:

    回忆一下打牌时抓牌的情景,为了方便打牌,抓牌时一般一边抓牌一边按花色和大小插入恰当的位置,当抓完所有的牌时,手中的牌便是有序的,这排序方法即插入排序。

    当读入一个元素时,在已经排序好的序列中,搜寻它正确的位置,再放入读入的元素。

    但不该忽略一个重要的问题:在插入这个元素前,应当先将将它后面的所有元素后移一位,以保证插入位置的原元素不被覆盖。

    (2)排序过程:

    例如:设n=8,数组a中8个元素是: 36,25,48,12,65,43,20,58,执行插入排序程序后,其数据变动情况:

    第0步:[36] 25 48 12 65 43 20 58
    第1步:[25 36] 48 12 65 43 20 58
    第2步:[25 36 48] 12 65 43 20 58
    第3步:[12 25 36 48] 65 43 20 58
    第4步:[12 25 36 48 65] 43 20 58
    第5步:[12 25 36 43 48 65] 20 58
    第6步:[12 20 25 36 43 48 65] 58
    第7步:[12 20 25 36 43 48 58 65]

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int maxn = 10005;
    int n,a[maxn];
    int main()
    {
        int i,j,k;
        scanf("%d",&n);
        for(i = 0; i < n; i++)
            scanf("%d",&a[i]);
        for(i = 0; i < n; i++)
        {
            for(j = i - 1; j >= 0; j--)
                if(a[j] < a[i])
                    break;
            if(j != i - 1)
            {
                int tmp = a[i];
                for(k = i - 1;k > j;k--)
                    a[k + 1] = a[k];
                a[k + 1] = tmp;
            }
        }
        for(int i = 0;i < n;i++)
            printf("%d ",a[i]);
        return 0;
    }

    四、桶排序

    (1)基本思想:

    桶排序的思想是若待排序的值在一个明显有限范围内(整型)时,可设计有限个有序桶,待排序的值装入对应的桶(当然也可以装入若干个值),桶号就是待排序的值,顺序输出各桶的值,将得到有序的序列。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int b[101],n,k;
    int main()
    {
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)
        {
            scanf("%d",&k);
            b[k]++;
        }
        for(int i = 0;i <= 100;i++)
            while(b[i])
            {
                printf("%d ",i);
                b[i]--;
            }
        return 0;
    }

    五、快速排序

    (1)基本思想:

    通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
    (2)排序过程:

    假设待排序的序列为{a[L],a[L+1],a[L+2],……,a[R]},首先任意选取一个记录(通常可选中间一个记作为枢轴或支点),然后重新排列其余记录,将所有关键字小于它的记录都放在左子序列中,所有关键字大于它的记录都放在右子序列中。由此可以将该“支点”记录所在的位置mid作分界线,将序列分割成两个子序列和。这个过程称作一趟快速排序(或一次划分)。

    一趟快速排序的具体做法是:附设两个指针i和j,它们的初值分别为L和R,设枢轴记录取mid,则首先从j所指位置起向前搜索找到第一个关键字小于的mid的记录,然后从i所指位置起向后搜索,找到第一个关键字大于mid的记录,将它们互相交换,重复这两步直至i>j为止。

     快速排序的时间的复杂性是O(nlog2n),速度快,但它是不稳定的排序方法。就平均时间而言,快速排序是目前被认为是最好的一种内部排序方法

    由以上讨论可知,从时间上看,快速排序的平均性能优于前面讨论过的各种排序方法,但快速排序需一个栈空间来实现递归。若每一趟排序都将记录序列均匀地分割成长度相接近的两个子序列,则栈的最大深度为log(n+1)。

    void qsort(int l,int r)
    {
        int i,j,mid,p;
        i = l;
        j = r;
        mmid = a[(l + r)/2];
        do
        {
            while(a[i] < mid)
                i++;
            while(a[j] > mid)
                j--;
            if(i <= j)
            {
                swap(a[i],a[j]);
                i++;
                j--;
            }
        }
        while(i <= j);
        if(l < j)
            qsort(l,j);
        if(i < r)
            qsort(i,r);
    }

    六、归并排序

    (1)基本思想:

    归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

    将已有序的子序列合并,得到完全有序的序列;

    即先使每个子序列有序,再使子序列段间有序。

    若将两个有序表合并成一个有序表,称为二路归并。

    (2)排序过程:

    例如有8个数据需要排序:10 4 6 3 8 2 5 7 

    归并排序主要分两大步:分解、合并。

    合并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;

    否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,

    然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。

    归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

    #include<cstdio>
    #define ll long long 
    using namespace std;
    
    const int maxn=5e5+5;
    int a[maxn],r[maxn],n;
    ll ans=0;
    
    void msort(int s,int t)
    {
        if(s==t) 
            return;
        int mid=s+t>>1;
        msort(s,mid),msort(mid+1,t);
        int i=s,j=mid+1,k=s;
        while(i<=mid&&j<=t)
            if(a[i]<=a[j]) 
                r[k++]=a[i++];
            else 
                r[k++]=a[j++],ans+=(ll)mid-i+1;
        while(i<=mid) 
            r[k]=a[i],k++,i++;
        while(j<=t) 
            r[k]=a[j],k++,j++;
        for(int i=s;i<=t;i++) 
            a[i]=r[i];
    }
    inline int read()
    {
        char ch=getchar();
        int sum=0,k=1;
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') 
                k=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') 
            sum=sum*10+(ch^48),ch=getchar();
        return sum*k;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) 
            a[i]=read();
        msort(1,n);
        printf("%lld
    ",ans);
        return 0;
    }

    各种排序算法的比较

    1.稳定性比较

           插入排序、冒泡排序、二叉树排序、二路归并排序及其他线形排序是稳定的。

           选择排序、希尔排序、快速排序、堆排序是不稳定的。

    2.时间复杂性比较

    插入排序、冒泡排序、选择排序的时间复杂性为O(n2);

    快速排序、堆排序、归并排序的时间复杂性为O(nlog2n);桶排序的时间复杂性为O(n);

    若从最好情况考虑,则直接插入排序和冒泡排序的时间复杂度最好,为O(n)

    其它算法的最好情况同平均情况相同;

    若从最坏情况考虑,则快速排序的时间复杂度为O(n2),直接插入排序和冒泡排序虽然平均情况相

    但系数大约增加一倍,所以运行速度将降低一半,最坏情况对直接选择排序、堆排序和归并排序影响不大。

    由此可知,在最好情况下,直接插入排序和冒泡排序最快;在平均情况下,快速排序最快;在最坏情况下,堆排序和归并排序最快。

    3.辅助空间的比较

    桶排序、二路归并排序的辅助空间为O(n)

    快速排序的辅助空间为O(log2n),最坏情况为O(n)

    其它排序的辅助空间为O(1);

    4.其它比较

    插入、冒泡排序的速度较慢,但参加排序的序列局部或整体有序时,这种排序能达到较快的速度。反而在这种情况下,快速排序反而慢了。

     当n较小时,对稳定性不作要求时宜用选择排序,对稳定性有要求时宜用插入或冒泡排序。

    若待排序的记录的关键字在一个明显有限范围内时,且空间允许是用桶排序。

    当n较大时,关键字元素比较随机,对稳定性没要求宜用快速排序。

    当n较大时,关键字元素可能出现本身是有序的,对稳定性没有要求时宜用堆排序

    快速排序是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;

    堆排序所需的辅助空间少于快速排序,并且不会出现快速排序可能出现的最坏情况。这两种排序都是不稳定的。

  • 相关阅读:
    <转>修正版 疯狂代码 写给WEB2.0的站长
    一组MSSQL千万数量级分页存储过程
    flash AS +xml 加载图片(使用MovieClipLoader实现LOADING)
    [转]优化SQL Server的内存占用之执行缓存
    日志记录组件[Log4net]详细介绍(转)
    IBatis.Net如何支持多个数据库 [转帖]
    c#导入导出EXCEL
    弄了个新blogs 欢迎捧场
    ActionSrcipt 资源
    一个鼠标拖出一个圆形的简单demo
  • 原文地址:https://www.cnblogs.com/darlingroot/p/10802729.html
Copyright © 2011-2022 走看看