【c++插入排序】对待排序序列使用直接插入排序算法进行排序,输出每一趟排序后的结果

涉及知识点

关键词:数据结构 顺序表 排列

本文所用函数参考书籍:《数据结构(c语言版 第2版)》严蔚敏 版本


插入排序

插入排序的基本思想和步骤:

  1. 初始状态:将第一个元素视为已排序序列
  2. 选择未排序序列的第一个元素:将未排序序列中的第一个元素视为当前需要插入的元素。
  3. 在已排序序列中从后向前扫描: 将当前元素与已排序序列中的元素依次比较,找到合适的位置插入。
  4. 插入元素: 将当前元素插入到已排序序列的适当位置,使得插入后的序列仍然保持有序。
  5. 重复步骤2~4: 重复以上步骤,直到未排序序列为空。

算法核心在于对每个未排序的元素,将其与已排序序列中的元素逐个比较。时间复杂度为 O(n^2),是一种稳定的算法。


【问题描述】对待排序序列使用直接插入排序算法进行排序,输出每一趟排序后的结果
【输入形式】
序列元素个数
序列
【输出形式】 每一趟排序后的结果
【样例输入】
6
18 6 28 93 46 55
【样例输出】
6 18 28 93 46 55
6 18 28 93 46 55
6 18 28 93 46 55
6 18 28 46 93 55
6 18 28 46 55 93


  • 法一:参照课本结构体写法·
#include<iostream>
using namespace std;

#define MAXSIZE 20
typedef int KeyType;
typedef struct{
	KeyType key;
	//InfoType otherinfo;	意思是这里还可以依据实际情况继续补充所需类型,本题不需要额外补充 
}RedType;//记录类型
typedef struct{
	RedType r[MAXSIZE+1];	//r[0]做哨兵,所以再额外加一个位置
	int length; 
}SqList;//顺序表类型 


//对顺序表L进行插入排序(从后往前顺序比较) 
void InsertSort(SqList &L)
{
	int i,j;	//i表示正在处理的插入元素,j作移动下标
	for(i=2;i <= L.length;++i){ 	//0号作哨兵,1号默认有序,2号开始插入 
		if(L.r[i].key < L.r[i-1].key){	//判断是否需要插入该元素
			L.r[0] = L.r[i];	//复制该元素为哨兵 
			L.r[i] = L.r[i-1];	//将前一个元素后移
			for(j=i-2;L.r[0].key < L.r[j].key;--j){
				L.r[j+1] = L.r[j];	//若再前一个也比i元素大,则继续后移
			} 
			//经过j循环,此时j指向的元素 <= i元素,则i插入j+1位置 
			L.r[j+1] = L.r[0];	
		}
		
		//输出
		for(int k=1;k<=L.length;k++){
			cout << L.r[k].key << " ";
		}
		cout << endl;
	}	 
 } 
 
 
 
 int main(){
 	SqList L;
 	
 	cin >> L.length;
 	for(int i=1;i<=L.length;i++){
 		cin >> L.r[i].key;
	 }
	 
 	InsertSort(L);
 	
 	return 0;
 }
  • 法二:使用普通数组
#include<iostream>
using namespace std;

#define MAXSIZE 20
typedef int KeyType;

// 对数组arr进行插入排序(从前往后顺序比较)
void InsertSort(KeyType arr[], int length) {
    int i, j;    // i表示正在处理的插入元素,j作移动下标
    for (i = 1; i < length; ++i) {    // 默认第一个元素是有序的,从第二个元素开始插入
        if (arr[i] < arr[i - 1]) {    // 判断是否需要插入该元素
            KeyType temp = arr[i];    // 备份当前元素
            j = i - 1;
            while (j >= 0 && temp < arr[j]) {
                arr[j + 1] = arr[j];    // 将前一个元素后移
                j--;
            }
            // 经过循环,此时temp >= arr[j],则temp插入j+1位置
            arr[j + 1] = temp;
        }
        // 输出每一趟排序后的结果
        for (int k = 0; k < length; k++) {
            cout << arr[k] << " ";
        }
        cout << endl;
    }
}

int main() {
    int length;

    cin >> length;
    KeyType arr[MAXSIZE];

    for (int i = 0; i < length; i++) {
        cin >> arr[i];
    }

    InsertSort(arr, length);

    return 0;
}


扩展:如果是在一个已经有序序列中,插入一个元素,使其依然有序,则插入排序可改写为:

#include<iostream>
using namespace std;

#define MAXSIZE 20
typedef int KeyType;

// 在有序序列中插入元素,使其依然有序
void InsertIntoSorted(KeyType arr[], int& length, KeyType element) {
    int i = length - 1;

    // 从后往前遍历有序序列,找到合适的位置插入新元素
    while (i >= 0 && element < arr[i]) {
        arr[i + 1] = arr[i];  // 将比新元素大的元素后移
        i--;
    }

    // 插入新元素
    arr[i + 1] = element;
    length++;

    // 输出有序序列
    for (int k = 0; k < length; k++) {
        cout << arr[k] << " ";
    }
    cout << endl;
}

int main() {
    int length;

    cin >> length;
    KeyType arr[MAXSIZE];

    for (int i = 0; i < length; i++) {
        cin >> arr[i];
    }

    KeyType element;
    cin >> element;

    InsertIntoSorted(arr, length, element);

    return 0;
}


折半插入排序

在此基础上,可以再小小的优化成折半插入排序(Binary Insertion Sort)即,二分查找+插入排序=折半插入排序

折半插入排序的基本思想和步骤:

  1. 初始状态: 将第一个元素视为已排序序列。
  2. 选择未排序序列的第一个元素: 将未排序序列中的第一个元素视为当前需要插入的元素。
  3. 在已排序序列中进行折半查找: 使用二分查找的方式在已排序序列中找到合适的位置插入。
  4. 插入元素: 将当前元素插入到已排序序列的适当位置,使得插入后的序列仍然保持有序。
  5. 重复步骤2~4: 重复以上步骤,直到未排序序列为空。

折半插入排序依旧是一种稳定的算法。时间复杂度O(n^2),空间复杂度O(1)

#include<iostream>
using namespace std;

#define MAXSIZE 20
typedef int KeyType;
typedef struct {
    KeyType key;
} RedType;  //记录类型
typedef struct {
    RedType r[MAXSIZE + 1];    //r[0]做哨兵,所以再额外加一个位置
    int length;
} SqList;  //顺序表类型

// 对顺序表L进行折半插入排序
void BinaryInsertSort(SqList& L) {
    for (int i = 2; i <= L.length; ++i) {
        if (L.r[i].key < L.r[i - 1].key) {
            L.r[0] = L.r[i];  // 复制该元素为哨兵
            int left = 1, right = i - 1;

            // 使用二分查找找到插入位置
            while (left <= right) {
                int mid = (left + right) / 2;
                if (L.r[0].key < L.r[mid].key) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }

            // 将元素插入到合适的位置
            for (int j = i - 1; j >= left; --j) {
                L.r[j + 1] = L.r[j];
            }
            L.r[left] = L.r[0];
        }

        // 输出每一趟排序后的结果
        for (int k = 1; k <= L.length; k++) {
            cout << L.r[k].key << " ";
        }
        cout << endl;
    }
}

int main() {
    SqList L;

    cin >> L.length;
    for (int i = 1; i <= L.length; i++) {
        cin >> L.r[i].key;
    }

    BinaryInsertSort(L);

    return 0;
}


希尔排序

希尔排序的基本思想是通过将序列分割成若干子序列,对子序列进行插入排序,最后再对整个序列进行一次插入排序。希尔排序是一种不稳定的排序算法,其时间复杂度取决于增量序列的选择。希尔排序的平均时间复杂度为 O(n log n)。

希尔排序的具体步骤如下:

  1. 选择增量: 选择一个增量序列,通常初始增量设为序列长度的一半,之后每次缩小为上一次的一半。
  2. 分割序列: 将序列分割成若干子序列,每个子序列由相隔特定增量的元素组成。
  3. 对每个子序列进行插入排序: 对每个子序列进行插入排序,使得每个子序列都变得部分有序。
  4. 缩小增量: 缩小增量,重复步骤2和步骤3,直到增量为1。
  5. 对整个序列进行插入排序: 最后一步是对整个序列进行一次插入排序,此时序列已经相对较为有序,插入排序的工作量较小。
#include<iostream>
using namespace std;

#define MAXSIZE 20
typedef int KeyType;
typedef struct {
    KeyType key;
} RedType;  //记录类型
typedef struct {
    RedType r[MAXSIZE + 1];    //r[0]做哨兵,所以再额外加一个位置
    int length;
} SqList;  //顺序表类型

// 对顺序表L进行希尔排序
void ShellSort(SqList& L) {
    int gap, i, j;
    for (gap = L.length / 2; gap > 0; gap /= 2) {
        for (i = gap + 1; i <= L.length; ++i) {
            if (L.r[i].key < L.r[i - gap].key) {
                L.r[0] = L.r[i];  // 复制该元素为哨兵
                for (j = i - gap; j > 0 && L.r[0].key < L.r[j].key; j -= gap) {
                    L.r[j + gap] = L.r[j];
                }
                L.r[j + gap] = L.r[0];
            }
        }

        // 输出每一趟排序后的结果
        for (int k = 1; k <= L.length; k++) {
            cout << L.r[k].key << " ";
        }
        cout << endl;
    }
}

int main() {
    SqList L;

    cin >> L.length;
    for (int i = 1; i <= L.length; i++) {
        cin >> L.r[i].key;
    }

    ShellSort(L);

    return 0;
}


文章多用于作者对自身所学知识的梳理和记录。
如能帮到你,不胜荣幸。

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值