常用的排序算法C++

来源:互联网 发布:网络购彩何时恢复 编辑:程序博客网 时间:2024/06/07 00:48

排序算法:

  • 冒泡排序
  • 堆排序
  • 归并排序
  • 快速排序
  • 希尔排序
  • 直接插入排序
  • 桶排序
  • 基数排序

冒泡排序:

有n个数,先把相邻的两个数进行比较,大的数往后挪,大的数继续和后面相邻的数比较,重复n-1次,此时,最大的数在序列的尾部,不加入第二轮排序,故第二轮比较n-2次,以此类推。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,就不需要交换,因此冒泡排序是稳定排序算法。

冒泡排序最好的情况为一次循环,则复杂度为O(n),最坏的情况需要进行所有循环,即O(n^2)。

/* * 冒泡排序法:O(n^2) */#ifndef __BUBBLE_SORT_H__#define __BUBBLE_SORT_H__#include "swap.h"/** 有n个数,循环i = n-1次,a[j]和a[j+1]比,如果大于则交换两个数,j++直到j < (n-i-1)退出进入下个循环。*/template <class ElemType>void BubbleSort(ElemType e[], int n) {for (int i = 0; i < n - 1; i++) {for (int j = 0; j < (n - i - 1); j++) {if (e[j] > e[j + 1])Swap(e[j], e[j + 1]);}}}#endif // !__BUBBLE_SORT_H__

堆排序:

对n个数,先生成最大堆(或最小堆),然后把最大数放到尾部,对前面n-1个数继续进行最大堆排序,最大堆排序:从最后一个父节点开始,左右子节点做比较,大的节点与当前节点再做比较,当前节点比大的子节点小,则交换位置,继续比较倒数第二个父节点直到完成。如此循环直到完成排序。

平均时间复杂度:O(N*logN)。堆排序是不稳定的排序。

以 2, 15, 45, 76, 32, 6, 23 为例:


swap.h:

#ifndef __SWAP_H__#define __SWAP_H__template <class ElemType>void Swap(ElemType &e1, ElemType &e2) {ElemType e;e = e1;e1 = e2;e2 = e;}#endif

heap_sort.h:
/** 堆排序法:O(N*logN)*/#ifndef __HEAP_SORT_H___#define __HEAP_SORT_H___#include "swap.h"/* 1.先对整个数组进行堆排序(排序过程中先左子节点和右子节点比较,大的那个和当前节点交换) * 2.将排序好的最大堆的最大值与末端值调换,总共生成最大堆n-2次 */template <class ElemType>void HeapSort(ElemType elem[], int n) {int i;// 第一次需要对整个数组进行堆排序(从非子叶节点(n-2)/2开始向上移动)for (i = (n - 2) / 2; i >= 0; i--) {ComposeBigestHeap(elem, i, n - 1);}// 需要进行n-2次生成最大堆for (i = n - 1; i > 0; i--) {Swap(elem[0], elem[i]);// 之后的每次最大堆只要对elem[0]进行最大堆生成即可(因为除了elem[0]其余均已排好序)// 排序范围每次均要减去最下端已拍好的数ComposeBigestHeap(elem, 0, i - 1);}}template <class ElemType>void ComposeBigestHeap(ElemType elem[], int low, int high) {int cur, i;// cur为当前要生成最大堆的节点,i=2*i+1遍历向下的左子节点for (cur = low, i = 2 * low + 1; i <= high; i = 2 * i + 1) {// elem[i]为左子节点,elem[i+1]为右子节点,右子节点大的话,则指向右子节点继续向下if (i < high && elem[i] < elem[i + 1]) {i++;}// 如果当前节点值大于最大子节点,则不作后面操作if (elem[cur] >= elem[i]) {break;}Swap(elem[cur], elem[i]);cur = i;// 指向交换后的子节点}}#endif // !__HEAP_SORT_H___

归并排序:

将需要作比较的数列分成两份,再对分割出来的两份进行再分割,直到无法分割为止,接着在进行逐个比较合并。时间复杂度:O(n*logn) ,属于稳定的排序。

例子:


/** 归并排序法:O(n*log(n))*/#ifndef __MERGE_SORT_H___#define __MERGE_SORT_H___/* 归并主要关键在于先拆分,再合并 */template <class ElemType>void MergeSort(ElemType elem[], int n) {ElemType *telem = new ElemType[n];// 新建一个临时数组SperateTwoPart(elem, telem, 0, n - 1);delete []telem;}// 将数组分成无数份,直到不可分后,比较并合并template <class ElemType>void SperateTwoPart(ElemType elem[], ElemType telem[], int low, int high) {if (low < high) {int mid = (low + high) / 2;SperateTwoPart(elem, telem, low, mid);SperateTwoPart(elem, telem, mid + 1, high);Merge(elem, telem, low, mid, high);}}template <class ElemType>void Merge(ElemType elem[], ElemType telem[], int low, int mid, int high) {int i, j, k;for (i = low, j = mid + 1, k = low; i <= mid&&j <= high; k++) {// 比较,小的数值存储到临时数组中if (elem[i] > elem[j]) {telem[k] = elem[j];j++;}else {telem[k] = elem[i];i++;}}// 将归并后数组中剩余的数存储到临时数组中for (; i <= mid; i++, k++) {telem[k] = elem[i];}for (; j <= high; j++, k++) {telem[k] = elem[j];}// 将临时数组存回到原始数组for (i = low; i <= high; i++) {elem[i] = telem[i];}}#endif // !__MERGE_SORT_H___

快速排序:

以第一个数为基数key,所有数和key,大的放到右边,小的放到左边,再以key为中心,分成两部分。继续选择第一个数为key,按照前面的方法,直到排序完成。时间复杂度:n*log(n),属于不稳定的排序。


<pre name="code" class="cpp">/** 快速排序法:O(N*logN)*/#ifndef __QUICK_SORT_H__#define __QUICK_SORT_H__#include "swap.h"/* 快速排序,在数组取一个基准数,把大于基准数的数放置在右侧,小于在左侧。 * 再把基准数分隔开的两个部分继续取基准数分割。 */template <class ElemType>void QuickSort(ElemType elem[], int n) {SperateQuickPart(elem, 0, n - 1);}template <class ElemType>void SperateQuickPart(ElemType elem[], int low, int high) {if (low >= high)return;// pos为序列中的基准数int pos = Partition(elem, low, high);// 以基准数为中心,左右分开SperateQuickPart(elem, low, pos - 1);SperateQuickPart(elem, pos + 1, high);}template <class ElemType>int Partition(ElemType elem[], int low, int high) {int key = elem[low];while (low < high) {// 当左边的数小于右边,high--while (low < high && elem[high] >= key) {--high;}elem[low] = elem[high];while (low < high && elem[low] <= key) {++low;}elem[high] = elem[low];}elem[low] = key;// 返回基准数return low;}#endif // !__QUICK_SORT_H__

希尔排序:

属于稳定的排序。

(引用自百度百科):

不需要大量的辅助空间,和归并排序一样容易实现。希尔排序是基于插入排序的一种算法, 在此算法基础之上增加了一个新的特性,提高了效率。希尔排序的时间复杂度与增量序列的选取有关,例如希尔增量时间复杂度为O(n²),而Hibbard增量的希尔排序的时间复杂度为O(

  
),希尔排序时间复杂度的下界是n*log2n。希尔排序没有快速排序算法快 O(n(logn)),因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择。但是比O(
  
)复杂度的算法快得多。并且希尔排序非常容易实现,算法代码短而简单。 此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。专家们提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法. 本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。 原因是,当n值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。当n值减小时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键词的比较次数和对象移动次数。想要弄清关键词比较次数和记录移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题。

/** 希尔插入排序法:最好O(n),最坏O(n^2)*/#ifndef __SHELL_SORT_H__#define __SHELL_SORT_H__/* 推理过程 * 6 5 4 3 2 1n=6 * 3 6 *   2     5 *     1     4 * 3 2 1 6 5 4n/2=3 * 2 3 *   1 3 *     3 6 * 5 6 *   4 6 * 2 1 3 5 4 6n/2=1 * 1 2 *   2 3 *     3 5 * 4 5 *   5 6 * 1 2 3 4 5 6n/2=0 */template <class ElemType>void ShellSort(ElemType elem[], int n) {for (int k = n / 2; k > 0; k /= 2){// 取中间数n/2for (int i = k; i < n; i++) {// i=n/2 且i++ElemType e = elem[i];int j = i;for (; j >= k && e < elem[j - k]; j -= k) {// elem[i]和elem[i-k]比较elem[j] = elem[j - k];}elem[j] = e;}}}#endif // !__SHELL_SORT_H__

直接插入排序:

每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。属于稳定的排序。

/** 直接插入排序法:最好O(n),最坏O(n^2)*/#ifndef __STRAIGHT_INSERT_SORT_H__#define __STRAIGHT_INSERT_SORT_H__/** 有n个数,循环n-1次,从第二个数a[i](i=1)开始,和它前面一个数比,如果小于前面的数则交换两个数,不小于退出进入下个循环,* 下个循环从a[i+1]开始,和它前面一个数a[i]比,如果小于前面的数交换并且继续和a[i](i=i-1)比,直到不小于退出进入下个循环.*/template <class ElemType>void StraightInsertSort(ElemType elem[], int n) {for (int i = 1; i < n; i++) {ElemType e = elem[i];int j;for (j = i - 1; j >= 0 && e < elem[j]; j--) {elem[j + 1] = elem[j];// e前面的数后移}elem[j + 1] = e;//j+1=i即elem[j + 1]=elem[i]}}#endif // !__STRAIGHT_INSERT_SORT_H__


节点和链表类,为用链表排序做准备。

node.h:

<span style="font-size:18px;">#ifndef __NODE_H__#define __NODE_H__// 结点类template <class ElemType>struct Node {// 数据成员:ElemType data;// 数据域Node<ElemType> *next;// 指针域// 构造函数:Node();// 无参数的构造函数Node(ElemType item, Node<ElemType> *link = NULL);// 已知数数据元素值和指针建立结构};// 结点类的实现部分template<class ElemType>Node<ElemType>::Node()// 操作结果:构造指针域为空的结点{   next = NULL;}template<class ElemType>Node<ElemType>::Node(ElemType item, Node<ElemType> *link)// 操作结果:构造一个数据域为item和指针域为link的结点{   data = item;   next = link;}#endif</span>

lk_list.h:
<span style="font-size:18px;">#ifndef __LK_LIST_H__#define __LK_LIST_H__#include "node.h"template <class ElemType>class LinkList{protected:Node<ElemType> *head;// 头结点指针mutable int cpos;// 当前位置的序号mutable Node<ElemType> * curPtr;// 指向当前位置的指针int count;// 元素个数Node<ElemType> *GetElemPtr(int position) const;// 返回指向第position个结点的指针void Init();// 初始化线性表public:LinkList();virtual ~LinkList();int Length() const;    // 求线性表长度bool Empty() const;// 判断线性表是否为空void Clear();// 将线性表清空void Traverse() const;// 遍历线性表int GetCurPosition() const;// 返回当前位置bool GetElem(int position, ElemType &e) const;// 求指定位置的元素bool SetElem(int position, const ElemType &e);// 设置指定位置的元素值bool Delete(int position, ElemType &e);// 删除元素bool Insert(int position, const ElemType &e);// 插入元素LinkList(const LinkList<ElemType> &copy); // 复制构造函数模板LinkList<ElemType> &operator =(const LinkList<ElemType> &copy); // 重载赋值运算符};template <class ElemType>Node<ElemType> *LinkList<ElemType>::GetElemPtr(int position) const {if (cpos > position) {cpos = 0;curPtr = head;}for (; cpos < position; cpos++) {curPtr = curPtr->next;}return curPtr;}template <class ElemType>void LinkList<ElemType>::Init() {head = new Node<ElemType>;curPtr = head;cpos = 0;count = 0;}template <class ElemType>LinkList<ElemType>::LinkList(){Init();}template <class ElemType>LinkList<ElemType>::~LinkList(){delete head;}template <class ElemType>int LinkList<ElemType>::Length() const// 操作结果:返回线性表元素个数{return count;}template <class ElemType>bool LinkList<ElemType>::Empty() const {return head->next == NULL;}template <class ElemType>void LinkList<ElemType>::Clear() {ElemType e;while (Length() > 0){Delete(1, e);}}template <class ElemType>void LinkList<ElemType>::Traverse() const {for (Node<ElemType> *n = head->next; n != NULL; n = n->next) {cout << n->data << " ";}cout << endl;}template <class ElemType>int LinkList<ElemType>::GetCurPosition() const {return cpos;}template <class ElemType>bool LinkList<ElemType>::GetElem(int position, ElemType &e) const {if (position < 1 || position > Length()){// position范围错return false;// 元素不存在}else {Node<ElemType> *n;n = GetElemPtr(position);e = n->data;return true;}}template <class ElemType>bool LinkList<ElemType>::SetElem(int position, const ElemType &e) {if (position < 1 || position > Length()){// position范围错return false;// 元素不存在}else {Node<ElemType> *n;n = GetElemPtr(position);n->data = e;return true;}}template <class ElemType>bool LinkList<ElemType>::Delete(int position, ElemType &e) {if (position < 1 || position > Length()){// position范围错return false;// 元素不存在}else {Node<ElemType> *n;n = GetElemPtr(position - 1);Node<ElemType> *nn = n->next;// nn为n的下一个后继节点n->next = nn->next;e = nn->data;if (position == Length()) {cpos = 0;curPtr = head;}else {cpos = position;curPtr = n->next;}count--;delete nn;return true;}}template <class ElemType>bool LinkList<ElemType>::Insert(int position, const ElemType &e) {if (position < 1 || position > Length() + 1){// position范围错return false;// 元素不存在}else {Node<ElemType> *n;n = GetElemPtr(position - 1);Node<ElemType> *newn;newn = new Node<ElemType>(e, n->next);n->next = newn;cpos = position;curPtr = newn;count++;return true;}}template <class ElemType>LinkList<ElemType>::LinkList(const LinkList<ElemType> &copy) {int copylen = copy.Length();ElemType e;Init();for (int cpos = 1; cpos <= copylen; cpos++) {copy.GetElem(cpos, e);Insert(Length() + 1, e);}cout << "调用拷贝构造函数" << endl;}template <class ElemType>LinkList<ElemType> &LinkList<ElemType>::operator = (const LinkList<ElemType> &copy) {if (&copy != this) {int copylen = copy.Length();ElemType e;Clear();for (int cpos = 1; cpos <= copylen; cpos++) {copy.GetElem(cpos, e);Insert(Length() + 1, e);}}cout << "调用赋值函数" << endl;return *this;}#endif // !__LK_LIST_H__</span>


桶排序:

将需要排序的数放到有限个桶,然后按照桶排好的顺序收集数据,如若桶中的数据 > 1,则用其他优秀的算法进行排序(如快速排序等)。如果桶的数量恰好等于数的数量,则效率最好,为O(n),当然我们不可能这么做,因为对于数量庞大排序,消耗空间太大。假设n个数,有m个桶,则平均复杂度:O(n)+O( m*(n/m)*log(n/m) ) = O( n+n*(log n - log m) ),排序期间相等的数不会改变顺序,依次属于稳定排序。


<pre name="code" class="cpp">/** 桶排序法:平均:O(N+N*logN-N*logM),最好:O(N)*/#ifndef __BUCKET_SORT_H__#define __BUCKET_SORT_H__#include "lk_list.h"// n为数的个数,m为桶的个数template <class ElemType> void BucketSort(ElemType elem[], int n, int m){LinkList<ElemType> *list;// 用于存储被分配的线性表数组list = new LinkList<ElemType>[10];int num = Distribute(elem, n, m, list);// 分配SortList(list, n, num);// 对分配好的list逐一进行排序,这里可以选择先进的排序方法提高效率Colect(elem, n, list);// 收集delete[]list;}template <class ElemType>int Distribute(ElemType elem[], int n, int m, LinkList<ElemType> list[]){int num = 0;for (int i = 0; i < n; i++){// 进行第i起分配int index = elem[i] / m;// 将list视为桶,符合条件的数放入相应的桶中list[index].Insert(list[index].Length() + 1, elem[i]);if (num < index)num = index;}// 返回桶个数,进而对各个桶内的数据进行排序return num;}template <class ElemType>void SortList(LinkList<ElemType> *list, int n, int num){for (int i = 0; i < num; i++){// 对各个桶内的数据进行排序,这里可替换为高效的排序算法QuickSort(list[i], n);}}template <class ElemType>void Colect(ElemType elem[], int n, LinkList<ElemType> list[]){for (int k = 0, j = 0; j < 10; j++){// 进行第i起分配ElemType tmpElem;// 逐个桶按顺序收集数据while (!list[j].Empty()){// 收集list[j]list[j].Delete(1, tmpElem);elem[k++] = tmpElem;}}}#endif // !__BUCKET_SORT_H__


基数排序:

排序时,假设我们排序的数的范围在0~999之间,则关键码digits分为个位、十位、百位,共3个,基数r(关键字的取值范围)为0~9(10位)。

假设有n=7个数,为 

33,2,65,312,98,9,120

根据基数分出10个桶,依照个位分别放入桶内

01 2 3 4 56 7 8 9

1202 33 65 98 9

312

排序后:           120,   2,    312,   33,   65,    98,    9

接着按照十位:2, 9, 312, 120, 33, 65, 98

百位:               2, 9, 33, 65, 98, 120, 312

时间复杂度:O( d*(r+n) ),其中d是关键码位数,r为基数,n为数的个数。且基数排序为稳定的排序。

radix_sort.h:

/* * 基数排序法:O(n^2) */#ifndef __RADIX_SORT_H__#define __RADIX_SORT_H__#include "lk_list.h"template <class ElemType>void RadixSort(ElemType elem[], int n, int r){int digits;// d为关键字位数digits = NumOfDigits(elem, n);// 判断数组中的最高位并返回位数LinkList<ElemType> *list;// 用于存储被分配的线性表数组list = new LinkList<ElemType>[r];for (int i = 1; i <= digits; i++){// 第i趟分配与收集Distribute(elem, n, r, i, list);// 分配Colect(elem, n, r, list);// 收集}delete []list;}template <class ElemType>void Distribute(ElemType elem[], int n, int r, int i, LinkList<ElemType> list[])// 初始条件: r为基数,list[0 .. r - 1]为被分配的线性表数组// 操作结果: 进行第i趟分配{for (int power = (int)pow((double)r, i - 1), j = 0; j < n; j++){// 进行第i起分配int index = (elem[j] / power) % r;list[index].Insert(list[index].Length() + 1, elem[j]);}}template <class ElemType>void Colect(ElemType elem[], int n, int r, LinkList<ElemType> list[])// 初始条件: r为基数,list[0 .. r - 1]为被分配的线性表数组// 操作结果: 进行第i趟收集{for (int k = 0, j = 0; j < r; j++){// 进行第i起分配ElemType tmpElem;while (!list[j].Empty()){// 收集list[j]list[j].Delete(1, tmpElem);elem[k++] = tmpElem;}}}template <class ElemType>int NumOfDigits(ElemType elem[], int n){int largest = 0;for (int i = 0; i < n; i++) {if (elem[i] > largest)largest = elem[i];}int digits = 0;//digits为最大值的位数while (largest){digits++;largest /= 10;}return digits;}#endif

源代码:http://download.csdn.net/detail/u013707014/9032507


1 0