排序算法:
- 冒泡排序
- 堆排序
- 归并排序
- 快速排序
- 希尔排序
- 直接插入排序
- 桶排序
- 基数排序
冒泡排序:
有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(
/*
* 希尔插入排序法:最好O(n),最坏O(n^2)
*/
#ifndef __SHELL_SORT_H__
#define __SHELL_SORT_H__
/* 推理过程
* 6 5 4 3 2 1 n=6
* 3 6
* 2 5
* 1 4
* 3 2 1 6 5 4 n/2=3
* 2 3
* 1 3
* 3 6
* 5 6
* 4 6
* 2 1 3 5 4 6 n/2=1
* 1 2
* 2 3
* 3 5
* 4 5
* 5 6
* 1 2 3 4 5 6 n/2=0
*/
template <class ElemType>
void ShellSort(ElemType elem[], int n) {
for (int k = n / 2; k > 0; k /= 2){ // 取中间数n/2
for (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__
直接插入排序:
/*
* 直接插入排序法:最好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> ©); // 复制构造函数模板
LinkList<ElemType> &operator =(const LinkList<ElemType> ©); // 重载赋值运算符
};
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> ©) {
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> ©) {
if (© != 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个桶,依照个位分别放入桶内
0 1 2 3 4 5 6 7 8 9
120 2 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