【排序算法】用C++实现各种排序算法

1. 在平时的学习中,很经常听到各种排序算法,其各有优缺点。尝试自己用C++实现各排序算法,作为对算法的基础学习。

  常见的内部排序算法:

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 归并排序
  • 快速排序
  • 堆排序
  • 希尔排序
  • 基数排序


2. 各种排序算法的思想及其C++实现(以需排列元素有n个,目标为得到从小到大的序列为例)

 

2.1 冒泡排序

  冒泡排序是我接触到的第一个排序算法(高中参加计算机竞赛时学过),其算法思想相对简单,用代码实现起来也较简单。即每次通过不断比较相邻的两个值,将最大值移至末尾:先比较第一个和第二个数,若第二个数较小,则交换两个数,将两数中的较大者移到第二个;再比较第二个和第三个,将两数中的较大者移到第三个;...;最后即可将整列数中的最大值移到最末尾。上述过程只能将一个数移到正确位置,循环n-1次后,即可全部排序完成,得到从小到大的序列。

C++代码:(使用Visual Studio 2013为IDE,文章中之后的代码将只包含排序函数部分)

 1 // BubbleSort.cpp : 定义控制台应用程序的入口点。
 2 //
 3 
 4 #include "stdafx.h"
 5 #include <iostream>
 6 
 7 using namespace std;
 8 
 9 void BubbleSort(int[], int);
10 
11 int _tmain(int argc, _TCHAR* argv[])
12 {
13     int array[] = { 34, 65, 12, 43, 67, 5, 78, 43, 3, 70 };
14     int length = sizeof(array) / sizeof(int);
15     for (int i = 0; i < length - 1; i++)
16         cout << array[i] << " ";
17     cout << array[length - 1] << endl;
18     cout << "length=" << length << endl;
19 
20     BubbleSort(array, length);
21     
22     for (int i = 0; i < length - 1; i++)
23         cout << array[i] << " ";
24     cout << array[length - 1] << endl;
25     cout << "length=" << length << endl;
26     system("pause");
27     return 0;
28 }
29 
30 void BubbleSort(int a[], int length)
31 {
32     for (int i = 1; i < length; i++)    //i等于1到9,指循环的次数
33         for (int j = 0; j < length - 1; j++)
34             if (a[j] > a[j + 1])
35                 swap(a[j], a[j + 1]);
36 }

 

 

2.2 选择排序

  选择排序与冒泡排序的思想有些相似:它通过依次将第1个数与后续所有数比较,发现比第一个数小的数,就将其与第一个数交换,从而使第一个数为所有数中的最小值;再将第二个数依次与后续所有数比较,从而将次小的数移至序列第二个;... ;n-1次操作后,即完成n个数从小到大的排序。

C++代码:

1 void SelectionSort(int a[], int length)
2 {
3     for (int i = 0; i < length - 1; i++)
4         for (int j = i + 1; j < length; j++)
5             if (a[i] > a[j])
6                 swap(a[i], a[j]);
7 }

 

 

2.3 插入排序

  插入排序默认第一项是排好序的(因为只有一项,不会是乱序的),然后将第二项插入到这个排好序的序列中,然后是第三项,第四项,...,第n项。即完成排序。

C++代码:(这里采用了二分查找)

 1 template<class Type>
 2 void InsertionSort(Type* array, int start, int end)
 3 {
 4     int flag = start + 1;    //flag之前是排好序的
 5     for (flag = start + 1; flag <= end; flag++)
 6     {
 7         int temp = array[flag];
 8         int left = start, right = flag -1;
 9         int mid = (left + right) / 2;
10 
11         if (temp < array[left])
12         {
13             for (int i = right + 1; i > left; i--)
14             {
15                 array[i] = array[i - 1];
16             }
17             array[left] = temp;
18             continue;
19         }
20         if (temp >= array[right])
21         {
22             continue;
23         }
24 
25         while (array[mid] != temp && (right - left) > 1)
26         {
27             if (temp < array[mid])
28             {
29                 right = mid;
30                 mid = (left + right) / 2;
31             }
32             if (temp > array[mid])
33             {
34                 left = mid;
35                 mid = (left + right) / 2;
36             }
37         }
38         while (array[mid] <= temp)
39             mid++;
40         for (int i = right + 1; i > mid; i--)
41         {
42             array[i] = array[i - 1];
43         }
44         array[mid] = temp;
45     }
46 }

 

 

2.4 归并排序

  归并排序先将一列无序的数分成两列无序的数,再将两列无序数分成四列...直到分成的每列数只含一个数。这些只含一个数的“无序的一列数”其实已经是“有序的”,因为只有一个数,没有“乱序之说”。再将这些“有序的列”逆向两两归并,得到长度更长的有序的列,直到将所有的项归并为一列有序的列。即完成排序。

C++代码:

 1 template<class temp>
 2 void MergeSort(temp * a, int begin, int end)        //对数列a[]从begin到end之间的元素进行排序。
 3 {
 4     if (end - begin == 1)    //若只有两个元素,交换即可。
 5         if (a[begin] > a[end])
 6             swap(a[begin], a[end]);
 7 
 8     if (end - begin > 1)    //大于两个元素使用归并排序,这是个递归的过程。
 9     {
10         int mid = (begin + end) / 2;
11         MergeSort(a, begin, mid);
12         MergeSort(a, mid + 1, end);
13         Merge(a, begin, mid, end);
14     }
15 }
16 
17 //数列a[]从begin到mid,以及从mid + 1到end,是已经排好序的
18 //现在将两部分归并成一个排好序的数列
19 template<class temp>
20 void Merge(temp*a, int begin, int mid, int end)        
21 {
22     temp * tempArray = new temp[end - begin + 1];    //暂存排序的结果。
23     bool * IsMerged = new bool[end - begin + 1];    //标记元素是否已经被归并到tempArray中。
24     for (int i = 0; i < end - begin + 1; i++){
25         tempArray[i] = 0;
26         IsMerged[i] = false;
27     }
28 
29     int indexA = begin;        //第一部分第一个元素。
30     int indexB = mid + 1;    //第二部分第一个元素。
31     int index = 0;            //用以插入到tempArray。
32     while (indexA <= mid && indexB <= end)
33     {
34         if (a[indexA] <= a[indexB])
35         {
36             tempArray[index] = a[indexA];
37             index++;
38             IsMerged[indexA - begin] = true;
39             indexA++;
40         }
41         else
42         {
43             tempArray[index] = a[indexB];
44             index++;
45             IsMerged[indexB - begin] = true;
46             indexB++;
47         }
48     }
49 
50     //将剩下的元素插入到tempArray
51     for (int i = 0; i < end - begin + 1; i++)
52         if (IsMerged[i] == false)
53             tempArray[index++] = a[begin + i];
54     
55     for (int i = 0; i < end - begin + 1; i++)
56         a[begin + i] = tempArray[i];
57 }

 归并排序的主要部分在于Merge函数的实现,即将两列有序数合并成一列。

 

 

2.5 快速排序

  快速排序被认为是最好的排序算法之一。快速排序经过一轮比较,将比某一个数小的数全都至于这个数前面,把比这个数大的数放在都放在它的后面。这样就使得这个数位于了它的正确位置,之后的排序工作都不会再影响到它;同时它也把整列数分成了两部分,再分别对剩下的两部分做相同的操作...直到所有数位于它的正确位置,即完成了排序。

C++代码:

 1 template<class temp>
 2 void QuickSort(temp * array, int begin, int end)
 3 {
 4     if (end - begin == 1)    //如果只有两个元素,直接交换即排好序
 5         if (array[begin] > array[end])
 6             swap(array[begin], array[end]);
 7 
 8     if (end - begin > 1)
 9     {
10         temp x = array[begin];
11         int left = begin;
12         int right = end;
13         while (right > left)
14         {
15             while (right > left && array[right] >= x )    //从右向左找出比x小的数
16                 right--;
17             if (right > left)
18             {
19                 array[left] = array[right];
20                 left++;
21             }
22 
23             while (right > left && array[left] < x)        //从左向右找出比x大或等于x的数
24                 left++;
25             if (right > left)
26             {
27                 array[right] = array[left];
28                 right--;
29             }
30         }
31         array[right] = x;
32         QuickSort(array, begin, right - 1);
33         QuickSort(array, right + 1, end);
34     }
35 }

 

 

2.6 堆排序

  堆排序也是一种时间复杂度较低的排序算法。它通过建立一棵完全二叉树,使数组中每个元素对应其中一个节点;再将这课完全二叉树建成堆:使其中每个根节点都大于(大顶堆)或小于(小顶堆)它的左右孩子节点,这样堆顶元素就是所有节点中最大(大顶堆)或最小的(小顶堆)。通过不断取出堆顶元素,并将剩余元素重新调整成堆,即可完成排序。

C++代码:

 1 //堆排序
 2 template <typename T>
 3 void HeapSort(T*array, int size)
 4 {
 5     for (int i = size - 1; i >= 0; i--)        //建大顶堆
 6         MaxHeapify(array, size, i);
 7 
 8     while (size > 1)    //每次取堆顶元素(最大值),再将剩余元素重新调整成大顶堆,再取剩余元素的堆顶元素(最大值)
 9     {
10         swap(array[0], array[size - 1]);
11         size--;
12         if (size > 1)
13             MaxHeapify(array, size, 0);
14     }
15 
16     return;
17 }
18 
19 //调整以element元素为根节点的树
20 //使其成为大顶堆
21 template <typename T>
22 void MaxHeapify(T*array, int size, int element)        //element为当前调整的树的根节点
23 {
24     int lchild = element * 2 + 1;
25     int rchild = lchild + 1;
26     while (rchild < size)        //element既有左子树又有右子树
27     {
28         if (array[element] >= array[lchild] && array[element] >= array[rchild])        //根节点最大,已经完成此次调整,直接返回
29             return;
30 
31         if (array[lchild] >= array[rchild])        //左孩子最大
32         {
33             swap(array[lchild], array[element]);
34             element = lchild;        //经过调整后,左子树可能不再符合大顶堆,所以循环调整左子树
35         }
36         else
37         {
38             swap(array[rchild], array[element]);
39             element = rchild;        //经过调整后,右子树可能不再符合大顶堆,所以循环调整右子树
40         }
41 
42         lchild = element * 2 + 1;    //重置左右子树
43         rchild = lchild + 1;
44     }
45 
46     if (lchild == size - 1 && array[lchild] > array[element])    //只有左子树且左子树大于根节点
47         swap(array[lchild], array[element]);
48 
49     return;
50 }

堆排序的关键在于将二叉树调整为堆的函数。

 

 

2.7 希尔排序

  希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。(来自百度百科:希尔排序

  也就是说,希尔排序是插入排序的改进,它通过先将整体分组,然后对分组进行排序;再不断减少分组的个数,直至分组个数为1(也就是整体进行排序),排序完成。

C++代码1:

 1 template <typename T>
 2 void ShellSort(T* array, int length)
 3 {
 4     for (int gap = length / 2; gap >= 1; gap /= 2)        //不同的增量
 5         for (int i = 0; i < gap; i++)
 6             for (int j = i; j < length; j += gap)
 7                 if (j + gap < length && array[j] > array[j + gap])
 8                 {
 9                     int temp = array[j + gap];
10                     int k = j;
11                     while (array[k] > temp && k >= 0)
12                     {
13                         array[k + gap] = array[k];
14                         k -= gap;
15                     }
16                     array[k + gap] = temp;
17                 }
18 }

更简洁的实现,C++代码2:

1 template <typename T>
2 void ShellSort(T* array, int length)
3 {
4     for (int gap = length / 2; gap >= 1; gap /= 2)
5         for (int i = 0; i < gap; i++)
6             for (int j = i; j < length - gap; j += gap)
7                 for (int k = j; array[k] > array[k + gap] && k >= 0; k -= gap)
8                     swap(array[k], array[k + gap]);
9 }

 

 

2.8 基数排序

  基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。(来自百度百科:基数排序

  基数排序的思想在日常生活中很常见,比如两数比大小时,先比较最高位、再比较次高位...基数排序则是先对个位排序、再对十位排序、...、直至最高位(或者从最高位开始到最低位)。

C++代码:

 

 1 template <typename T>
 2 void RadixSort(T* array, int length)
 3 {
 4     int digits_of_max_number = Digits_of_max_number(array, length);        //最大项的位数,也就是排序循环的次数
 5     T* temp = new T[length];        //临时储存数组
 6     int count[10];        
 7     int index = 1;        //先对个位进行排序
 8     for (int i = 0; i < digits_of_max_number; i++)        //循环digits_of_max_number次,i=0代表个位
 9     {
10 
11         for (int j = 0; j < 10; j++)        //每次循环前清零
12             count[j] = 0;
13         for (int j = 0; j < length; j++)    //计算从右往左第i位(i=0代表个位)中k出现的次数count[k]
14         {
15             int k = (array[j] / index) % 10;
16             count[k]++;
17         }
18         
19         for (int j = 1; j < 10; j++)        //通过上一个循环得到的count[k],计算“从右往左第i位(i=0代表个位)为k的数”本次循环后在新数组中应处位置的下标
20             count[j] = count[j - 1] + count[j];
21 
22         for (int j = length - 1; j >= 0; j--)    //将array[j]通过count[k]调整到新位置,暂存到数组temp中
23         {
24             int k = (array[j] / index) % 10;
25             temp[count[k] - 1] = array[j];
26             count[k]--;
27         }
28         for (int j = 0; j < length; j++)    //将temp中的数复制回array
29             array[j] = temp[j];
30 
31         index *= 10;    //对下一位进行排序
32     }
33     
34     return;
35 }
36 
37 template <typename T>
38 int Digits_of_max_number(T* array, int length)
39 {
40     int d = 1, index = 10;
41     for (int i = 0; i < length; i++)
42         while (array[i] >= index)
43         {
44             d++;
45             index *= 10;
46         }
47     return d;
48 }

 

posted @ 2015-08-01 10:11  永哼哼  阅读(995)  评论(0编辑  收藏  举报