赞
踩
本章的所有代码可以访问这里
排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求能在内外存之间移动数据的排序。
<实际中我们玩扑克牌时,就用了插入排序的思想>
我们首先认为我们第一个数据是有序的。
取一个数据data,将已排序的元素序列从后往前遍历
如果遍历过程中发现已排序的元素序列中的某个数据Data1
大于要排序的data
那我们就将数据Data1
向后移动一个单位,然后继续向前遍历。如果已排序的元素序列中的某个数据Data2
小于data
,那我们就将数据data
放到Data2
的后面,然后就不需要向前遍历了,因为已排序的元素序列经过上面的2-3步骤后新来的数据data也被放到了正确的位置。
所以我们对许多数据进行排序就被转化为上面2-3步骤的循环了。
动图演示
//直接插入排序 void InsertSort(int* a,int n) { //排序 for (int i = 0; i + 1 < n; i++) { //end是有序数列的最后一个位置的下标 int end = i; int tmp = a[end + 1]; //end >= 0 防止在--end时数组越界 while (end >= 0 && tmp < a[end]) { a[end +1] = a[end]; --end; } a[end+1] = tmp; } }
希尔排序法的步骤是:
先选定一个小于N(N为数组的个数)的整数gap,把所有数据分成gap个组,所有距离为gap的数据分在同一组内,并对每一组的元素进行直接插入排序。然后取新的gap(新的gap要小于上一个gap),重复上述分组和排序的工作。当到达gap=1时,所有数据在同一组内排好顺序。
希尔排序法的思想是:
希尔排序先将待排序列通过gap分组进行预排序(gap>1时都是预排序),使待排序列接近有序,然后再对该序列进行一次插入排序(gap=1时其实就是插入排序),此时插入排序的时间复杂度接近于O(N)
代码实现
//希尔排序 正常一组一组排序 void ShellSort(int* a,int n) { //定义第一次gap的值 int gap = n; //多次预排序,gap=1时是直接排序 while (gap > 1) { //减小gap的值直到1 gap = gap / 3 + 1; //多趟排序 for (int j = 0; j < gap; j++) { //单趟排序 i是当前有序序列最后一个位置的下标 for (int i = j; i + gap < n; i += gap) { int end = i; //记录一下待插入的数据 int tmp = a[end + gap]; //end >= 0 防止在end -=gap 时数组越界 while (end >= 0 && tmp < a[end]) { a[end + gap] = a[end]; end -= gap; } a[end + gap] = tmp; } } } }
基本思想:
每一次从待排序的数据元素中选出最小和最大的一个元素,存放在序列的起始位置和末尾位置,然后起始位置向后移动一个单位,末尾位置向前移动一个单位,直到全部待排序的数据元素排完 。
步骤:
遍历一遍整个数组,选出最大值和最小值,第一次将最小值放到a[0]最大值a[n-1],第二次将最小值放到a[1]最大值a[n-2],直到下标
m
>
=
n
−
m
−
1
m >= n-m-1
m>=n−m−1结束程序。
动态演示
(下图演示的是一次只选一个最小值的算法,我们一次选出最大和最小,比图中的更高效一些)
代码实现
//选择排序 void SelectSort(int* a, int n) { //多趟排序 int begin = 0; int end = n - 1; while (begin < end) { //先假设 a[0] 位置既是最小值又是最大值 //mini是最小值的下标,maxi是最大位置的下标 int mini = begin, maxi = begin; //单趟遍历,选出最大值于最小值 for (int i = begin; i <=end; ++i) { if (a[i] > a[maxi]) { maxi = i; } else if (a[i] < a[mini]) { mini = i; } } Swap(&a[begin], &a[mini]); if (begin == maxi) { maxi = mini; } Swap(&a[end], &a[maxi]); ++begin; --end; } }
直接选择排序的特性总结:
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
堆排序详情请看这里
堆排序的特性总结:
基本思想:
两两进行比较,左边大于右边就进行交换,走完一趟就排序好一个数。
代码实现
//冒泡排序 int BubbleSort(int*a,int n) { for (int j = 0; j < n; j++) { //end是排序数组的最后一个需要排序元素的下标 int end = n - 1 - j; //定义一个交换标志 int exchange = 0; for (int i = 0; i < end; i++) { if (a[i] > a[i + 1]) { Swap(&a[i], &a[i + 1]); exchange = 1; } } // 一趟冒泡过程中,没有发生交换,说明已经有序了,不需要再处理 if (exchange == 0) { break; } } }
冒泡排序的特性总结:
基本思路:
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
步骤:
动态演示
代码实现
//快速排序 hoare法 void QuickSort(int* a, int begin, int end) { //当区间只有一个值时,或区间不存在时退出递归 if (begin >= end) { return; } //left是闭区间最最左边的下标,right是最右边位置的下标 int left = begin, right = end; //选择最左边作为key值 int keyi = left; //一趟排序 while (left < right) { //右边先走,找到a[right]<key的位置 while (left < right && a[right] >= a[keyi]) { --right; } //左边后走,找到a[left]>key的位置 while (left < right && a[left] <= a[keyi]) { ++left; } //交换左右两边的值,如果left == right了,也没有必要交换了。 if (left != right) { Swap(&a[left], &a[right]); } } //交换左右两边的值,如果keyi == left了,也没有必要交换了。 if (keyi != left) { Swap(&a[keyi], &a[left]); } keyi = left; //此时区间被分割为[begin,keyi-1] keyi [keyi+1,end] //注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。 QuickSort(a, begin, keyi-1); QuickSort(a, keyi+1, end); }
步骤:
//快速排序 挖坑法 void QuickSort(int* a, int begin, int end) { //当区间只有一个值时,或区间不存在时退出递归 if (begin >= end) { return; } int left = begin, right = end; int keyi = left; //刚开始时keyi的位置作为坑位 int hole = keyi; int key = a[keyi]; while (left < right) { while (left < right && a[right] >= key) { --right; } a[hole] = a[right]; hole = right; while (left < right && a[left] <= key) { ++left; } a[hole] = a[left]; hole = left; } a[hole] = key; keyi = hole; //此时区间被分割为[begin,keyi-1] keyi [keyi+1,end] //注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。 QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
步骤:
//快速排序 前后指针法 void QuickSort(int* a, int begin, int end) { //当区间只有一个值时,或区间不存在时退出递归 if (begin >= end) { return; } int prev = begin , cur = begin + 1; int keyi = begin; while (prev <= cur && cur <= end) { if (a[cur] < a[keyi] && ++prev != cur) { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); keyi = prev; //此时区间被分割为[begin,keyi-1] keyi [keyi+1,end] //注意传递的是begin,不是0,中间递归时不一定是从0开始的区间,0是一个常量,begin是一个变量。 QuickSort(a, begin, keyi - 1); QuickSort(a, keyi + 1, end); }
快速排序的特性总结:
快速排序的非递归实现要依赖栈,我们对快速排序进行递归时传递的关键数据是区间的下标,只有拿到了区间的下标我们才能进行正确的排序。
因此我们应该用利用栈的特性将区间的下标存放起来。
代码实现:
//单趟排序 int Partion3(int* a ,int begin ,int end) { int prev = begin , cur = begin + 1; int keyi = begin; while (prev <= cur && cur <= end) { if (a[cur] < a[keyi] && ++prev != cur) { Swap(&a[prev], &a[cur]); } ++cur; } Swap(&a[keyi], &a[prev]); keyi = prev; return keyi; } //快速排序的非递归实现 void QuickSortNonR(int* a ,int begin ,int end) { Stack st; StackInit(&st); StackPush(&st, begin); StackPush(&st, end); while (!StackEmpty(&st)) { int right = StackTop(&st); StackPop(&st); int left = StackTop(&st); StackPop(&st); //一趟排序 int keyi = Partion3(a, left, right); //此时区间被划分为[left,keyi-1]keyi[keyi+1,right] if (keyi + 1 < right) { StackPush(&st, keyi + 1); StackPush(&st, right); } if (left < keyi - 1) { StackPush(&st, left); StackPush(&st, keyi - 1); } } StackDestroy(&st); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。