算法-排序 发表于 2016-12-23 | 分类于 arithmetic | | 阅读次数 视觉直观感受 7 种常用的排序算法 头文件如下:123456#define OK 0#define ERROR -1#define TRUE 1#define FALSE 0#define NULL (void*)0typedef int Status; 交换两个变量的值12345678910111213141516171819202122232425262728void Swap(int& a, int& b){ #if 0 a = a + b - (b = a); #endif #if 0 b = a + (a = b) * 0; #endif #if 0 a = a + b; b = a - b; a = a - b; #endif #if 0 a = a * b; b = a / b; a = a / b; #endif #if 1 a = a ^ b; b = a ^ b; a = a ^ b; #endif} 插入排序123456789101112131415161718192021222324252627282930313233/*性能: 1)稳定 2)空间代价:O(1) 3)时间代价:步骤: 1、从第一个元素开始,该元素可以认为已经被排序 2、取出下一个元素,在已经排序的元素序列中从后向前扫描 3、如果该元素(已排序)大于新元素,将该元素移到下一位置 4、重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 5、将新元素插入到该位置后 6、重复步骤2~5*/Status InsertSort(int array[], int len){ if (NULL == array) { return ERROR; } register int i, j, temp; for (i = 1; i < len; i++) { temp = array[i]; j = i-1; //key1. before the current location while ((j >= 0) && (array[j] > temp)) { array[j+1] = array[j]; j--; } array[j+1] = temp; //key2. pay attention to 'j+1' } return OK;} 二分插入排序12345678910111213141516171819202122232425262728293031323334353637383940/*性能: 1)稳定 2)空间代价:O(1) 2)时间间代价:步骤: 1、从第一个元素开始,该元素可以认为已经被排序 2、取出下一个元素,在已经排序的元素序列中二分查找到第一个比它大的数的位置 3、将新元素插入到该位置后 4、重复上述两步*/Status BinInsertSort(int array[], int len){ if (NULL == array) { return ERROR; } register int i, j, temp, left, right, mid; for (i = 1; i < len; i++) { temp = array[i]; left = 0; right = i-1; while (left <= right) { mid = (left + right)/2; if (array[mid] > temp) right = mid-1; else left = mid+1; }//key. 此时left肯定就是待插入的位置坐标 for (j = i-1; j > left; j--) { array[j+1] = array[j]; } array[left] = temp; } return OK;} 希尔排序1234567891011121314151617181920212223242526272829303132333435363738/*性能: 1) 2)空间代价: 2)时间间代价:步骤: 1、先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。 2、所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序。 3、取第二个增量d2<d1重复上述的分组和排序, 4、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。*/Status ShellSort1(int array[], int len){ register int i, j, k, gap, temp; for (gap = len/2; gap > 0; gap /= 2) //步长 { for (i = 0; i < gap; i++) //直接插入排序 - 执行 gap 趟 { for (j = i + gap; j < len; j += gap) { if (array[j] < array[j - gap]) { temp = array[j]; k = j - gap; while (k > 0 && array[k] > temp) { array[k + gap] = array[k]; k -= gap; } array[k + gap] = temp; } } } } return OK;} 选择排序12345678910111213141516171819202122232425262728293031323334/*性能: 1)稳定 2)空间代价:O(1) 2)时间间代价:步骤: 1、初始状态:无序区为R[1..n],有序区为空。 2、第i趟排序(i=1,2,3...n-1) 第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。 该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换, 使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。 3、前n-1趟结束,数组有序化了*/Status SelectSort(int array[], int len){ if (NULL == array) { return ERROR; } register int i, j, min; for (i = 0; i < len - 1; i++) { min = i; for (j = i+1; j < len; j++) { if (array[min] > array[j]) { min = j; } } Swap(array[i], array[min]); } return OK;} 堆排序12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849/*性能: 1) 2)空间代价: 2)时间间代价:步骤: 一般都用数组来表示堆,i结点的父结点下标为(i-1)/2,左右子结点的下标分别为 2*i + 1和 2*i + 2 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。 堆即是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。1. 什么是堆? 我们这里提到的堆一般都指的是二叉堆,它满足二个特性: 1>父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值 2>每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)2、什么是堆调整(Heap Adjust)? 这是为了保持堆的特性而做的一个操作。对某一个节点为根的子树做堆调整,其实就是将该根节点进行“下沉”操作 (具体是通过和子节点交换完成的),一直下沉到合适的位置,使得刚才的子树满足堆的性质。 例如对最大堆的堆调整我们会这么做: 1、在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个,将其下标存储在largest中。 2、如果A[i]已经就是最大的元素,则程序直接结束。 3、否则,i的某个子结点为最大的元素,将A[largest]与A[i]交换。 4、再从交换的子节点开始,重复1,2,3步,直至叶子节点,算完成一次堆调整。 这里需要提一下的是,一般做一次堆调整的时间复杂度为log(n)。3、如何建堆? 建堆是一个通过不断的堆调整,使得整个二叉树中的数满足堆性质的操作。 在数组中的话,我们一般从下标为n/2的数开始做堆调整,一直到下标为0的数 (因为下标大于n/2的数都是叶子节点,其子树已经满足堆的性质了)4、如何进行堆排序 ? 堆排序是在上述3中对数组建堆的操作之后完成的。 数组储存成堆的形式之后,第一次将A[0]与A[n - 1]交换,再对A[0…n-2]重新恢复堆。 第二次将A[0]与A[n-2]交换,再对A[0…n-3]重新恢复堆,重复这样的操作直到A[0]与A[1]交换。 由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了 */#include<math.h>int parent(int i){ return (int)floor((i - 1) / 2);}int left(int i){ return (2 * i + 1)}int right(int i){ return (2 * i + 2)}void HeapAdjust(int array[], int i, int heap_size){} 冒泡排序12345678910111213141516171819202122232425262728293031/*性能: 1)稳定 2)空间代价: 2)时间间代价:步骤: 1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。 2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 3、针对所有的元素重复以上的步骤,除了最后一个。 4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。*/Status BubbleSort(int array[], int len){ if (NULL == array) { return ERROR; } register int i, j; for (i = len; i > 0; i--) { for (j = 0; j < i-1; j++) { if (array[j] > array[j+1]) //key 最大的数据沉底 { Swap(array[j], array[j+1]); } } } return OK;} 快速排序1234567891011121314151617181920212223242526272829303132333435363738394041/*性能: 1)稳定 2)空间代价: 2)时间间代价:步骤: 1、从数列中挑出一个元素,称为 "基准"(pivot), 2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。 3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。*/int Partition(int* data, int low, int high){ int pivotkey = low; while (low < high) { while (low < high && data[high] > data[pivotkey]) { high--; } Swap(data[low], data[high]); while (low < high && data[low] < data[pivotkey]) { low++; } Swap(data[low], data[high]); } return low;}Status QuickSort(int array[], int low, int high){ int pivot; if (low < high) { pivot = Partition(array, low, high); QuickSort(array, low, pivot-1); QuickSort(array, pivot+1, high); } return OK;} 归并排序类