排序算法


快排是目前公认的最好的排序算法,当排序关键字是随机分布是,平均时间最短。


这个关于排序的网站不错,还很全!!

http://www.cnblogs.com/kkun/archive/2011/11/23/2260312.html


一、选择排序

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 [49 97 65 76]

第五趟排序后 13 27 38 49 49 [97 97 76]

第六趟排序后 13 27 38 49 49 76 [76 97]

第七趟排序后 13 27 38 49 49 76 76 [ 97]

最后排序结果 13 27 38 49 49 76 76 97

3.

void selectionSort(Type* arr,long len){

   long i=0,j=0;/*iterator value*/

   long maxPos;

   assertF(arr!=NULL,"In InsertSort sort,arr is NULL\n");

   for(i=len-1;i>=1;i--) {

             maxPos=i;

             for(j=0;j<i;j++)

                  if(arr[maxPos]<arr[j])maxPos=j;

             if(maxPos!=i)swapArrData(arr,maxPos,i);

   }

}

视觉直观感受7种常用排序算法

选择排序法的第一层循环从起始元素开始选到倒数第二个元素,主要是在每次进入的第二层循环之 前,将外层循环的下标赋值给临时变量,接下来的第二层循环中,如果发现有比这个最小位置处的元素更小的元素,则将那个更小的元素的下标赋给临时变量,最 后,在二层循环退出后,如果临时变量改变,则说明,有比当前外层循环位置更小的元素,需要将这两个元素交换.


二.直接插入排序

复制代码
        /// <summary>
        /// 插入排序
        /// </summary>
        /// <param name="unsorted"></param>
        static void insertion_sort(int[] unsorted)
        {
            for (int i = 1; i < unsorted.Length; i++)
            {
                if (unsorted[i - 1] > unsorted[i])
                {
                    int temp = unsorted[i];
                    int j = i;
                    while (j > 0 && unsorted[j - 1] > temp)
                    {
                        unsorted[j] = unsorted[j - 1];
                        j--;
                    }
                    unsorted[j] = temp;
                }
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            insertion_sort(x);
            foreach (var item in x)
            {
                if (item > 0)
                    Console.WriteLine(item + ",");
            }
            Console.ReadLine();
        }
复制代码


三. 冒泡排序

[算法思想]:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为 R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到 最后任何两个气泡都是轻者在上,重者在下为止。

 

    [算法]:

     void BubbleSort(SeqList R) {

     //R(l..n)是待排序的文件,采用自下向上扫描,对R做冒泡排序

         int i,j;

         Boolean exchange; //交换标志

         for(i=1;i<n;i++){ //最多做n-1趟排序

             exchange=FALSE; //本趟排序开始前,交换标志应为假

             for(j=n-1;j>=i;j--) //对当前无序区R[i..n]自下向上扫描

                 if(R[j+1].key<R[j].key){//交换记录

                     R[0]=R[j+1]; //R[0]不是哨兵,仅做暂存单元

                     R[j+1]=R[j];

                     R[j]=R[0];

                     exchange=TRUE; //发生了交换,故将交换标志置为真

                 }

             if(!exchange) return;//本趟排序未发生交换,提前终止算法

         } //endfor(外循环)

     } //BubbleSort

    [分析]:起泡排序的结束条件为:最后一趟没有进行“交换”。从起泡排序的过程可 见,起泡排序是一个增加有序序列长度的过程,也是一个缩小无序序列长度的过程,每经过一趟起泡,无序序列的长度只缩小1。 [算法思想]:将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从 下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。



四. 希尔排序

基本思想:

     先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然 后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记 录放在同一组中进行直接插入排序为止。

     该方法实质上是一种分组插入方法。

给定实例的shell排序的排序过程

     假设待排序文件有10个记录,其关键字分别是:

        49,38,65,97,76,13,27,49,55,04。

     增量序列的取值依次为:

        5,3,1

依次实现间隔内排序

复制代码
        static void shell_sort(int[] unsorted, int len)
        {
            int group, i, j, temp;
            for (group = len / 2; group > 0; group /= 2)
            {
                for (i = group; i < len; i++)
                {
                    for (j = i - group; j >= 0; j -= group)
                    {
                        if (unsorted[j] > unsorted[j + group])
                        {
                            temp = unsorted[j];
                            unsorted[j] = unsorted[j + group];
                            unsorted[j + group] = temp;
                        }
                    }
                }
            }
        }


        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            shell_sort(x, x.Length);
            foreach (var item in x)
            {
                Console.WriteLine(item);
            }
            Console.ReadLine();
        }
复制代码

视觉直观感受7种常用排序算法



五. 堆排序

由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。它是对数据的有序性不敏感的一种算法。但堆排序将需要做两个步骤:-是建堆,二是排序(调整堆)。所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能。

Heap4

视觉直观感受7种常用排序算法

堆排序的C语言实现
01 #include <stdio.h>
02 #include <stdlib.h>
03  
04 void HeapSort(int num[],int size);
05 void BuildHeap(int num[] ,int size);
06 void PercolateDown(int num[] , int index,int size);
07 void PrintHeap(const char* strMsg,int array[],int nLength);
08 void Swap(int num[] , int v, int u);
09  
10 int main(int argc, char *argv[])
11 {
12   int data[13]={8,5,4,6,13,7,1,9,12,11,3,10,2};
13   HeapSort(data,13);
14    
15   system("PAUSE"); 
16   return 0;
17 }
18  
19  
20 void HeapSort(int num[] ,int size)
21 {
22     int i;
23     int iLength=size;
24      
25     PrintHeap("Befor Sort:",num,iLength);
26      
27     BuildHeap(num,size);// 建立小顶堆  
28      
29     for (i = iLength - 1; i >= 1; i--) {  
30         Swap(num, 0, i);// 交换  
31         size--;// 每交换一次让规模减少一次  
32         PercolateDown(num, 0,size);// 将新的首元素下滤操作
33         PrintHeap("Sort Heap:",num,iLength); 
34     }
35 }
36  
37 // 建堆方法,只需线性时间建好  
38 void BuildHeap(int num[] ,int size) {
39     int i;
40     for (i = size / 2 - 1; i >= 0; i--) {// 对前一半的节点(解释为“从最后一个非叶子节点开始,将每个父节点都调整为最小堆”更合理一些)  
41         PercolateDown(num, i,size);// 进行下滤操作
42         PrintHeap("Build heap:",num,size);
43     }  
44 }
45      
46 // 对该数进行下滤操作,直到该数比左右节点都小就停止下滤  
47 void PercolateDown(int num[] , int index,int size) {  
48     int min;// 设置最小指向下标  
49     while (index * 2 + 1<size) {// 如果该数有左节点,则假设左节点最小  
50         min = index * 2 + 1;// 获取左节点的下标  
51         if (index * 2 + 2<size) {// 如果该数还有右节点  
52             if (num[min] > num[index * 2 + 2]) {// 就和左节点分出最小者  
53                 min = index * 2 + 2;// 此时右节点更小,则更新min的指向下标  
54             }  
55         }  
56         // 此时进行该数和最小者进行比较,  
57         if (num[index] < num[min]) {// 如果index最小,  
58             break;// 停止下滤操作  
59         else {  
60             Swap(num, index, min);// 交换两个数,让大数往下沉  
61             index = min;// 更新index的指向  
62         }  
63     }// while  
64 }
65      
66 // 给定数组交换两个数的位置  
67 void Swap(int num[] , int v, int u) { 
68     int temp = num[v];  
69     num[v] = num[u];  
70     num[u] = temp;  
71 }  
72  
73 void PrintHeap(const char* strMsg,int array[],int nLength)
74 {
75      int i;
76      printf("%s",strMsg);
77      for(i=0;i<nLength;i++)
78      {
79         printf("%d ",array[i]);
80      }
81      printf("\n");
82 }

下面也是C语言的实现,稍微改动了下:

01 #include <stdio.h>
02 #include <stdlib.h>
03  
04 void HeapSort(int num[],int size);
05 void BuildHeap(int num[] ,int size);
06 void PercolateDown(int num[] , int index,int size);
07 void PrintHeap(const char* strMsg,int array[],int nLength);
08 void Swap(int num[] , int v, int u);
09  
10 int main(int argc, char *argv[])
11 {
12   /* 将数组看成完全二叉树的中序遍历结果的线性存储 */
13   int data[13]={8,5,4,6,13,7,2,9,12,11,3,10,1};
14   HeapSort(data,13);
15    
16   system("PAUSE");   
17   return 0;
18 }
19  
20  
21 void HeapSort(int num[] ,int size)
22 {
23     int i;
24     int iLength=size;
25      
26     PrintHeap("Befor Sort:",num,iLength);
27      
28     BuildHeap(num,size);// 建立小顶堆  
29      
30     for (i = iLength - 1; i >= 1; i--) {  
31         Swap(num, 0, i);// 交换  
32         size--;// 每交换一次让规模减少一次  
33         PercolateDown(num, 0,size);// 将新的首元素下滤操作
34         PrintHeap("Sort Heap:",num,iLength); 
35     }
36 }
37  
38 /* 建堆方法,只需线性时间建好;
39    建堆的结果:数组的第一个元素(即树根)是所有元素中的最小值,索引小于等于size/2-1的其它元素(即其它非叶子节点)的值都是其所在子树的最小值 */  
40 void BuildHeap(int num[] ,int size) {
41     int i;
42     //从最后一个非叶子节点开始,对每个非叶子节点进型最小根调整,保证每个根节点都是其子树中的最小值
43     for (i = size / 2 - 1; i >= 0; i--) {  
44         PercolateDown(num, i,size);// 进行下滤操作
45         PrintHeap("Build heap:",num,size);
46     }  
47 }
48      
49 /* 对该数进行下滤操作,直到该数比左右节点都小就停止下滤。
50    即对某个根节点的值进行位置下降调整,使该值比其左右子节点都小;
51    若该节点是叶子节点,则无法while循环 */
52 void PercolateDown(int num[] , int index,int size) {  
53     int min;// 设置最小指向下标  
54     while (index * 2 + 1<size) {// 如果该数有左节点,则假设左节点最小  
55         min = index * 2 + 1;// 获取左节点的下标  
56         if (index * 2 + 2<size) {// 如果该数还有右节点  
57             if (num[min] > num[index * 2 + 2]) {// 就和左节点分出最小者  
58                 min = index * 2 + 2;// 此时右节点更小,则更新min的指向下标  
59             }  
60         }  
61         // 此时进行该数和最小者进行比较,  
62         if (num[index] < num[min]) {// 如果index最小,  
63             break;// 停止下滤操作  
64         else {  
65             Swap(num, index, min);// 交换两个数,让大数往下沉  
66             index = min;// 更新index的指向  
67         }  
68     }// while  
69 }
70      
71 // 给定数组交换两个数的位置  
72 void Swap(int num[] , int v, int u) { 
73     int temp = num[v];  
74     num[v] = num[u];  
75     num[u] = temp;  
76 }  
77  
78 void PrintHeap(const char* strMsg,int array[],int nLength)
79 {
80      int i;
81      printf("%s",strMsg);
82      for(i=0;i<nLength;i++)
83      {
84         printf("%d ",array[i]);
85      }
86      printf("\n");
87 }


六. 快速排序

快速排序的基本思路是:首先我们选择一个中间值middle(程序中我们可使用数组中间值),把比中间值小的放在其左边,比中间值大的放在其右边。由于这个排序算法较复杂,我们先给出其进行一次排序的程序框架(从各类数据结构教材中可得)

                   视觉直观感受7种常用排序算法

复制代码
        static int partition(int[] unsorted, int low, int high)
        {
            int pivot = unsorted[low];
            while (low < high)
            {
                while (low < high && unsorted[high] > pivot) high--;
                unsorted[low] = unsorted[high];
                while (low < high && unsorted[low] <= pivot) low++;
                unsorted[high] = unsorted[low];
            }
            unsorted[low] = pivot;
            return low;
        }

        static void quick_sort(int[] unsorted, int low, int high)
        {
            int loc = 0;
            if (low < high)
            {
                loc = partition(unsorted, low, high);
                quick_sort(unsorted, low, loc - 1);
                quick_sort(unsorted, loc + 1, high);
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            quick_sort(x, 0, x.Length - 1);
            foreach (var item in x)
            {
                Console.WriteLine(item + ",");
            }
            Console.ReadLine();
        }
复制代码

  

 七,归并排序

原理:将原序列划分为有序的两个序列,然后利用归并算法进行合并,合并之后即为有序序列。

要点:归并、分治

复制代码
        static void merge(int[] unsorted, int first, int mid, int last, int[] sorted)
        {
            int i = first, j = mid;
            int k = 0;
            while (i < mid && j < last)
                if (unsorted[i] < unsorted[j])
                    sorted[k++] = unsorted[i++];
                else
                    sorted[k++] = unsorted[j++];

            while (i < mid)
                sorted[k++] = unsorted[i++];
            while (j < last)
                sorted[k++] = unsorted[j++];

            for (int v = 0; v < k; v++)
                unsorted[first + v] = sorted[v];
        }

        static void merge_sort(int[] unsorted, int first, int last, int[] sorted)
        {
            if (first + 1 < last)
            {
                int mid = (first + last) / 2;
                Console.WriteLine("{0}-{1}-{2}", first, mid, last);
                merge_sort(unsorted, first, mid, sorted);
                merge_sort(unsorted, mid, last, sorted);
                merge(unsorted, first, mid, last, sorted);
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 6, 2, 4, 1, 5, 9 };
            int[] sorted = new int[x.Length];
            merge_sort(x, 0, x.Length, sorted);
            for (int i = 0; i < sorted.Length; i++)
            {
                if (x[i] > 0)
                    Console.WriteLine(x[i]);
            }
            Console.ReadLine();
        }
复制代码

 


 八 基数排序Radix sort

原理类似桶排序,这里总是需要10个桶,多次使用

首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,暂时忽视十位数

例如

待排序数组[62,14,59,88,16]简单点五个数字

分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样

|  0  |  0  | 62 |  0  | 14 |  0  | 16 |  0  |  88 | 59 |

|  0  |  1  |  2  |  3  |  4 |  5  |  6  |  7  |  8  |  9  |桶编号

将桶里的数字顺序取出来,

输出结果:[62,14,16,88,59]

再次入桶,不过这次以十位数的数字为准,进入相应的桶,变成下边这样:

由于前边做了个位数的排序,所以当十位数相等时,个位数字是由小到大的顺序入桶的,就是说,入完桶还是有序

|  0  | 14,16 |  0  |  0  |  0  | 59 | 62  | 0  | 88  |  0  |

|  0  |  1      |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |桶编号


因为没有大过100的数字,没有百位数,所以到这排序完毕,顺序取出即可

最后输出结果:[14,16,59,62,88]

代码仅供参考

复制代码
        /// <summary>
        /// 基数排序
        /// 约定:待排数字中没有0,如果某桶内数字为0则表示该桶未被使用,输出时跳过即可
/// </summary>
        /// <param name="unsorted">待排数组</param>
        /// <param name="array_x">桶数组第一维长度</param>
        /// <param name="array_y">桶数组第二维长度</param>
        static void radix_sort(int[] unsorted, int array_x = 10, int array_y = 100)
        {
            for (int i = 0; i < array_x/* 最大数字不超过999999999...(array_x个9) */; i++)
            {
                int[,] bucket = new int[array_x, array_y];
                foreach (var item in unsorted)
                {
                    int temp = (item / (int)Math.Pow(10, i)) % 10;
                    for (int l = 0; l < array_y; l++)
                    {
                        if (bucket[temp, l] == 0)
                        {
                            bucket[temp, l] = item;
                            break;
                        }
                    }
                }
                for (int o = 0, x = 0; x < array_x; x++)
                {
                    for (int y = 0; y < array_y; y++)
                    {
                        if (bucket[x, y] == 0) continue;
                        unsorted[o++] = bucket[x, y];
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            int[] x = { 999999999, 65, 24, 47, 13, 50, 92, 88, 66, 33, 22445, 10001, 624159, 624158, 624155501 };
            radix_sort(x);
            foreach (var item in x)
            {
                if (item > 0)
                    Console.WriteLine(item + ",");
            }
            Console.ReadLine();
        }
复制代码

 

几种算法的比较和选择

1.稳定性比较

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

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

2.时间复杂性比较

 

    平均情况 最好情况最坏情况
归并排序O(nlogn) O(nlogn)O(nlogn)
基数排序O(n)O(n)O(n)
快速排序O(nlogn)O(nlogn)O(n2)
希尔排序O(n1.5)O(n)O(n1.5)
插入排序O(n2)O(n)O(n2)

选择排序

O(n2)O(n2)O(n2)

 

 

 

 

 

 


3.辅助空间的比较

线形排序、二路归并排序的辅助空间为O(n),其它排序的辅助空间为O(1);

4.其它比较

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

反而在这种情况下,快速排序反而慢了。

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

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

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

当n较大时,关键字元素可能出现本身是有序的,对稳定性有要求时,空间允许的情况下。

宜用归并排序。

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


相关知识介绍(所有定义只为帮助读者理解相关概念,并非严格定义):
1、稳定排序和非稳定排序

简单地说就是所有相等的数经过某种排序方法后,仍能保持它们在排序之前的相对次序,我们就
说这种排序方法是稳定的。反之,就是非稳定的。
比如:一组数排序前是a1,a2,a3,a4,a5,其中a2=a4,经过某种排序后为a1,a2,a4,a3,a5,
则我们说这种排序是稳定的,因为a2排序前在a4的前面,排序后它还是在a4的前面。假如变成a1,a4,
a2,a3,a5就不是稳定的了。

2、内排序和外排序

在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序;
在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序。


、 若n较小n〈=50采用直接插入或直接选择排序。 

2、 若文件初始状态已基本有序正序直接插入冒泡或快速。 

3、 N较大采用时间复杂度为Onlgn的算法快速堆或归并。 快速排序是目前认为最好的内部排序方法但是堆排序的辅助空间比快速排序少并且最坏情况比快速好但这两种排序算法都不稳定如要求稳定的排序算法用归并排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值