排序算法学习
来源:互联网 发布:csgo职业选手优化fps 编辑:程序博客网 时间:2024/06/06 02:08
排序算法有:冒泡排序,快速排序,选择排序,堆排序,插入排序,合并排序,希尔排序,计数排序,基数排序,桶排序。
交换排序: 冒泡排序、快速排序
选择排序:直接选择排序、树形选择排序、堆排序
插入排序: 直接插入排序、希尔排序、合并排序
分配排序: 桶排序、基数排序、计数排序
交换排序
1、冒泡排序:
对待排序元素的关键字从后往前进行多遍扫描,遇到相邻两个关键字次序与排序规则不符时,就将这两个元素进行交换。这样关键字较小的那个元素就像一个泡泡一样,从最后面冒到最前面来。
#include <iostream> using namespace std; void BubbleSort(int a[], int n) { for (int i = 0; i < n; i++) //遍历n次 for (int j = n-1; j > i; j--) { if (a[j] < a[j-1]) { //当前比较前面键值,使当前总为最小的 swap(a[j-1], a[j]);//交换 } } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "冒泡排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; BubbleSort(num, 6); cout << "冒泡排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }
冒泡的时间复杂度为O(n2),空间复杂度O(1)。稳定。
快速排序采用了分治算法策略,它是冒泡排序的一种改进。
基本思路是:把待排列的数据分为两个子列,从数列中挑出一个数作为基准,遍历其他数据,把小于它的放前面,大的放在基准的后面。之后,通过递归,将各个子序列划分为更小的序列,直到把小于基准值元素的子数列和大于基准值元素的字数列排序。
#include <iostream> using namespace std; //拆分为两个子列 int Partition(int a[], int left, int right) { int base = a[left]; while (left < right) { while (left < right && a[right]>base) //从右往左找出第一个比基准小的数据 --right; a[left] = a[right]; //将这个数放到基准的左边 while (left < right && a[left]<base) //从左往右找出第一个比基准大的数据 ++left; a[right] = a[left]; //放到右边 } a[left] = base; return left; //返回基准的位置 } void QuickSort(int a[], int left, int right) { int i; if (left < right) { i = Partition(a, left, right); QuickSort(a, left, i-1); QuickSort(a, i+1, right); } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; QuickSort(num, 0, 5); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }
</pre><pre name="code" class="cpp">
<strong><span style="font-size:32px;">选择排序</span></strong>
<strong><span style="font-size:32px;"></span></strong>
<span style="font-size:32px;"></span><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">1,直接选择排序:</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">对待排序的序列,选出关键字最小的数据,将它和第一个位置的数据交换,接着,选出关键字次小的数据,将它与第二个位置上的数据交换。以此类推,直到完成整个过程。</p><p style="margin-top: 0px; margin-bottom: 0px; padding-top: 0px; padding-bottom: 0px; font-family: Arial; font-size: 14px; line-height: 26px;">所以如果有n个数据,那个需要遍历n-1遍。其实现代码如下:</p><strong></strong>
#include <iostream> using namespace std; void SelectSort(int a[], int n) { int i, j, small; for (i = 0; i < n-1; ++i) { small = i; for (j = i+1; j < n; ++j) { if (a[small] > a[j]) small = j; } if (small != i) swap(a[small], a[i]); } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; SelectSort(num, 6); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }直接选择排序的时间复杂度为O(n2)。同样适合对链式结构进行排序
2、堆排序
#include <iostream> using namespace std; //用数组二叉树 void HeapAdjust(int a[], int s, int n)//构成堆 { int j; while(2*s + 1 < n) //第s个结点有右子树 { j=2*s + 1 ; if((j+1) < n) { if(a[j] < a[j+1])//右左子树小于右子树,则需要比较右子树 j++; //序号增加1,指向右子树 } if(a[s] < a[j])//比较s与j为序号的数据 { swap(a[s], a[j]); s = j ;//堆被破坏,需要重新调整 } else //比较左右孩子均大则堆未破坏,不再需要调整 break; } } void HeapSort(int a[],int n)//堆排序 { int t, i; int j; for(i = n/2 - 1; i >= 0; i--) //将a[0,n-1]建成大根堆 HeapAdjust(a, i, n); for(i = n-1; i > 0; i--) { swap(a[0], a[i]); HeapAdjust(a, 0, i); //将a[0]至a[i]重新调整为堆 } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; HeapSort(num, 6); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }
堆排序的时间复杂度为O(nlog2n),空间复杂度为O(1)。不稳定。
插入排序
1,直接插入排序
直接插入排序是一种简单直观的排序算法,它的工作原理是通过建有序序列,对于没有排序的数据,在已排序序列中从后往前扫描,找到相应位置,并插入。故,如果是数组这样的连续空间的数据序列,那就每次插入都要将其位置的后面数据都向后移动,相对比较麻烦。
#include <iostream> using namespace std; void InsertSort(int a[], int n) { int i, j, temp; for (i = 1; i < n; ++i) { temp = a[i]; //先保存当前值 for (j = i-1; j >= 0 && temp < a[j]; --j) //从后往前移,直到找到适合位置 a[j+1] = a[j]; //往后移一位,腾出位置 a[j+1] = temp; //将值放入已找出的适当位置 } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; InsertSort(num, 6); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }
这样的直接插入排序空间复杂度O(1),但是时间复杂度为O(n2)
2、希尔排序
希尔排序(Shell Sort)是插入排序的一种。是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。
它的具体做法是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成(n除以d1)个组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
#include <iostream> using namespace std; void ShellSort(int a[], int n) { int d, i, j, temp; d = n/2; //分成n/2组 while (d >= 1) { for (i = d; i < n; ++i) { //对每组进行直接插入排序 temp = a[i]; j = i - d; while (j >= 0 && a[j] > temp) { a[j+d] = a[j]; j -= d; } a[j+d] = temp; } d /= 2; } } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; ShellSort() cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }希尔排序时间复杂度为O(n4/3),空间复杂度为O(1),但是不适合在链表结构上使用。
3、合并排序(归并排序)
合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。合并排序也叫归并排序。
<pre name="code" class="cpp">#include <iostream> using namespace std; //2-路合并 void Merge(int a[], int r[], int left, int mid, int n) {//a[]归并到r[] int s1 = left, s2 = mid+1, s3 = left; while (s1 <= mid && s2 <= n)//比较较小的数据填充到a[] if (a[s1] <= a[s2]) r[s3++] = a[s1++]; else r[s3++] = a[s2++]; while (s1 <= mid) r[s3++] = a[s1++];//将未填充的数补全 while (s2 <= n) r[s3++] = a[s2++];//将未填充的数补全 } //2-路归并排序 void MergePass(int a[], int r[], int n, int len) { int beg = 0, end; while (beg + len < n) { end = beg + 2*len - 1; if (end >= n)//最后一个可能少于len个 end = n - 1; Merge(a, r, beg, beg+len-1, end);//合并 beg = end + 1;//提供给下次开始 } if (beg < n) while (beg < n) { r[beg] = a[beg]; beg++; } } void MergeSort(int a[], int n) { int len = 1;//当前进行归并有序数列的长度 int f = 0; int *p = (int *)malloc(sizeof(int)*n); while(len < n) { if (f) MergePass(p, a, n, len);//交替归并到p和a else MergePass(a, p, n, len); len *= 2; f = 1 - f; } if (f) //排序后是归并到p的情况,复制回到a for (f = 0; f < n; f++) a[f] = p[f]; free(p); } int main() { int num[6] = {23,45,13,2,99,78}; cout << "排序前:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; MergeSort(num, 6); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << num[i] << " "; } cout << endl; return 0; }
分配排序
桶排序:
假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。
他的排序过程如图:桶排序只是用于关键字取值范围较小的情况,否则会因为所需箱子的数目太多而导致资源的浪费。这种排序的使用价值不大,他一般用于基数排序的一个中间过程。
2,基数排序:
基数排序是桶排序的一种改进和推广。它的基本思想是,先设立r个队列,队列编号分别为0~r-1,(r为关键字的基数),然后按照下面的规则对关键字进行“分配”和“收集”。
1, 按照最低有效位的值,把n个关键字分配到上述的r个队列里,然后从小到达将各队列中关键字收集起来。
2, 再按低次有效位的值把刚刚收集起来的关键字分配到r个队列中,重复收集工作。
3, 重复上述分配和收集工作,直到最高的有效位。(也就是说,如果数位为d,则需要重复进行d次。d由所有元素中最长的一个元素的位数计量。)
图示如下:上图过程,就是先按个位分配然后收集,再按十位,百位分配和收集,最后就得出排序结果。
在C++中可以使用库函数lexicongraphical_compare()进行字典次序比较。
每一趟分配的时间是O(n),所以总时间的开销为O(d(n+r)) = O(n),通常d,r为常数。空间负复杂度为O(n+r)。基数排序使用于采用链式结构存储结构的排序。
3,计数排序:
计数排序是一个类似于桶排序的排序算法,其优势是对已知数量范围的数组进行排序。它创建一个长度为这个数据范围的数组C,C中每个元素记录要排序数组中对应记录的出现个数。这个算法于1954年由 Harold H. Seward 提出。
计数排序算法的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。
#include <iostream> using namespace std; //n为个数,k为最大值,b为输出 void CountingSort(int a[], int b[], int n, int k) { int* c = new int[k+1]; memset(c, 0, (k+1) * sizeof(int)); for (int j = 0; j < n; j++) c[a[j]]++;//保存每个下标的值的个数 for (int j = 1; j <= k; j++) c[j] += c[j - 1];//从前到后累计的位置 for (int j = n - 1; j >= 0; j--) { b[c[a[j]] - 1] = a[j]; c[a[j]]--; } delete []c; } int main() { int num[6] = {23,45,13,2,99,78}; int out[6]; cout << "排序前:" << endl; int max = 0; for (int i = 0; i < 6; i++) { if (max < num[i]) max = num[i]; cout << num[i] << " "; } cout << endl; CountingSort(num, out, 6, max); cout << "排序后:" << endl; for (int i = 0; i < 6; i++) { cout << out[i] << " "; } cout << endl; return 0; }
- 算法学习-排序算法
- 排序算法学习- 快速排序
- 排序算法学习-冒泡排序
- 排序算法学习:快速排序
- 常见排序算法学习
- 算法学习_排序
- 算法学习-常用排序
- 冒泡排序算法学习
- 排序算法学习
- 算法学习 数组排序
- 算法学习--排序
- 算法学习 之排序
- 排序算法学习
- 排序算法学习笔记
- 排序算法学习笔记
- 排序问题 - 算法学习
- 算法学习-归并排序
- 算法学习__排序
- HDU 1.1.4 A+B for Input-Output Practice (IV)
- spring在web工程和普通java工程使用时候区别
- HTML5拖动技术
- ecplise建java工程有红色叹号
- Android NDK项目崩溃信息抓取
- 排序算法学习
- Emojicon Android表情开发
- 执行脚本出现bin/bash: bad interpreter: No such file or directory
- HEXO+Github,搭建属于自己的博客
- kidd风的IOS日志之IOS9获取联系人信息Contact FrameWork的基本使用
- iOS property两种实现方法区别的简单介绍
- appium 真机测试问题 出现 instruments crashed on startup
- Android - Adapter的使用
- 创建Pch预编译文件