排序
直接插入排序
折半插入排序是在直接插入排序上的一个优化,举个例子,我们左边的数据元素本身是有序的,这个时候要插入的关键字跟这个有序序列的最后一个元素对比,也就是前一个元素对比,发现是逆序,那么这个时候就能确定这个元素得插在这个序列里面,怎么快速找到要插入的位置呢,直接插入排序的做法是通过指针遍历前面的每个元素,找到带插入的位置。
但是我们可以通过折半查找的思想快速定位到要插入的位置,但是找到位置后,插入,右边元素移动还是跟原来一样,这个就是折半插入排序由来。
值得注意的是,直接插入排序我们可以用顺序表、也可以用链表,但是折半只能用顺序表
希尔插入排序
冒泡排序
快速排序
简单选择排序
堆排序
归并排序
基数排序
外部排序
外部排序要求我们尽量减少访问磁盘次数,怎么减少呢,磁盘数量是固定不变的,每次排序我们要读入还要写回,既然磁盘数量不变,我们能不能用几趟就排序好呢,所以优化方向是减少趟数S = h-1 = Logkr (k是几个输入缓冲区,r是初始归并段)
- 加多几个缓冲区,带来的代价是内部归并关键字对比加大,要从k个输入缓冲区也就是归并段中比较k-1次,不过我们可以通过败者树优化
- 减少初始归并段的个数,我习惯叫0趟归并排序,这里可以通过置换-选择排序进一步优化
外部排序时间开销 = 读写外存的时间+0趟内部排序所需时间+内部归并所需时间
代码
//
// seq_sort.cpp
#include <stdio.h>
#include<iostream>
using namespace std;
void print_data(int A[], int n){
// cout << "下标从0开始:";
for (int i = 0; i<n; i++) {
cout << A[i] << " ";
}
cout << endl << endl;
}
//下标1开始
void print_data1(int A[], int n){
// cout << "下标从1开始:";
for (int i = 1; i<=n; i++) {
cout << A[i] << " ";
}
cout << endl << endl;
}
//直接插入排序
//算法思想,把前面看成有序,后面的元素查找合适的位置然后插进去
void insert_sort(int A[], int n){
cout << "初始序列:" << endl;
print_data(A, n);
int i, j, temp;
for (i = 1; i<n; i++) {
if (A[i-1]>A[i]) {//只有前驱比当前元素大的时候,才需要发生循环对比,当前元素之前的元素都是有序的
temp = A[i];
for (j = i - 1; j>=0; j--) {
if (temp<A[j]) {
A[j+1] = A[j];
}else{
// cout << "break j:" << j << endl;
break;//跳出, 前面是有序的
}
}
A[j+1] = temp;
}
}
cout << "直接插入排序1 insert_sort:" << endl;
print_data(A, n);
}
//直接插入排序 优化for循环判断
void insert_sort1(int A[], int n){
cout << "初始序列:" << endl;
print_data(A, n);
int i, j, temp;
for (i = 1; i<n; i++) {
if (A[i-1]>A[i]) {//只有前驱比当前元素大的时候,才需要发生循环对比,当前元素之前的元素都是有序的
temp = A[i];
for (j = i - 1; temp<A[j] && j>=0; j--) {
A[j+1] = A[j];
}
A[j+1] = temp;
}
}
cout << "直接插入排序2 insert_sort1:" << endl;
print_data(A, n);
}
// 加个哨兵 下标0 不存实际数据
void insert_sort2(int A[], int n){
cout << "初始序列:" << endl;
print_data1(A, n);
int i,j;
for (i = 2; i<=n; i++) {
if (A[i]<A[i-1]) {
A[0] = A[i];
for (j=i-1; A[0]<A[j]; j--) {
A[j+1] = A[j];
}
A[j+1] = A[0];
}
}
cout << "直接插入排序3 insert_sort2:" << endl;
print_data1(A, n);
}
// 定位插入的位置 我们用折半查找
void insert_sort3(int A[], int n){
cout << "初始序列:" << endl;
print_data1(A, n);
int i,j;
for (i = 2; i<=n; i++) {
if (A[i]<A[i-1]) {
A[0] = A[i];
int low = 1, high = i-1, mid;
while (high>=low) {
mid = (low+high)/2;
if (A[mid] < A[0]) {
low = mid+1;
}else{//包含=的情况,排序的稳定性要求我们要继续查找
high = mid - 1;
}
}
for (j=i-1; low<=j; j--) {
A[j+1] = A[j];
}
A[low] = A[0];
}
}
cout << "直接插入排序4 insert_sort3:" << endl;
print_data1(A, n);
}
void bubble_sort(int A[], int n){
cout << "初始序列:" << endl;
print_data(A, n);
for (int i = 0; i<n-1; i++) {
bool flag = false;
for (int j = 1; j<n-i; j++) {
if (A[j-1]>A[j]) {
int temp = A[j];
A[j] = A[j-1];
A[j-1] = temp;
flag = true;
}
}
if (flag==false) {
break;
}
}
cout << "冒泡排序 bubble_sort:" << endl;
print_data(A, n);
}
// 希尔排序 中间目标是找出基本有序,最后增量为1,也就还是要用直接插入排序
// 这个用链表实现比较麻烦吧 因为要找增量对应的元素
// 不稳定,因为相同的元素在不同子表时会发生调换
void shell_sort(int A[], int n){
cout << "初始序列:" << endl;
print_data1(A, n);
int i,j,d;
//数组下标0不存放实际数据
for (d = n/2; d>=1; d=d/2) {
for (i = d+1; i<=n; i++) {//d+1不奇怪 正确的 下面的if要往回判断
if (A[i]<A[i-d]) {//前面的比后面的大,需要交换
A[0] = A[i];
//现在要找位置,并把对应元素后移+d的位置
for (j = i-d; A[0]<A[j]&&j>0; j = j-d) {
A[j+d] = A[j];
}
A[j+d] = A[0];
}
}
}
cout << "希尔排序1 shell_sort:" << endl;
print_data1(A, n);
}
// 上面的实现是,每次根据当下的元素,进行插入,要到i=n时,全部子表才有序,
// 能不能直接把子表有序化,再有序下一个子表呢
void shell_sort1(int A[], int n){
cout << "初始序列:" << endl;
print_data1(A, n);
int i,j,d;
//数组下标0不存放实际数据
for (d = n/2; d>=1; d=d/2) {
int counter = 1;
while (counter <= d) {
for (i = d+counter; i<=n; i+=d) {
if (A[i]<=A[i-d]) {//前面的比后面的大,需要交换
A[0] = A[i];
//现在要找位置,并把对应元素后移+d的位置
for (j = i-d; A[0]<A[j]&&j>0; j = j-d) {
A[j+d] = A[j];
}
A[j+d] = A[0];
}
}
counter++;
}
}
cout << "希尔排序2 shell_sort1:" << endl;
print_data1(A, n);
}
//快速排序,居然敢自称快速排序,一定是很牛的算法
//顺序 或者 逆序效率最差。因为没办法没成2个部分 基准是第一个
//优化建议,基准选中间元素,或者随机
//最好时间复杂度是O(n) * O(log2n), 每次递归排序不超过n个元素,*递归深度就是全部的时间复杂度
//最坏时间复杂度是当递归深度=n,所以最坏时间复杂度是O(n2)
//不稳定,因为每次排序就确定了1个位置,如果2个相同元素离很近,排序后的下标又很大,那么先排的元素就去到后面
//平均时间复杂度是O(nlog2n)
int do_quick_sort(int A[], int low, int high);
void quick_sort(int A[], int n, int low, int high){
if (low<high) {
int temp = do_quick_sort(A, low, high);
quick_sort(A, n, low, temp-1);
quick_sort(A, n, temp+1, high);
}
}
int do_quick_sort(int A[], int low, int high){
int temp = A[low];//以low为第一个元素,说明要从high开始对比
while(low<high){
while (low<high && temp<A[high]) {//指针从右向左移动,找到比基准值小的元素,丢到基准值左边
high--;
}
A[low] = A[high];
while (low<high && temp>A[low]) {
low++;
}
A[high] = A[low];
}
A[low] = temp;
return low;
}
// 简单选择排序
void simple_select_sort(int A[], int n){
cout << "初始序列:" << endl;
print_data(A, n);
int min = 0 ;
for (int i = 0; i<n-1; i++) {
min = i;
for (int j = i+1; j<n; j++) {
if (A[min]>A[j]) {
min = j;
}
}
if (i!=min) {
int temp = A[min];
A[min] = A[i];
A[i] = temp;
}
}
cout << "简单选择排序 simple_select_sort:" << endl;
print_data(A, n);
}
void max_heap_adjust(int A[], int k, int n);
// 堆排序
// 完全二叉树
// 建立大根堆的过程,先把给定的初始序列看成完成二叉树,然后对分支结点进行调整,调整到满足大根堆要求
// O(n) + O(nlog2n) = O(nlog2n)
// 不稳定,比如1、2、2。,建堆还是顺序的,2、1、2.但是排序时两个2会对掉,1、2.、2
void max_heap_sort(int A[], int n){
cout << "大根堆初始序列:" << endl;
print_data1(A, n);
//从后往前调整非叶子结点 O(n)
for (int i = n/2; i>0; i--) {
max_heap_adjust(A, i, n);
}
cout << "建立大根堆 build_max_heap:" << endl;
print_data1(A, n);
//时间复杂度 O(nlog2n)
for (int i = 1; i<=n; i++) {
A[0] = A[1];
A[1] = A[n-i+1];
A[n-i+1] = A[0];
max_heap_adjust(A, 1, n-i);
}
cout << "大根堆排序得到递增序列 max_head_sort:" << endl;
print_data1(A, n);
}
void max_heap_adjust(int A[], int k, int n){
A[0] = A[k];
//i是k他的左孩子下标
//小元素下坠
for (int i = 2*k; i<=n; i*=2) {//下一个左孩子下标
if (i<n) {//可能没有右孩子
if (A[i]<A[i+1]) {
i++;
}
}
if (A[0]<A[i]) {
A[k] = A[i];
k = i;
}else{
break;
}
}
A[k] = A[0];
}
void min_heap_adjust(int A[], int k, int n);
void min_heap_sort(int A[], int n){
cout << "小根堆初始序列:" << endl;
print_data1(A, n);
cout << "建立小根堆 build_min_heap:" << endl;
for (int i = n/2; i>0; i--) {//非叶子结点从后往前遍历
min_heap_adjust(A, i, n);
}
print_data1(A, n);
for (int i = n; i>0; i--) {
A[0] = A[i];
A[i] = A[1];
A[1] = A[0];
min_heap_adjust(A, 1, i-1);
}
cout << "小根堆排序得到递减序列 min_heap_sort:" << endl;
print_data1(A, n);
}
void min_heap_adjust(int A[], int k, int n){
A[0] = A[k];
//分支结点肯定有左孩子
for (int i = k*2; i<=n; i*=2) {
if (i<n) {
if (A[i]>A[i+1]) {
i++;
}
}
if (A[0]<A[i]) {
break;
}else{
A[k] = A[i];
k = i;//用于交换
}
}
A[k] = A[0];
}
void min_heap_pop(int A[], int k, int n){
//check A[]是否是小根堆
//逻辑删除下标k元素
A[k] = A[n];
n--;
cout << "n " << n << endl;
min_heap_adjust(A, k, n);
print_data1(A, n);
}
void min_heap_push(){
int A[10]={0,1,2,3,4,5};
int k = 6;
int n = 5;
//check A[]是否是小根堆
//逻辑增加下标k元素
A[0] = A[++n] = -1;
for (int i = k/2; i>0; i/=2) {
if (A[i]>A[0]) {
A[k] = A[i];
k = i;
}
}
A[k] = A[0];
print_data1(A, n);
}
//归并排序
int *B = (int *)malloc(10*sizeof(int));
void do_merge_sort(int A[], int low, int high){
for (int i = low; i<=high; i++) {
B[i] = A[i];
}
int mid = (low+high) / 2;
int i = 0, j = 0, k = 0;
for (i = low, j = mid+1, k = low; i<=mid&&j<=high; k++) {
if (B[i]>B[j]){
A[k] = B[j++];
}else{
A[k] = B[i++];
}
}
while (i<=mid) {
A[k++]=B[i++];
}
while (j<=high) {
A[k++]=B[j++];
}
}
void merge_sort(int A[], int low, int high){
if (low < high) {
//这里类似二叉树后序遍历,把合并的操作放在最后
int mid = (low+high)/2;
merge_sort(A, low, mid);
merge_sort(A, mid+1, high);
do_merge_sort(A, low, high);
}
}
int main(){
cout << "welcome, to my world!" << endl;
cout << "下面求每个初始序列的递增排序:" << endl << endl;
// int A[] = {9,8,7,6,5,4,3,2,1};
int A[] = {49,38,65,97,76,13,27,8,9};
// int A[] = {1,2,3,4,5,6,7,8,9};
insert_sort(A, 9);//传指针
int B[] = {49,38,65,97,76,13,27,8,9};
insert_sort1(B, 9);//传指针
int C[] = {999,49,38,65,97,76,13,27,8,9};//999表示这个位置不放实际数据,下面一样
insert_sort2(C, 9);//传指针
int D[] = {999,49,38,65,97,76,13,27,8,9};
insert_sort3(D, 9);//传指针
int E[] = {49,38,65,97,76,13,27,8,9};
bubble_sort(E, 9);//传指针
int F[] = {999,49,38,65,97,76,13,27,8,9};
shell_sort(F, 9);//传指针
int G[] = {999,49,38,65,97,76,13,27,8,9};
shell_sort1(G, 9);
int H[] = {49,38,65,97,76,13,27,8,9};
cout << "初始序列:";
print_data(H, 9);
quick_sort(H, 9, 0, 8);
cout << "快速排序 quick_sort:" << endl;
print_data(H, 9);
int I[] = {49,38,65,97,76,13,27,8,9};
simple_select_sort(I, 9);
int J[] = {999,49,38,65,97,76,13,27,8,9};
cout << "初始序列:" << endl;
print_data1(J, 9);
max_heap_sort(J, 9);
int K[] = {999,49,38,65,97,76,13,27,8,9};
cout << "初始序列:" << endl;
print_data1(K, 9);
min_heap_sort(K, 9);
int L[] = {49,38,65,97,76,13,27,8,9};
cout << "初始序列:" << endl;
print_data(L, 9);
cout << "归并排序 merge_sort:" << endl;
merge_sort(L, 0, 8);
print_data(L, 9);
return 0;
}
输出
welcome, to my world!
下面求每个初始序列的递增排序:
初始序列:
49 38 65 97 76 13 27 8 9
直接插入排序1 insert_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
直接插入排序2 insert_sort1:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
直接插入排序3 insert_sort2:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
直接插入排序4 insert_sort3:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
冒泡排序 bubble_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
希尔排序1 shell_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
希尔排序2 shell_sort1:
8 9 13 27 38 49 65 76 97
初始序列:49 38 65 97 76 13 27 8 9
快速排序 quick_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
简单选择排序 simple_select_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
大根堆初始序列:
49 38 65 97 76 13 27 8 9
建立大根堆 build_max_heap:
97 76 65 38 49 13 27 8 9
大根堆排序得到递增序列 max_head_sort:
8 9 13 27 38 49 65 76 97
初始序列:
49 38 65 97 76 13 27 8 9
小根堆初始序列:
49 38 65 97 76 13 27 8 9
建立小根堆 build_min_heap:
8 9 13 38 76 65 27 97 49
小根堆排序得到递减序列 min_heap_sort:
97 76 65 49 38 27 13 9 8
初始序列:
49 38 65 97 76 13 27 8 9
归并排序 merge_sort:
8 9 13 27 38 49 65 76 97
Program ended with exit code: 0