赞
踩
直接插入排序的思想:
void InsertSort() { int i, j; for (i = 2; i <= len; ++i) { if (arr[i] < arr[i - 1]) { arr[0] = arr[i]; //复制为监视哨 arr[i] = arr[i - 1]; // for (j = i - 2; arr[0] < arr[j]; --j) arr[j + 1] = arr[j]; //记录后移 arr[j + 1] = arr[0]; } } } 排序过程 监视哨 1 2 3 4 5 6 7 8 9 10 第0次排序: 0 49 28 65 76 13 27 36 58 11 17 第1次排序: 28 28 49 65 76 13 27 36 58 11 17 //从第2个数开始比较 第2次排序: 28 28 49 65 76 13 27 36 58 11 17 第3次排序: 28 28 49 65 76 13 27 36 58 11 17 第4次排序: 13 13 28 49 65 76 27 36 58 11 17 第5次排序: 27 13 27 28 49 65 76 36 58 11 17 第6次排序: 36 13 27 28 36 49 65 76 58 11 17 第7次排序: 58 13 27 28 36 49 58 65 76 11 17 第8次排序: 11 11 13 27 28 36 49 58 65 76 17
直接插入排序算法特点
算法分析
折半插入排序的思想:
算法分析
排序思想:
将记录序列分成若干子序列,分别对每个子序列进行插入排序
例如将 n 个记录分成 d 个子序列:
{R[1], R[1+d], R[1+2d], …, R[1+kd]}
{R[2], R[2+d], R[2+2d], …, R[2+kd]}
……………………………………
{R[d], R[d+d], R[d+2d], …, R[d+kd]}
其中 d 为增量,在排序过程中从大到小逐渐缩小,最后一趟排序减至1
排序结果 1 2 3 4 5 6 7 8 9 10 11
第0次排序 : 16 25 12 30 47 11 23 36 9 18 31
第1次排序(d=5): 11 23 12 9 18 16 25 36 30 47 31 (11-16 23-25 12-36...)
第2次排序(d=3): 9 18 12 11 23 16 25 31 30 47 36 (9-11-25-47 18-23-31-36...)
第3次排序(d=1): 9 11 12 16 18 23 25 30 31 36 37 (9 11 12 16...)
普通的冒泡排序:
void BubbleSort0(int arr[], int len) {
int i = 0, tmp = 0;
for (i = 0; i < len-1; i++) {
int j = 0;
for (j = 0; j < len-1-i; j++) {
if(arr[j] > arr[j+1]) {
tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
a[]={1,2,3,4,5,6,7,8,10,9}
这组数据,按照上面的排序方式,第一趟排序后将10和9交换已经有序,接下来的8趟排序就是多余的,什么也没做。所以在交换的地方加一个标记,如果那一趟排序没有交换元素,说明这组数据已经有序,不用再继续下去。void BubbleSort1(int arr[], int len) { int i = 0, tmp = 0; for (i = 0; i < len-1; i++) { int j = 0; int flag = 0; for (j = 0; j < len-1-i; j++) { if(arr[j] > arr[j+1]) { tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; flag = 1; // 标记 } } if(flag == 0) return; // 没交换,已有序 } }
(例如:1, 2, 3, 4, 7, 6, 5)
但是对于前面大部分是无序而后边小半部分有序的数据 (1, 2, 5, 7, 4, 3, 6, 8, 9, 10)
排序效率不高对于这种类型数据,我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可void BubbleSort2(int arr[], int len) { int i = 0, tmp = 0, flag = 0; int pos = 0, k = len-1; for (i = 0; i < len-1; i++) { int j = 0; pos = 0; flag = 0; for (j = 0; j < k; j++) { if(arr[j] > arr[j+1]) { tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; flag = 1; // 标记 pos = j; // 交换元素后,记录最后一次交换的位置 } } if(flag == 0) return; // 没交换,已有序 k = pos; // 下一次比较到记录位置即可 } }
void BubbleSort3(int arr[], int len) { int i = 0, j = 0, flag = 0; int pos = 0, k = len-1; int n = 0; // 同时找最大值的最小需要两个下标遍历 for (i = 0; i < len-1; i++) { pos = 0; flag = 0; for (j = 0; j < k; j++) { if(arr[j] > arr[j+1]) { int tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; flag = 1; // 标记 pos = j; // 交换元素后,记录最后一次交换的位置 } } if(flag == 0) return; // 没交换,已有序 k = pos; // 下一次比较到记录位置即可 for(j = k; j > n; j--) { int tmp = arr[j]; arr[j] = arr[j-1]; arr[j-1] = tmp; flag = 1; } n++; if(flag == 0) return; } }
int Partition(int low, int high) { d[0] = d[low]; //用表的第一个记录作为枢纽记录 int pivotkey = d[low].number; //枢纽记录的关键字 while (low < high) { //从表的两端交替向中间扫描 while (low < high && d[high].number >= pivotkey) --high; d[low] = d[high]; //将比枢纽记录关键字小的 移到低端 while (low < high && d[low].number <= pivotkey) ++low; d[high] = d[low]; //将比枢纽记录关键字大的 移到高端 } d[low] = d[0]; //枢纽记录归位 return low; //返回枢纽记录的位置 } void QuickSort(int low, int high) { if (low < high) { int pos = Partition(low, high); //确定枢纽位置 QuickSort(low, pos - 1); //对左子序列排序,不包含枢纽 QuickSort(pos + 1, high); //对右子序列排序,不包含枢纽 } } 枢纽0 | 1 2 3 4 5 6 7 8 9 10 排序前: 49 | 49 28 65 76 13 27 36 58 11 17 //以第一个记录49作为枢纽 排序后: 49 | 17 28 11 36 13 27 49 58 76 65 排序前: 17 | 17 28 11 36 13 27 //左子序列的第一个记录17作为枢纽,进行快排 排序后: 17 | 13 11 17 36 28 27 排序前: 13 | 13 11 //左子序列的左子序列快排 排序后: 13 | 11 13 排序前: 36 | 36 28 27 //左子序列的右子序列快排 排序后: 36 | 27 28 36 排序前: 27 | 27 28 //左子序列的右子序列的右子序列快排 排序后: 27 | 27 28 排序前: 58 | 58 76 65 //右子序列快排 排序后: 58 | 58 76 65 排序前: 76 | 76 65 //右子序列的右子序列快排 排序后: 76 | 65 76
算法分析
算法改进
略
堆排序的来源
n个元素的关键字序列 R[1].key, R[2].key, … ,R[n].key
写成完全二叉树结构时,若根为R[i].key
,则 左子树为R[2*i].key
,右子树为R[2*i+1].key
小根堆:根比左子树和右子树都小(R[i].key ≤ R[2*i].key && R[i].key ≤ R[2*i+1].key)
大根堆:根比左子树和右子树都大(R[i].key ≥ R[2*i].key && R[i].key ≥ R[2*i+1].key)
堆排序的思想
void HeapAdjust(int s, int m) { int j; arr[0] = arr[s]; //暂存堆顶到arr[0] for (j = 2 * s; j <= m; j *= 2) { if (j < m && arr[j] < arr[j + 1]) ++j; //横比 j最初指向左子树 if (arr[0] >= arr[j]) break; //纵比 定位 arr[s] = arr[j]; s = j; } arr[s] = arr[0]; //将调整前的堆顶记录插到位置s处 } void HeapSort() { int i, temp; for (i = len / 2; i > 0; --i) //建堆 HeapAdjust(i, len); for (i = len; i > 1; --i) { temp = arr[i]; arr[i] = arr[1]; arr[1] = temp; HeapAdjust(1, i - 1); } }
算法分析
算法思想
算法分析
对长度为 n 的记录进行2-路归并排序
k-路归并排序需要进行 l o g k n log_{k}n logkn 趟,时间复杂度 O ( n l o g k n ) O(nlog_{k}n) O(nlogkn)
排序趟数与序列的原始状态有关的排序方法是 冒泡、快速
插入排序
快速排序
堆排序
归并排序(2-路归并)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。