• 【排序算法】用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 }
  • 相关阅读:
    把数据库转化成数据库脚本
    营养瘦身第一菜——金陵素什锦 健康程序员,至尚生活!
    十类好吃不胖的食物 健康程序员,至尚生活!
    一周带饭实录(附作菜菜谱) 健康程序员,至尚生活!
    日常五大习惯有助减肥 健康程序员,至尚生活!
    【暴强】200种好口碑便宜护肤品 健康程序员,至尚生活!
    腹式肠道操 缩胃瘦身有奇效 健康程序员,至尚生活!
    一天4时段喝水轻松瘦身 健康程序员,至尚生活!
    10种不要钱的护肤法则 健康程序员,至尚生活!
    看了这篇你肯定瘦 全身上下想瘦哪就瘦哪 健康程序员,至尚生活!
  • 原文地址:https://www.cnblogs.com/yongheng20/p/4693701.html
Copyright © 2020-2023  润新知