@kakadee
2016-07-12T06:52:09.000000Z
字数 5918
阅读 1593
算法 查找和排序
将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。
如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
// 1. 直接插入排序void InsertSort(vector<int> &num) {int low = 0, high = num.size()-1;for (int i = low + 1; i <= high; ++i){if (num[i] < num[i - 1]) {int j = i;while (j > low && num[j] < num[j - 1]) {swap(num[j], num[j - 1]);j--;}}}}
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
void ShellInsertSort(vector<int> &num, int n, int dk) {for (int i = dk; i < n; ++i) {int j = i;while (j - dk >= 0 && num[j] < num[j - dk]) {swap(num[j], num[j - dk]);j -= dk;}}}void ShellSort(vector<int> &num) {int dk = num.size() / 2;while (dk >= 1) {ShellInsertSort(num, num.size(), dk);dk /= 2;}}
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推.....
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。
int SelectMinKey(vector<int> &num, int i) {int minKey = i;for (int j = i + 1; j < num.size(); ++j) {minKey = num[j] < num[minKey] ? j : minKey;}return minKey;}void SimpleSelectSort(vector<int> &num) {for (int i = 0; i < num.size(); ++i) {int minKey = SelectMinKey(num, i);if (minKey != i)swap(num[minKey], num[i]);}}
堆的定义如下:具有n个元素的序列(),当且仅当满
堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。
一般都用数组来表示堆,i结点的父结点下标就为。它的左右子结点下标分别为和。如第0个结点左右子结点下标分别为1和2。
// 新加入i结点 其父结点为(i - 1) / 2void MinHeapFixup(vector<int> &a, int i){int j = (i - 1) / 2, temp = a[i];while (j >= 0 && i != 0){ //把较大的子结点往下移动,替换它的子结点if (a[j] <= temp) break;a[i] = a[j];i = j;j = (i - 1) / 2;}a[i] = temp;}// 更简短的表达:void MinHeapFixup2(vector<int> &a, int i){for (int j = (i - 1) / 2; (j >= 0 && i != 0)&& a[i] > a[j]; i = j, j = (i - 1) / 2)Swap(a[i], a[j]);}// 在最小堆中加入新的数据nNumvoid MinHeapAddNumber(vector<int>, int nNum){a.push_back(nNum);MinHeapFixup(a, a.size()-1);}
堆中每次都只能删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。
// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2void MinHeapFixdown(vector<int> &a, int i, int n){int j, temp;temp = a[i];j = 2 * i + 1;while (j < n){if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的j++;if (a[j] >= temp)break;a[i] = a[j]; //把较小的子结点往上移动,替换它的父结点i = j;j = 2 * i + 1;}a[i] = temp;}//在最小堆中删除数void MinHeapDeleteNumber(vector<int> &a,int n){Swap(a[0], a[n - 1]);MinHeapFixdown(a, 0, n - 1);}
void MakeMinHeap(vector<int> &a, int n){for (int i = n / 2 - 1; i >= 0; i--)MinHeapFixdown(a, i, n);}
// 小顶堆排序后的数组是递减的,若要递增,使用大顶堆void Minheapsort(vector<int> &a, int n){MakeMinHeap(a, n);for (int i = n - 1; i >= 1; i--){Swap(a[i], a[0]);MinHeapFixdown(a, 0, i);}}
// 4. 堆排序void MaxHeapFixdown(vector<int> &a, int i, int n){int j = 2 * i + 1, temp = a[i];while (j < n){if (j + 1 < n && a[j + 1] > a[j]) //在左右孩子中找最大的j++;if (a[j] <= temp)break;a[i] = a[j]; //把较大的子结点往上移动,替换它的父结点i = j;j = 2 * i + 1;}a[i] = temp;}void MakeMaxHeap(vector<int> &a, int n){for (int i = (n-1)/2; i >= 0; i--)MaxHeapFixdown(a, i, n);}void Maxheapsort(vector<int> &a, int n){MakeMaxHeap(a, n);for (int i = n - 1; i > 0; i--){swap(a[i], a[0]);MaxHeapFixdown(a, 0, i);}}
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
void BubbleSort(vector<int> &a) {int n = a.size();for(int i = 0;i<n;++i)for(int j=0;j<n-i-1;++j){if(a[j] > a[j+1])swap(a[j],a[j+1]);}}
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
(a)一趟排序的过程:
初 始 关 键 字 : (49) 38 65 97 76 13 27 49
进 行 1 次 交 換: 27 38 65 97 76 13 (49) 49
进 行 2 次 交 换: 27 38 (49) 97 76 13 65 49
迸 行 3 次 交 換: 27 38 13 97 76 (49) 65 49
进 行 4 次 交 换: 27 38 13 (49) 76 97 65 49
int Partition(vector<int> &a, int low, int high) {int pivotKey = a[low]; // 这里pivotKey记录的一定是值,不能是下标,因为下标的值可能会换while (low < high) {while (low < high && a[high] >= pivotKey) high--;swap(a[high], a[low]);while (low < high && a[low] <= pivotKey) low++;swap(a[high], a[low]);}return low;}void QuickSort(vector<int> &a, int low, int high) {if (low < high) {int pivotLoc = Partition(a, low, high);QuickSort(a, low, pivotLoc - 1);QuickSort(a, pivotLoc + 1, high);}}
归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
// 7. 归并排序//将二个有序数列a[first...mid]和a[mid...last]合并。void MergeArray(vector<int> &array, int low, int mid, int high){int i = low; // i是第一段序列的下标int j = mid + 1; // j是第二段序列的下标int k = 0; // k是临时存放合并序列的下标vector<int> tmp(high - low + 1); // array2是临时合并序列// 扫描第一段和第二段序列,直到有一个扫描结束while (i <= mid && j <= high) {// 判断第一段和第二段取出的数哪个更小,将其存入合并序列,并继续向下扫描if (array[i] <= array[j]) {tmp[k++] = array[i++];} else {tmp[k++] = array[j++];}}// 若第一段序列还没扫描完,将其全部复制到合并序列while (i <= mid) {tmp[k++] = array[i++];}// 若第二段序列还没扫描完,将其全部复制到合并序列while (j <= high) {tmp[k++] = array[j++];}// 将合并序列复制到原始序列中for (i = 0; i < k; i++)array[low + i] = tmp[i];}void MergeSort(vector<int> &a, int low, int high){if (low < high){int mid = (low + high) / 2;MergeSort(a, low, mid); //左边有序MergeSort(a, mid + 1, high); //右边有序MergeArray(a, low, mid, high); //再将二个有序数列合并}}
是将阵列分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的阵列内的数值是均匀分配的时候,桶排序使用线性时间(O(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
// 8. 桶排序//找到num的从低到高的第pos位的数据int GetNumInPos(int num, int pos){int temp = 1;for (int i = 0; i < pos - 1; i++)temp *= 10;return (num / temp) % 10;}#define RADIXNUM 10 // 基数(桶的个数)#define KEYNUM 10 // 关键字个数,这里为最大整数的位数void RadixSort(vector<int> &a){int n = a.size();vector<vector<int> > bucket(RADIXNUM, vector<int>(n)); // 分配桶空间for (int pos = 1; pos <= KEYNUM; pos++) //从个位到最高位{for (int i = 0; i < n; i++) //分配过程{int num = GetNumInPos(a[i], pos);int index = ++bucket[num][0];bucket[num][index] = a[i];}for (int i = 0, j = 0; i < RADIXNUM; i++) //收集{for (int k = 1; k <= bucket[i][0]; k++)a[j++] = bucket[i][k];bucket[i][0] = 0; //复位}}}