您的位置:首页 > 编程语言 > C语言/C++

十大排序算法代码总览(C++)

2018-03-30 17:14 169 查看


注:

算法稳定性

定义:待排序序列中的相同关键字记录经过排序后相对次序保持不变。比如Xm=xn,原本Xm在Xn之前,排序后依然Xm在Xn之前。

条件:算法的稳定性由具体算法决定,且在一定条件下算法稳定性也会发生转变。

稳定性意义:只有在排序一个复杂对象的多个数字属性且初始顺序存在意义时,才需要注意算法的稳定性,即需要尽量保持原有排序序列的意义。比如原本商品按价格高低排序,想着想要按销量排序,且使得同销量的商品依然保持价格高低的排序,这是可以使用稳定性算法。

1冒泡排序(Bubble Sort)

简述:

重复进行相邻数组元素的两两比较,并按规则进行交换,直到没有元素再需要交换。最终使得大的元素逐渐沉到数列底部,相较小的元素浮现到数列前端。

算法描述:

1 比较相邻两个元素,如果第一个比第二个大,就交换位置。

2 从第一对开始,对数组中的每一对相邻的两个元素重复步骤1,使得最大的元素沉到数组底部。

3 重复步骤2,除了底部已经排序好的元素。(每一趟都会多一个以排序好的元素)

4 重复以上步骤直到排序完成。

算法分析:

最好:T(n)=O(n);数据全部正序

最差:T(n)=O(n^2);数据全部反序

平均:T(n)=O(n^2);

代码实现

#include<iostream>
using namespace std;
void swap(int &a, int &b) {//交换函数
int tmp = a;
a = b;
b = tmp;
}
void bubbleSort(int a[],int len) {
for (int i = 0; i < len; ++i) {//排列len趟完成排序
for (int j = 0; j < len - i - 1; ++j) {//每趟排序的元素都从第一个元素开始到尾部没有排序好的元素。第一趟为第一个元素到最后一个元素,排出了最大元素。第二趟为第一个元素到倒数第二个元素(排除以排序好的元素),排出第二大的元素。
if (a[j] > a[j + 1]) {
swap(a[j], a[j + 1]);
}
}
}
}
int main() {
int a[] = { 520,0,1,9,56,100,1,85,5,3,6 };
int len = sizeof(a) / sizeof(a[0]);
bubbleSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}
//运行结果:
0 1 1 3 5 6 9 56 85 100 520


算法改进

1**增加了反向冒泡。传统冒泡排序每一趟排序一次,找出一个最大值。改进后每一趟排序两次,正向冒泡,找出最大的元素;反向冒泡,找出最小元素,使得一次可以得到两个最终值。从而减少排序趟数。**

2**增加了标志点flag**。目的在于记录每一趟最后一次交换的元素位置,即表示标志点之后或者之前的位置已经排好,后续无需再排,缩小排序区间,减少排序次数。

#include<iostream>
using namespace std;
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
void bubbleSort(int a[],int len) {
int low = 0, high = len - 1;
while (low < high) {//排序趟数。排序区间[low,high]。
int flag = 0;//标志点
for (int i = low; i < high; ++i) {//第一次正向冒泡
if (a[i] > a[i + 1]) {
swap(a[i], a[i + 1]);
flag = i;
}
}
high = flag;//表示标志点之后的元素已经排好,区间右值缩小。
for (int i = high; i > low; --i) {//第2次反向冒泡
if (a[i] < a[i - 1]) {
swap(a[i], a[i - 1]);
flag = i;
}
}
low = flag;//表示断点之前的元素已经排好,区间左值缩小。
}
}
int main() {
int a[] = { 520,0,1,9,56,100,1,85,5,3,6 };
int len = sizeof(a) / sizeof(a[0]);
bubbleSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}
//运行结果:
0 1 1 3 5 6 9 56 85 100 520


2选择排序(Select Sort)

简述

将数组分为已排序和待排序两个区间,即有序区和无序区。每次从无序区中选出最小/最大的元素与无序区的第一个元素(即有序区尾部)交换位置,直到无序区只剩一个元素,排序完成。

算法描述

1 将数组划分为有序区和无序区。(开始有序区为空,无序区[0,n-1])

2 在无序区中找出最大的那个元素,与该区间第一个元素交换。(有序区元素个数加1,无序区减1)

3 重复步骤2 n-1次,排序完成。(排序趟数为n-1,最后一个元素无需排序)

算法分析

时间复杂度最稳定,无论什么时候都为:T(n)=0(n^2)

代码实例

#include<iostream>
using namespace std;
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
void selectSort(int a[], int len) {
for (int i = 0; i < len - 1; ++i) {
int minIndex = i;
for (int j = i; j < len; ++j) {//找出无序区最小元素的下标
if (a[j] < a[minIndex]) {
minIndex = j;
}
}
swap(a[i], a[minIndex]);//无序区第一个元素与最小值交换。
}
}

int main() {
int a[] = { 5,89,562,4,2,0,56512,4512,5 };
int len = sizeof(a) / sizeof(a[0]);
selectSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}
//运行结果:
0 2 4 5 5 89 562 4512 56512


3插入排序(Insert Sort)

简述

插入排序跟选择排序很像,都分为有序区和无序区。但是选择排序是每次都从无序区中选出最小元素插入到有序区末尾,而插入排序是直接将数组的第一个元素作为有序区的第一个元素,每次都拿出无序区第个一元素插入到有序区合适的位置上,直到无序区为空,排序完成。

算法描述

1 将数组分为有序区和无序区,有序区0,无序区[1,n-1];

2 取下无序区第一个元素,保存其值。

3有序区中元素从后往前与新元素比较,如果新元素更小,旧元素往后移。

3 重复步骤3,直到新元素大于或等于旧元素,将新元素插入该元素之后。

4 重复步骤234, n-1次,排序完成。

算法分析

最好:T(n)=o(n),数组元素正序排列

最坏:T(n)=o(n^2)数组元素反序排列

平均:T(n)=o(n^2)

代码实例

#include<iostream>
using namespace std;
void sertSort(int a[], int len) {
for (int i = 1; i < len; ++i) {
int key = a[i];//保存无序区第一个元素为key
int j = i - 1;
while (!(j <0) && a[j] > key) {//新元素在有序区寻找位置
a[j + 1] = a[j];
j--;
}
a[j+1] = key;
}
}

int main() {
int a[] = {5,45,1,3,0,99,2,10 };
int len = sizeof(a) / sizeof(a[0]);
sertSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}
//运行结果:
0 1 2 3 5 10 45 99


算法改进

新元素在插入到有序区时,使用二分法查找位置而非一个一个依次查找。

#include<iostream>
using namespace std;
void sertSort(int a[], int len) {
for (int i = 1; i < len; ++i) {
int key = a[i];
int left = 0, right = i-1;
while (!(left > right)) {//在区间内查找位置
int middle = (left + right) / 2;
if (a[middle] > key)
right = middle-1;
else left = middle+1;
}
for (int j = i - 1; !(j<left); --j) {
a[j+1] = a[j ];
}
a[left] = key;//left为新元素要插入的位置。
}
}

int main() {
int a[] = {5,45,1,3,0,99,2,10 };
int len = sizeof(a) / sizeof(a[0]);
sertSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}
//运行结果:
0 1 2 3 5 10 45 99


4希尔排序(Shell Sort)

简述

希尔排序是以某个增量h为步长跳跃分组进行插入排序,由于增量是一个从h逐渐缩小至1的过程,所以又称缩小增量排序。

其核心在于间隔序列设定,即增量的设定,这也是也插入排序的本质区别。插入排序始终增量为1。

最佳增量:k趟排序增量步长为(2^k)-1,即增量序列(2^k)-1,…15,7,3,1

算法描述

1确定增量序列(t1,t2…tk)ti>tj,tk=1;

2按增量序列个数k分成k趟排序

3每趟排序按对应增量ti,将序列分割成若干子序列,分别进行直接插入排序。

简述2

希尔排序实际上是将一维数组分成具有不同列数的二维数组(对应方式,a[i]->a[i/h][i%h])。在每一趟排序中对每一列进行插入排序。列宽也就是增量,增量减小也就是列数减小。

以排序数组a[]={10,8,4,3,1,5,7,9,2,6},增量分别为5,2,1为例:



代码实例

#include<iostream>
using namespace std;
void swap(int&a, int&b) {
int tmp = a;
a = b;
b = tmp;
}
void shellSort(int a[], int len) {
int gap = len ;
while (gap = gap / 2) {//增量
cout << "每列待排序元素:"<<endl;
for (int i = gap; i < len; i++) {
cout << i << " ";
int key = a[i];//待排序元素
int j = i - gap;
for (; j+1>0&&a[j ] > key; j -= gap) {//插入排序
a[j + gap] = a[j];
}
a[j + gap] = key;
}cout << endl;
cout << "增量" << gap << "的排序结果:" << endl;
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}cout <<endl;
}
}

int main() {
int a[] = { 10,8,4,3,1,5,7,9,2,6};
int len = sizeof(a) / sizeof(a[0]);
shellSort(a, len);
cout << "排序最终结果:";
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


5归并排序(Merge Sort)

简述

采用递归和分治的思想,首先将数组二分成多个子序列,然后两两子序列进行比较与合并,使其合并为一个完全有序的序列。不断进行比较与合并,使子序列们最终合并为一个完整有序的序列。

算法描述

1将数组二分为多个子序列

2子序列进行排序(注:只有1个元素的序列本身就已经是排序好的序列)

3排序好的子序列间进行比较与合并。

3子序列完全合并为一个有序序列

算法分析

最佳:o(n)

最坏:o(nlogn)

平均:o(nlogn)

#include<iostream>
using namespace std;

void merge(int a1[], int na1, int a2[], int na2) {
int tmp[1000];
int t = 0, i = 0<
10568
/span>, j = 0,k=0;
while( i < na1&&j < na2){
if (a1[i] > a2[j]) {
tmp[t++] = a2[j++];
}
else tmp[t++] = a1[i++];
}
while (i < na1) {
tmp[t++] = a1[i++];
}
while (j < na2) {
tmp[t++] = a2[j++];
}
while(k<t){
a1[k] = tmp[k++];
}
}
void mergeSort(int a[],int len) {
if (len>1) {
int mid = len / 2;
int *a1 = a;
int na1 = mid;
int *a2 = a + mid ;
int na2 = len - mid;
mergeSort(a1, na1);
mergeSort(a2, na2);
merge(a1, na1, a2, na2);
}
}

int main() {
int a[] = {0,7,5,2,1,3,8,4,6,9 };
int len = sizeof(a) / sizeof(a[0]);
mergeSort(a,len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


6快速排序(Quick Sort)

简述

分治和递归的应用。选取数组中一个元素为基准(pivot)P,对数组进行排序,使得比P大的元素都在P的右边,比P小的元素在P的左边。然后对以P为分界点的左右子串递归进行快排。

算法描述

1从数列中选取一个元素作为基准;

2进行分区操作(partition)。重新排列数列,使得比基准小的元素都在其左边,比基准小的元素都在它右边,即分为左子列,基准,右子列。

3对左右子列递归进行快排。

算法分析

最好:o(nlogn)

最坏:o(n^2)

平均:o(nlogn)

代码范例1

#include<iostream>
using namespace std;
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
void quickSort(int a[], int low, int high) {
if (low < high) {
int i = low - 1;
int j = low;
int key = a[high];//基准
for (int j = low; j <= high; ++j) {//使比基准小或等于基准的元素前移。
if (a[j] <=key) {
++i;
swap(a[i], a[j]);
}
}
quickSort(a, low, i - 1);
quickSort(a, i + 1, high);
}
}
int main() {
int a[] = { 0,4,1,2,3,0,0,0 };
int len = sizeof(a) / sizeof(a[0]);
quickSort(a, 0, len - 1);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


代码范例2

#include<iostream>
using namespace std;

void quickSort(int a[], int low, int high) {
if (low >= high)
return;
int i, j, key;
i = low;
j = high;
key = a[low];
while (i < j) {
while (i<j&&a[j]>=key) {
j--;
}
a[i] = a[j];
while (i < j&&a[i] <=key) {
i++;
}
a[j] = a[i];
}
a[i] = key;//基准到位
quickSort(a, low, i - 1);
quickSort(a, i + 1, high);
}
int main() {
int a[] = { 0,4,1,2,3,0,0,0 };
int len = sizeof(a) / sizeof(a[0]);
quickSort(a, 0, len - 1);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


7堆排序(Heap Sort)

简述

堆排序是将数组构建成大顶堆,即根节点是数组中最大元素,将根节点与堆底最后一个元素交换,使得最大值排到末尾,即已排序好。将剩下的n-1个元素重新调整为大顶堆,在堆顶/根节点处得到第二大的值,与堆底最后一个元素交换,便又排序好一个元素。

算法描述

1将数组构建成大顶堆

2交换堆顶元素和堆底元素

3调整堆,使其重新成为大顶堆

4重复步骤2和步骤3 n-1次,排序完成。n为数组长度。

算法分析

最好、最坏,平均都为o(nlogn)

代码示例

#include<iostream>
using namespace std;
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
void heap(int a[],int node,int len) {
int nodecopy = a[node];
for (int i = 2 * node + 1; i < len; i = 2 * i + 1) {//遍历node所有下属节点
if(i + 1 < len&&a[i + 1] > a[i])//如果右子树节点存在且右子树节点大于左子树,那么将下标移到右子树位置
i++;
if (a[i] > a[node]) {
a[node] = a[i];
node = i;
}
}
a[node] = nodecopy;//确定a[node]最终位置
}

void heapSort(int a[],int len) {
for (int node = len / 2 - 1; node >= 0; --node) {
heap(a, node, len);
}
for (int i = len-1;i>0; --i) {
swap(a[0], a[i]);
heap(a, 0, i);
}
}

int main() {
int a[] = { 7,0,1,2,8,5,9, };
int len = sizeof(a) / sizeof(a[0]);
heapSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
}


8计数排序(Count Sort)

简述

非比较排序。计数排序是利用哈希原理,记录元素出现的频次。在统计结束后可以直接遍历哈希表,将数据填回数据空间。由于是空间换时间,所以适合对数据范围集中的数据使用。而且由于用数组下标表示,只适合只有正整数,0的数据。

算法描述

1统计数组中元素出现的频次并找出极值。

2填充原数组。

算法分析

T(n)=O(n+k)

注:k为范围,即新开辟的数组大小

代码示例

#include<iostream>
using namespace std;
void countSort(int a[], int len) {
int low = a[0], high = a[0];
for (int i = 0; i < len; ++i) {//找范围
low = a[i] < low ? a[i] : low;
high = a[i] > high ? a[i] : high;
}
int range = high - low+1;
int *count = new int[range];
memset(count, 0, sizeof(int)*(range));
for(int i=0;i<len;++i){//统计频次
count[a[i]-low]++;
}
for (int i = 0,j=0; i < range; ++i) {//赋值
while (count[i]--) {
a[j++] = i+low;
}
}
}
int main() {
int a[] = { 2, 2, 3, 8, 7,0,4};
int len = sizeof(a) / sizeof(a[0]);
countSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


9基数排序(Radix Sort)

简述

非选择排序。将元素依次按低位到高位进行分类排序(个位,十位,百位…)与收集。即分别排序,分别收集。

注:适用范围,最好小于1000;数字为0或正整数。

算法描述

1取得数组中最大位数。

2从低位开始,对数组进行排序。

3将排序好的元素复制到原始数组。

4重复2、3步骤到最高位。排序完成。

算法分析

最好、最差、平均均为O(n*k)注:k为位数。

代码范例

#include<iostream>
#include<vector>
using namespace std;
int getnum(int a[], int len) {

int max = a[0];
int num = 1;
for (int i = 1; i < len; ++i) {
max=a[i] > max ? a[i] : max;
}
while (max /= 10) {
num++;
}
return num;
}
void radixSort(int a[], int len) {
int num = getnum(a, len);//获得位数
vector<vector<int>>radix(10);
for(int k=0;k<num;++k){
for (int i = 0; i < len; ++i) {//存放元素
int t = int(a[i] / pow(10, k))%10;
radix[t].push_back(a[i]);
}
vector<vector<int> >::iterator p;
vector<int>::iterator q;
int i = 0;
for (p = radix.begin(); p != radix.end(); ++p) {//取出元素
for (q = (*p).begin(); q !=(*p).end(); ++q) {
a[i++] = *q;
}
}
for (int i = 0; i < 10; ++i) {//清空容器中元素
if(!radix[i].empty())
radix[i].clear();
}

}
}
int main() {
int a[] = { 0,1,89,4,45,41,7,9,0,52,80,100 };
int len = sizeof(a) / sizeof(a[0]);
radixSort(a, len);
for (int i = 0; i < len; ++i) {
cout << " " << a[i];
}
getchar();
return 0;
}


10桶排序(Bucket sort)

简述

顾名思义,桶排序就是把同类元素放在相同的桶里,然后又对桶内元素递归调用桶排序本身,是分治和递归的典型。

计数排序,基数排序以及桶排序的比较:

三者都出现了桶的概念。

计数排序是一个桶只存单一值,

基数排序是根据元素的位数来往桶里存放数据,

而桶排序是存储一定范围的数据。

算法描述

1确定桶的数量和桶区间

2遍历列表把元素放到对应桶里

3重复步骤2,即对桶中元素递归调用桶排序。

4把排序好的元素放回原列表,直到排序完成

算法分析

平均:T(n)=o(n^2)

#include<iostream>
#include<vector>
using namespace std;
int getRange(vector<int> a, int begin, int end,int &min,int &max) {//获得数组元素范围和极值
for (int i = begin + 1; i <= end; ++i) {
min = a[i] < min ? a[i] : min;
max = a[i] > max ? a[i] : max;
}
return max - min+1;
}
void bucketSort(vector<int>&a, int begin, int end) {//容器做参数时用引用传递来调换元素顺序,
if (end - begin <1)
return;//递归出口
int min, max;min=max = a[0];
int range = getRange(a, begin, end, min, max); //获得元素范围和极值
int bucketNum = 5;//本次定义了5个桶
int gap = range / 5+1;//设定桶区间
vector <vector<int > > bucket(bucketNum);//用二维容器来装桶
for (int i = begin; i <= end; ++i){
cout << "桶编号: " << (a[i] - min) / gap << "放入元素:" << a[i] << endl;
bucket[(a[i]-min )/ gap] .push_back( a[i]);//元素放在不同的桶里
}
for (int i = 0; i < bucketNum; ++i) {
bucketSort( bucket[i],0,bucket[i].size()-1);//对桶里的元素递归调用桶排序
}
for (int i = 0,j=0; i < bucketNum; ++i) {
if (!bucket[i].empty()) {//桶非空判断
for (vector<int>::iterator p = bucket[i].begin(); p!=bucket[i].end(); ++p) {//桶里排序好的元素放回原容器a
a[j++] = *p;
}
}
}
cout << endl;
}
int main() {
vector<int>a;
for (int i = 0,j=100; i<20; ++i) {
a.push_back(j--);
}
bucketSort(a, 0, a.size()-1);
cout << "排序结果:" << endl;
for (int i = 0; i<a.size(); ++i) {
cout << "  "<<a[i];
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: