zoukankan      html  css  js  c++  java
  • 数据结构(复习)--------关于快速排序,归并排序等多种算法(转载于白话经典算法)

    快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采 用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考 试如软考,考研中也常常出现快速排序的身影。

    总的说来,要直接默写出快速排序还是有一定难度的,因为本人就自己的理解对快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定

     

    快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。

    该方法的基本思想是:

    1.先从数列中取出一个数作为基准数。

    2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

    3.再对左右区间重复第二步,直到各区间只有一个数。

     

    虽然快速排序称为分治法,但分治法这三个字显然无法很好的概括快速排序的全部步骤。因此我的对快速排序作了进一步的说明:挖坑填数+分治法:

    先来看实例吧,定义下面再给出(最好能用自己的话来总结定义,这样对实现代码会有帮助)。

     

    以一个数组作为示例,取区间第一个数为基准数。

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    72

    6

    57

    88

    60

    42

    83

    73

    48

    85

    初始时,i = 0;  j = 9;   X = a[i] = 72

    由于已经将a[0]中的数保存到X中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。

    从 j开始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++;  这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i开始向后找一个大于X的数,当 i=3,符合条件,将a[3]挖出再填到上一个坑中a[8]=a[3]; j--;

     

    数组变为:

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    48

    6

    57

    88

    60

    42

    83

    73

    88

    85

     i = 3;   j = 7;   X=72

    再重复上面的步骤,先从后向前找,再从前向后找

    从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

    从i开始向后找,当i=5时,由于i==j退出。

    此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将X填入a[5]。

     

    数组变为:

    1

    3

    5

    7

    9

    48

    6

    57

    42

    60

    72

    83

    73

    88

    85

    可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。

     

     

    对挖坑填数进行总结

    1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。

    2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。

    3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。

    4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中。

    照着这个总结很容易实现挖坑填数的代码:

    -------------------------------------------------------------------------------------------------------------------------------

        int AdjustArray(int s[], int l, int r) //返回调整后基准数的位置  
        {  
            int i = l, j = r;  
            int x = s[l]; //s[l]即s[i]就是第一个坑  
            while (i < j)  
            {  
                // 从右向左找小于x的数来填s[i]  
                while(i < j && s[j] >= x)   
                    j--;    
                if(i < j)   
                {  
                    s[i] = s[j]; //将s[j]填到s[i]中,s[j]就形成了一个新的坑  
                    i++;  
                }  
          
                // 从左向右找大于或等于x的数来填s[j]  
                while(i < j && s[i] < x)  
                    i++;    
                if(i < j)   
                {  
                    s[j] = s[i]; //将s[i]填到s[j]中,s[i]就形成了一个新的坑  
                    j--;  
                }  
            }  
            //退出时,i等于j。将x填到这个坑中。  
            s[i] = x;  
          
            return i;  
        }  
    
    再写分治法的代码:
    [cpp] view plaincopy
    
        void quick_sort1(int s[], int l, int r)  
        {  
            if (l < r)  
            {  
                int i = AdjustArray(s, l, r);//先成挖坑填数法调整s[]  
                quick_sort1(s, l, i - 1); // 递归调用   
                quick_sort1(s, i + 1, r);  
            }  
        }  
    
    这样的代码显然不够简洁,对其组合整理下:
    [cpp] view plaincopy
    
        //快速排序  
        void quick_sort(int s[], int l, int r)  
        {  
            if (l < r)  
            {  
                //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1  
                int i = l, j = r, x = s[l];  
                while (i < j)  
                {  
                    while(i < j && s[j] >= x) // 从右向左找第一个小于x的数  
                        j--;    
                    if(i < j)   
                        s[i++] = s[j];  
                      
                    while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数  
                        i++;    
                    if(i < j)   
                        s[j--] = s[i];  
                }  
                s[i] = x;  
                quick_sort(s, l, i - 1); // 递归调用   
                quick_sort(s, i + 1, r);  
            }  
        }  
    
    快速排序还有很多改进版本,如随机选择基准数,区间内数据较少时直接用另的方法排序以减小递归深度。有兴趣的筒子可以再深入的研究下。
    
     
    
    注1,有的书上是以中间的数作为基准数的,要实现这个方便非常方便,直接将中间的数和第一个数进行交换就可以了。
    
     
    
     转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/6684558
    

     ------------------------------------------------------------------------------------------------------------------------------------------------------------

    下面是我的渣渣代码;

     // 关于数据结构的总结与复习  Coding
    //关于快速排序的算法的实现
    #include <cstdio>
    #include <cstdlib>
    //#define _OJ_
    
    void
    Quick_sort(int a[], int left, int right)
    {
        int i, j, x;
        if(left < right) {
        i = left;    j = right;    x = a[left];
        while (i < j) {
            while (i < j && x <= a[j])
                j--;
            if(i < j)    a[i++] = a[j];
    
            while (i < j && x >= a[i])
                i++;
            if(i < j)    a[j--] = a[i];
        }
        a[i] = x;
        Quick_sort(a, left, i - 1);
        Quick_sort(a, i + 1, right);
       }
    }
    
    
    int main(int argc, char const *argv[]) {
    #ifndef _OJ_ //ONLINE JUDGE
           freopen("input.txt", "r", stdin);
           //freopen("output.txt", "w", stdout);
    #endif
        
        int n;
        int a[10];
        // scanf("%d", &n);
        for (int i = 0; i < 10; i++)
            scanf("%d", &a[i]);
        Quick_sort(a, 0, 9);
        for (int i = 0; i < 9; i++) {
            printf("%d ", a[i]);
        }
    
        return 0;
    }
    

     ------------------------------------------------------------------------------------------------------------------------------------------------------

    2.归并排序的算法实现

    ------------------------------------------------------------------------------------------------------------------------------------------------------

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

    首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

    可以看出合并有序数列的效率是比较高的,可以达到O(n)。

    解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

    可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。

        //将有序数组a[]和b[]合并到c[]中  
        void MemeryArray(int a[], int n, int b[], int m, int c[])  
        {  
            int i, j, k;  
          
            i = j = k = 0;  
            while (i < n && j < m)  
            {  
                if (a[i] < b[j])  
                    c[k++] = a[i++];  
                else  
                    c[k++] = b[j++];   
            }  
          
            while (i < n)  
                c[k++] = a[i++];  
          
            while (j < m)  
                c[k++] = b[j++];  
        }  
    

    ------------------------------------------------------------------------------------------------------------------------------------------------------------

        //将有二个有序数列a[first...mid]和a[mid...last]合并。  
        void mergearray(int a[], int first, int mid, int last, int temp[])  
        {  
            int i = first, j = mid + 1;  
            int m = mid,   n = last;  
            int k = 0;  
              
            while (i <= m && j <= n)  
            {  
                if (a[i] <= a[j])  
                    temp[k++] = a[i++];  
                else  
                    temp[k++] = a[j++];  
            }  
              
            while (i <= m)  
                temp[k++] = a[i++];  
              
            while (j <= n)  
                temp[k++] = a[j++];  
              
            for (i = 0; i < k; i++)  
                a[first + i] = temp[i];  
        }  
        void mergesort(int a[], int first, int last, int temp[])  
        {  
            if (first < last)  
            {  
                int mid = (first + last) / 2;  
                mergesort(a, first, mid, temp);    //左边有序  
                mergesort(a, mid + 1, last, temp); //右边有序  
                mergearray(a, first, mid, last, temp); //再将二个有序数列合并  
            }  
        }  
          
        bool MergeSort(int a[], int n)  
        {  
            int *p = new int[n];  
            if (p == NULL)  
                return false;  
            mergesort(a, 0, n - 1, p);  
            delete[] p;  
            return true;  
        }  
    

     

     

    归 并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为 O(N*logN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆 排序)也是效率比较高的。

     

    在本人电脑上对冒泡排序,直接插入排序,归并排序及直接使用系统的qsort()进行比较(均在Release版本下)

    对20000个随机数据进行测试:

    对50000个随机数据进行测试:

    再对200000个随机数据进行测试:

    注:有的书上是在mergearray()合并有序数列时分配临时数组,但是过多的new操作会非常费时。因此作了下小小的变化。只在MergeSort()中new一个临时数组。后面的操作都共用这一个临时数组。

  • 相关阅读:
    Number Two
    蝴蝶结
    webug3.0靶场渗透基础Day_1
    SQL SERVER2014的安装
    SQLILABS学习笔记(一)
    关于暴力破解的一些学习笔记(pikachu)
    sql注入学习笔记
    CSRF与平行越权的区别
    任意文件下载(pikachu)
    XSS跨站脚本攻击学习笔记(pikachu)
  • 原文地址:https://www.cnblogs.com/airfand/p/5084091.html
Copyright © 2011-2022 走看看