[原创] 基础中的基础(一):简单排序算法总结(附代码)

  又到了一年毕业生找工作的时候,回想起自己找工作的经历,那种紧张、期盼、失望和纠结的情景仍然历历在目。在这里跟大家分享一些作者在找工作时总结的基础知识,希望能够大家带来一点点帮助^_^。作者菜鸟一枚,即使总结这些简单的基础知识,也难免出错,还请各位不吝赐教!

  本文主要总结了一些很基础的排序算法。通过一张表格,将这些算法的部分要点总结在一起,以方便查找和对比。本文最后给出了一些排序算法的C语言实现供大家参考。本文并不对各个算法进行详细介绍,有需要的读者请自行查找(维基百科对每种算法讲的很详细,见本文参考资料)。

  本文中的代码在:https://github.com/icemoon1987/sort_algorithm

 

一、简单排序算法总结

  表中简述部分的用语非常随意,很不专业。原因在于我并不赞成机械地背诵算法步骤,而是要理解算法处理方式和逻辑。此处的文字是在笔者理解算法的基础上,对于其中处理要点的口语化叙述,请大家参考。

  表中注意事项部分,是根据我在实现算法的过程中出现的错误总结的,同样非常个人化,很可能不全面(或是多余),仅供参考。

 

序号

名称

简述

复杂度

特性

注意事项

改进

1

冒泡排序

从数组最后开始,两两比较元素(n-1)

如果顺序不对,则交换元素位置

一轮过后,冒一个泡到当前首元素位置

减小数组长度,进入下一轮(n)

最好:O(n2)

平均:O(n2)

最差:O(n2)

空间:O(1)

稳定

就地排序

比较排序

冒泡方向与比较方向相配合

冒泡停止条件

如果前一轮没有交换过数据,则立刻停止

局部冒泡排序

鸡尾酒排序(双向冒泡)

奇偶排序(a[j],a[j+1]一轮j为奇数,一轮为偶数,在多处理器条件下,实现很快)

梳排序(定义gap,每次减1)

2

选择排序

每轮找到最小值(n-1)

与本轮首元素交换

减小数组长度进入下一轮(n)

最好:O(n2)

平均:O(n2)

最差:O(n2)

空间:O(1)

不一定稳定

就地排序

比较排序

每轮的首元素位置

堆排序

3

插入排序

从后边拿出一个元素

与前边各个元素比较,如果不符合要求,则依次向后移动前边的元素(n)

符合要求后,放下这个元素

加长数组长度,进入下一轮(n)

最好:O(n)

平均:O(n2)

最差:O(n2)

空间:O(1)

稳定

就地排序

比较排序

插入元素的位置

插入元素下标越界

二分查找排序,通过二分查找,更快找到插入位置

希尔排序,每次比较前进更多

4

希尔排序

规定增量,下标为增量倍数的元素为一组

各组进行插入排序

选择更小的增量,进入下一轮,直到为1

最好:O(n)

平均:与增量有关

最差:O(nlogn2)

空间:O(1)

 

不稳定

就地排序

比较排序

下标加入增量后的越界

插入元素位置

增量序列选择

1、4、10、23……

5

合并排序

将当前数组分为左右两部分

递归,分别排序左右两部分

合并左右两部分,比较两堆中最上边的元素,哪个小就放入合并序列

最好:Θ(n)

平均:Θ(nlogn)

最差:Θ(nlogn)

空间:Θ(n)

稳定

非就地排序

比较排序

分治递归

动态开辟空间、释放空间

递归终止条件(p<=r)

哨兵元素

函数输入参数能否取到

中间元素加入分组

 

6

堆排序

将数组整理成堆

交换根节点与最末尾元素交换,堆长减1

在根节点重新生成堆

重复,直到堆长为1

最好:O(nlogn)

平均:O(nlogn)

最差:Θ(nlogn)

空间:O(1)

不稳定

就地排序

比较排序

左右孩子下标

下标是否超过堆长检查

建树起始下标

Max_heapify函数递归

 

7

快速排序

选择基准元(最后元素)

小于基准元的元素交换到前方

将基准元与小于其的元素的后一个交换,实现大于基准元的元素都在后方

分为两部分,递归调用

最好:Θ(n2)

平均:Θ(nlogn)

最差:Θ(nlogn)

空间:O(1)

不稳定

就地排序

比较排序

分治

基准元本身不加入本组

函数输入参数能否取到

小于基准源元素起始下标

基准元交换元素下标

随机快速排序:随机选择基准元

三数取中,选取基准元

尾递归:用控制结构进行递归,而不是直接调用两次函数

8

计数排序

假设元素都小于等于k(或找到最大值)

开辟数组,记录各个元素出现的次数

从最前边,两两相加,计算小于等于元素的个数

从后向前遍历输入数组(保证稳定性),通过以上信息,直接将元素放入输出数组

更新小于等于元素的个数

最好:O(n+k)

平均:O(n+k)

最差:O(n+k)

空间:O(n+k)

稳定

非就地排序

非比较排序

线性时间

小于等于元素个数为1的数,放在第一个,下标为0

动态开辟数组大小k+1

下标越界

对输入有要求

 

9

基数排序

一位一位的进行排序(k)

从低位向高位

子排序算法需要稳定(例如计数排序)

最好:O(kn)

平均:O(kn)

最差:O(kn)

空间:O(kn)

 

稳定

非就地排序

非比较排序

线性时间

分割数字算法

 

10

桶排序

输入为[0,1]上的均匀分布

将[0,1]分为n个桶,将输入序列分到各个桶中

对每个桶进行插入排序

合并各个桶的结果

最好:

平均:O(n+k)

最差:O(n2)

空间:O(n*K)

稳定

非就地排序

非比较排序

线性时间

 

 

 

二、算法实现(C语言)

  1. 冒泡排序 

 1 int bubble_sort(int *input, const int len)
 2 {
 3     int i,j;
 4     int tem;
 5 
 6     if(NULL == input || len < 0)
 7     {
 8         return -1;
 9     }
10 
11     for(i=0 ; i<len ; ++i)
12     {
13         for(j = len-1 ; j>i ; --j)
14         {
15             if(input[j]<input[j-1])
16             {
17                 tem = input[j];
18                 input[j] = input[j-1];
19                 input[j-1] = tem;
20             }
21         }
22     }
23     return 0;
24 }

 

  2. 插入排序 

 1 int insert_sort::sort(int *input, const int size)
 2 {
 3     int i;
 4 
 5     if(NULL == input || size < 0)
 6     {
 7         return -1;
 8     }
 9 
10     for(i = 1 ; i < size ; ++i)
11     {
12         int key = input[i];
13         int j = i-1;
14         while(j>=0 && input[j]>key)
15         {
16             input[j+1] = input[j];
17             --j;
18         }
19         input[j+1]=key;
20     }
21 
22     return 0;
23 }

 

   3. 合并排序

 1 // 合并排序:主函数
 2 void merge_sort(int *a, const int p, const int r)
 3 {
 4     if(p<r)
 5     {
 6         int q = (p+r)/2;
 7         sort(a,p,q);
 8         sort(a,q+1,r);
 9         merge_pp(a,p,q,r);
10     }
11 
12     return ;
13 }
14 
15 // 合并排序:合并函数
16 void merge_pp(int *a, const int p, const int q, const int r)
17 {
18     vector<int> left;
19     vector<int> right;
20     int i;
21 
22     for(i = p ; i <= q ; ++i)
23     {
24         left.push_back(a[i]);
25     }
26 
27     for(i = q+1 ; i<=r ; ++i)
28     {
29         right.push_back(a[i]);
30     }
31 
32     i = p;
33 
34     while( (!left.empty()) && (!right.empty()) )
35     {
36         if(left.front() < right.front())
37         {
38             a[i++] = left.front();
39             left.erase(left.begin());
40         }
41         else
42         {
43             a[i++] = right.front();
44             right.erase(right.begin());
45         }
46     }
47 
48     while(!right.empty())
49     {
50         a[i++] = right.front();
51         right.erase(right.begin());
52     }
53 
54     while(!left.empty())
55     {
56         a[i++] = left.front();
57         left.erase(left.begin());
58     }
59 
60     return;
61 }

 

  4. 快速排序

 1 // 快速排序:主函数
 2 void sort(int *input, const int p, const int r)
 3 {
 4     if(p<r)
 5     {
 6         // 将输入数组分割成两部分
 7         int q = partition(input, p, r);
 8 
 9         // 递归调用,分别排序两部分
10         sort(input, p, q-1);
11         sort(input, q+1, r);
12     }
13 
14     return;
15 }
16 
17 // 快速排序:分割函数
18 int partition(int *input, const int p, const int r)
19 {
20     int key = input[r];
21     int i = p-1;
22     int j = p;
23     int tem = 0;
24 
25     for(; j < r ; ++j)
26     {
27         if( input[j] < key )
28         {
29             ++i;
30             tem = input[i];
31             input[i] = input[j];
32             input[j] = tem;
33         }
34     }
35 
36     tem = input[i+1];
37     input[i+1] = key;
38     input[r] = tem;
39 
40     return i+1;
41 }

 

   5. 堆排序

 1 // 最大堆排序:获取左孩子函数
 2 int left(const int i)
 3 {
 4     return (2*i+1);
 5 }
 6 
 7 // 最大堆排序:获取右孩子函数
 8 int right(const int i)
 9 {
10     return (2*i+2);
11 }
12 
13 // 最大堆排序:在某一节点重新生成最大堆函数
14 void max_heapify(int *input, const int i, const int heap_len)
15 {
16     int l = left(i);
17     int r = right(i);
18     int largest = i;
19     int tem = 0;
20 
21     if( l < heap_len && input[l]>input[largest] )
22     {
23         largest = l;
24     }
25 
26     if( r < heap_len && input[r]>input[largest] )
27     {
28         largest = r;
29     }
30 
31     if( largest != i )
32     {
33         tem = input[largest];
34         input[largest] = input[i];
35         input[i] = tem;
36         max_heapify(input,largest,heap_len);
37     }
38 
39     return;
40 }
41 
42 // 最大堆排序:构建最大堆函数
43 void build_max_heap(int *input, const int len)
44 {
45     int i = len/2-1;
46 
47     for(; i >= 0 ; --i)
48     {
49         max_heapify(input, i , len);
50     }
51 
52     return;
53 }
54 
55 // 最大堆排序:主函数
56 void maxheap_sort(int *input, const int len)
57 {
58     int tem = 0;
59     int heap_len = len;
60         
61     // 将输入数组生成最大堆
62     build_max_heap(input,len);
63 
64     while( heap_len > 0)
65     {
66         // 交换根节点与最末尾节点
67         tem = input[0];
68         input[0] = input[heap_len-1];
69         input[heap_len-1] = tem;
70 
71         // 在根节点重新生成最大堆
72         --heap_len;
73         max_heapify(input, 0, heap_len);
74     }
75 
76     return;
77 }

 

  6. 计数排序

 1 // 计数排序:主函数
 2 void count_sort(int *input, int *output, const int len)
 3 {
 4     int max = find_max(input, len);
 5     int i;
 6 
 7     int *tem = (int *)malloc(sizeof(int) * (max + 1));
 8     for(i = 0; i < max+1 ; ++i)
 9         tem[i] = 0;
10 
11 // 记录各个元素出现次数
12     for(i = 0 ; i < len ; ++i)
13         tem[input[i]]++;
14 
15 // 对记录数据进行修改:从最前边,两两相加,计算小于等于某个元素的元素个数
16     for(i = 1 ; i < max+1 ; ++i)
17         tem[i] += tem[i-1];
18 
19 // 将元素直接放入输出数组
20     for(i = 0 ; i < len ; ++i)
21     {
22         output[ tem[input[i]] - 1 ] = input[i];
23         tem[input[i]]--;
24     }
25     return;
26 }
27 
28 // 计数排序:寻找最大值函数
29 int find_max(int *input, const int len)
30 {
31     int max = 0;
32     int i;
33 
34     for(i = 0 ; i < len ; ++i)
35         if( input[i]>max )
36             max = input[i];
37 
38     return max;
39 }

 

  好了,本文的主要内容就这些了,最后再跟大家分享几个学习算法的个人心得:

    1、学习算法不光是学习知识,更是学习一种思维方式。在积累了一定算法基础之后,相信您对新问题、新算法都能够有自己的感觉和思路。

    2、不要背诵算法步骤,要理解算法步骤后的方法和逻辑。单纯地背诵算法没有用处,一定要理解算法解决问题的方法。如果理解了方法,算法步骤不过是用一种有逻辑的方式将这种方法表达出来而已。理解算法对记忆算法也有极大帮助。

    3、算法学习和其他计算机知识学习一样,一定要注重实践。在实现的过程中,我们将发现很多算法中的具体问题,对算法的细节和思想都有可能有新的发现。

  

本文参考资料:

    http://en.wikipedia.org/wiki/Sorting_algorithm  维基百科上的那些算法演示 gif 挺有趣的:)

    《算法导论》(第二版) 

posted @ 2013-09-09 10:07  icemoon1987  阅读(946)  评论(2编辑  收藏  举报