涉及知识点
关键词:数据结构 顺序表 排列
本文所用函数参考书籍:《数据结构(c语言版 第2版)》严蔚敏 版本
插入排序
插入排序的基本思想和步骤:
- 初始状态:将第一个元素视为已排序序列
- 选择未排序序列的第一个元素:将未排序序列中的第一个元素视为当前需要插入的元素。
- 在已排序序列中从后向前扫描: 将当前元素与已排序序列中的元素依次比较,找到合适的位置插入。
- 插入元素: 将当前元素插入到已排序序列的适当位置,使得插入后的序列仍然保持有序。
- 重复步骤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)即,二分查找+插入排序=折半插入排序
折半插入排序的基本思想和步骤:
- 初始状态: 将第一个元素视为已排序序列。
- 选择未排序序列的第一个元素: 将未排序序列中的第一个元素视为当前需要插入的元素。
- 在已排序序列中进行折半查找: 使用二分查找的方式在已排序序列中找到合适的位置插入。
- 插入元素: 将当前元素插入到已排序序列的适当位置,使得插入后的序列仍然保持有序。
- 重复步骤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)。
希尔排序的具体步骤如下:
- 选择增量: 选择一个增量序列,通常初始增量设为序列长度的一半,之后每次缩小为上一次的一半。
- 分割序列: 将序列分割成若干子序列,每个子序列由相隔特定增量的元素组成。
- 对每个子序列进行插入排序: 对每个子序列进行插入排序,使得每个子序列都变得部分有序。
- 缩小增量: 缩小增量,重复步骤2和步骤3,直到增量为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 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;
}
文章多用于作者对自身所学知识的梳理和记录。
如能帮到你,不胜荣幸。