参考王道《2023年数据结构考研复习指导》
注意:本程序中,带哨兵是指arr[0]该存储单元不用于存储数据。
#include <iostream>
#define N 20 // 数组大小
void InsertSort(int arr[], int n);
void BinaryInsertSort(int arr[], int n);
void ShellSort(int arr[], int n);
void ShellSort2(int arr[], int n);
void BubbleSort(int arr[], int n);
void QuickSort(int arr[], int low, int high);
int Partition(int arr[], int low, int high);
void SelectSort(int arr[], int n);
void BuildMaxHeap(int arr[], int len);
void HeadAdjust(int arr[], int k, int len);
void HeapSort(int arr[], int len);
void Merge(int arr[], int low, int mid, int high, int n);
void MergeSort(int arr[], int low, int high, int n);
int main() {
srand((unsigned)time(NULL)); // 初始化随机数种子
int arr[N+1]; // arr[0]为哨兵
for (int i = 1; i <= N; ++i) { // 随机生成数组
arr[i] = rand() % 100 + 1;
}
std::cout << "数组初始状态为:" << std::endl;
for (int i = 1; i <= N; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// InsertSort(arr, N);
// BinaryInsertSort(arr, N);
// ShellSort(arr, N);
// ShellSort2(arr, N);
HeapSort(arr, N);
std::cout << "数组排序后为:" << std::endl;
for (int i = 1; i <= N; ++i) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
int arr2[N]; // arr2[0]为哨兵
for (int i = 0; i < N; ++i) { // 随机生成数组
arr2[i] = rand() % 100 + 1;
}
std::cout << "数组初始状态为:" << std::endl;
for (int i = 0; i < N; ++i) {
std::cout << arr2[i] << " ";
}
std::cout << std::endl;
// BubbleSort(arr2, N);
// QuickSort(arr2, 0, N - 1);
// SelectSort(arr2, N);
MergeSort(arr2, 0, N - 1, N);
std::cout << "数组排序后为:" << std::endl;
for (int i = 0; i < N; ++i) {
std::cout << arr2[i] << " ";
}
std::cout << std::endl;
system("pause");
return 0;
}
// 带哨兵的插入排序(数组)
void InsertSort(int arr[], int n) {
int i, j;
for (i = 2; i <= n; ++i) {
if (arr[i] < arr[i - 1]) {
arr[0] = arr[i];
for (j = i - 1; arr[0] < arr[j]; --j) {
arr[j + 1] = arr[j];
}
arr[j + 1] = arr[0];
}
}
}
// 优化——带哨兵的折半插入排序(数组)
void BinaryInsertSort(int arr[], int n) {
int i, j, low, high, mid;
for (i = 2; i <= n; ++i) {
if (arr[i] < arr[i - 1]) {
arr[0] = arr[i];
// 折半查找
low = 1;
high = i - 1;
while (low <= high) {
mid = (low + high) / 2;
if (arr[mid] > arr[0]) { // 当中间值大于目标值
high = mid - 1;
} else { // 当中间值小于或等于目标值(为了保证排序的稳定性,即使目标值与中间值相对,目标值也必须放在中间值右边)
low = mid + 1;
}
}
for (j = i - 1; j >= low; --j) { // 后移
arr[j + 1] = arr[j];
}
arr[j + 1] = arr[0];
}
}
}
// 带哨兵的希尔排序(数组)
// 和王道书上略有不同,王道书是在分组间交替排序
// 这里是对一个分组排序完后,再对其他分组排序,更符合希尔排序
void ShellSort(int arr[], int n) {
int d, i, j, k;
for (d = n / 2; d > 0; d = d / 2) {
for (i = 1; i <= d; ++i) {
for (j = i + d; j <= n; ++j) {
if (arr[j] < arr[j - d]) {
arr[0] = arr[j];
for (k = j - d; k > 0 && arr[0] < arr[k]; k -= d) {
arr[k + d] = arr[k];
}
arr[k + d] = arr[0];
}
}
}
}
}
// 王道版带哨兵的希尔排序(数组)
void ShellSort2(int arr[], int n) {
int d, i, j;
for (d = n / 2; d >= 1; d = d / 2) {
for (i = d + 1; i <= n; ++i) {
if (arr[i] < arr[i - d]) {
arr[0] = arr[i];
for (j = i - d; j > 0 && arr[0] < arr[j]; j -= d) {
arr[j + d] = arr[j];
}
arr[j + d] = arr[0];
}
}
}
}
// 冒泡排序(无哨兵)
void BubbleSort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
bool flag = false;
for (int j = n - 1; j > i; --j) {
if (arr[j - 1] > arr[j]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
flag = true;
}
}
if (!flag) { // 如果没有发生交换,说明表已经有序
return ;
}
}
}
// 快速排序(无哨兵)
void QuickSort(int arr[], int low, int high) {
if (low < high) {
int pivotops = Partition(arr, low, high); // 划分
QuickSort(arr, low, pivotops - 1); // 对左子表进行快速排序
QuickSort(arr, pivotops + 1, high); // 对右子表进行快速排序
}
}
// 用第一个元素将待排序序列划分成左右两个部分
int Partition(int arr[], int low, int high) {
int pivot = arr[low]; // 用第一个元素作为枢轴
while (low < high) {
while (low < high && arr[high] >= pivot) {
--high;
}
arr[low] = arr[high]; // 比枢轴小的移动到左边
while (low < high && arr[low] <= pivot) {
++low;
}
arr[high] = arr[low]; // 比枢轴大的移动到右边
}
arr[low] = pivot;
return low; // 返回存放枢轴的最终位置
}
// 简单选择排序(无哨兵)
void SelectSort(int arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
int min = i;
for (int j = i + 1; j < n; ++j) {
if (arr[j] < arr[min]) {
min = j;
}
}
if (min != i) {
int temp = arr[i];
arr[i] = arr[min];
arr[min] = temp;
}
}
}
// 建立大根堆
void BuildMaxHeap(int arr[], int len) { // len为数组长度
for (int i = len / 2; i > 0; --i) { // 从后往前调整所有非终端结点
HeadAdjust(arr, i, len);
}
}
// 将以k为根的子树调整为大根堆
void HeadAdjust(int arr[], int k, int len) { // len为数组长度
arr[0] = arr[k]; // arr[0]暂存子树的根节点
for (int i = 2 * k; i <= len; i *= 2) { // 沿key较大的子结点向下筛选
if (i < len && arr[i] < arr[i + 1]) { // 如果存在右结点且右结点大于左结点
++i;
}
if (arr[0] >= arr[i]) { // 如果根结点大于等于子结点,则说明符合大根堆
break;
} else { // 否则,根结点与子结点互换
arr[k] = arr[i];
k = i; // 修改k值,以便继续向下筛选
}
}
arr[k] = arr[0]; // 被筛选结点的值放入最终位置
}
// 堆排序(带哨兵)
void HeapSort(int arr[], int len) {
BuildMaxHeap(arr, len); // 初始建堆
for (int i = len; i > 1; --i) {
int temp = arr[1]; // 堆顶元素和堆底元素交换
arr[1] = arr[i];
arr[i] = temp;
HeadAdjust(arr, 1, i - 1); // 将剩余元素调整成大根堆
}
}
// arr[low ... mid] 和 arr[mid+1 ... high]各自有序,将两个部分归并
void Merge(int arr[], int low, int mid, int high, int n) {
int* temp = (int*)malloc(n*sizeof(int)); // 辅助数组
int i, j, k;
for (i = low; i <= high; ++i) { // 将arr[low ... high]中的元素复制到temp中
temp[i] = arr[i];
}
for (i = low, j = mid + 1, k = i; i <= mid && j <= high; k++) {
if (temp[i] <= temp[j]) { // 保证稳定性
arr[k] = temp[i++];
} else {
arr[k] = temp[j++];
}
}
while (i <= mid) { // 将剩余排序好的元素放入arr
arr[k++] = temp[i++];
}
while (j <= high) { // 将剩余排序好的元素放入arr
arr[k++] = temp[j++];
}
free(temp);
}
// 2路归并排序(无哨兵)
void MergeSort(int arr[], int low, int high, int n) {
if (low < high) {
int mid = (low + high) / 2; // 从中间划分
MergeSort(arr, low, mid, n); // 对左半部分归并排序
MergeSort(arr, mid + 1, high, n); // 对右半部分归并排序
Merge(arr, low, mid, high, n); // 归并
}
}