目录
算法逻辑可能并不严谨,欢迎各位大佬批评指正!
一、问题分析及数据结构设计
1 问题分析
- 输入一组关键字序列,需要实现简单选择排序、直接插入排序和冒泡排序算法。这些排序算法都是基于比较的排序算法,对于给定的关键字序列,通过比较关键字的大小来实现排序。
- 对于希尔排序算法,也是一种插入排序算法,但是与直接插入排序不同的是,希尔排序是通过将序列划分为若干个子序列,对每个子序列进行插入排序,然后逐步减小子序列的长度来实现排序。
- 快速排序是一种基于比较的排序算法,通过选取一个基准元素,将序列中的元素分为比基准元素小和比基准元素大的两部分,然后递归地对两部分进行排序,最终实现整个序列的排序。
- 堆排序是一种基于堆数据结构的排序算法,通过将序列构建成一个最大堆或最小堆,然后不断将堆顶元素与末尾元素交换,并重新调整堆,最终实现整个序列的排序。
- 折半插入排序是一种插入排序算法,与直接插入排序不同的是,折半插入排序是通过二分查找找到插入位置,从而减少比较的次数,提高算法的效率。
- 最后需要编写一个主函数,设计一个简单的菜单,以便用户能够选择调试上述算法。在菜单中,用户选择要使用的排序算法,最终输出排序结果。
2 数据结构设计
- 数组(Array):使用数组来存储关键字序列。数组是一种线性数据结构,通过索引访问元素,用于实现简单选择排序、直接插入排序和冒泡排序等算法。
- 链表(Linked List):链表是一种动态数据结构,通过指针将节点连接起来。使用链表来实现希尔排序算法。希尔排序需要分割序列为子序列,链表的插入和删除操作更高效。
- 递归或栈(Stack):在实现快速排序算法时,使用递归或栈来处理递归调用。快速排序算法需要逐步划分子序列,通过递归或栈来保存划分的子序列可以有效实现该算法。
- 堆(Heap):堆是一种二叉树结构,用于实现堆排序算法。在堆排序中,需要构建一个最大堆或最小堆来实现排序操作。
- 二分查找(Binary Search):折半插入排序需要使用二分查找来确定插入的位置。使用有序数组或有序链表来实现折半查找。
- 主函数中的菜单使用简单的控制流和用户输入来实现,不需要特定的数据结构。
二、算法设计
1 简单选择排序
1.1 算法思想
算法思想: 每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在起始位置,然后再从剩余未排序元素中继续选择最小(或最大)元素,依次类推,直到所有元素排序完毕。
时间复杂度: O(n^2),其中n为待排序序列的长度。
空间复杂度: O(1)。
1.2 算法流程图
1.3 算法
// 简单选择排序实现
void simpleSelectionSort(std::vector<int>& arr) {
for (int i = 0; i < arr.size() - 1; ++i) {
int minIndex = i;
for (int j = i + 1; j < arr.size(); ++j) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
std::swap(arr[i], arr[minIndex]);
}
}
2 直接插入排序
2.1 算法思想
算法思想: 通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
时间复杂度: 最好情况O(n),最坏情况O(n2),平均情况O(n2)。
空间复杂度: O(1)。
2.2 算法流程图
2.3 算法
// 直接插入排序实现
void directInsertionSort(std::vector<int>& arr) {
for (int i = 1; i < arr.size(); ++i) {
int temp = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > temp) {
arr[j + 1] = arr[j];
--j;
}
arr[j + 1] = temp;
}
}
3 冒泡排序
3.1 算法思想
算法思想: 比较相邻的元素。如果第一个比第二个大,就交换它们两个;否则就保持不变。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数。
时间复杂度: 最好情况O(n),最坏情况O(n2),平均情况O(n2)。
空间复杂度: O(1)。
3.2 算法流程图
3.3 算法
//冒泡排序
void bubbleSort(std::vector<int>& arr) {
for (int i = 0; i < arr.size() - 1; ++i) {
for (int j = 0; j < arr.size() - 1 - i; ++j) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
4 希尔排序
4.1 算法思想
算法思想: 是插入排序的一种更高效的改进版本,先将整个待排序的记录序列分割成为若干子,部分序列分别进行直接插入排序,待整个序列“基本有序”时,再对全体记录进行一次直接插入排序。
时间复杂度: 取决于增量序列的选择,最坏情况达到O(n^2),最好的情况是O(n log^2 n)。
空间复杂度: O(1)。
4.2 算法流程图
4.3 算法
//希尔排序
void shellSort(std::vector<int>& arr) {
for (int gap = arr.size() / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.size(); ++i) {
int temp = arr[i];
int j = i;
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
5 快速排序
5.1 算法思想
算法思想: 通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,然后分别对这两部分记录继续进行排序,直到整个序列有序。
时间复杂度: 平均时间复杂度O(n log n),最坏情况O(n^2)。
空间复杂度: 最好情况O(log n),最坏情况O(n)。
5.2 算法流程图
5.3 算法
//快速排序
void quickSort(std::vector<int>& arr, int left, int right) {
if (left < right) {
int pivot = arr[left];
int low = left;
int high = right;
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;
quickSort(arr, left, low - 1);
quickSort(arr, low + 1, right);
}
}
6 堆排序
6.1 算法思想
算法思想: 将待排序的序列构造成一个大顶堆,此时整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余元素重新调整为大顶堆,再次将根节点与末尾元素交换,反复执行直到整个序列有序。
时间复杂度: O(n log n)。
空间复杂度: O(1)。
6.2 算法流程图
6.3 算法
//堆排序实现方法
void heapify(std::vector<int>& arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest]) {
largest = l;
}
if (r < n && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
std::swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
//堆排序
void heapSort(std::vector<int>& arr) {
int n = arr.size();
for (int i = n / 2 - 1; i >= 0; --i) {
heapify(arr, n, i);
}
for (int i = n - 1; i > 0; --i) {
std::swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
7 折半插入排序
7.1 算法思想
算法思想: 利用二分查找的思想来减少比较次数,将直接插入排序中的线性查找改为二分查找,从而减少比较的次数
时间复杂度: 最好情况O(n log n),最坏情况O(n^2)。
空间复杂度: O(1)。
7.2 算法流程图
7.3 算法
//折半插入排序
void binaryInsertionSort(std::vector<int>& arr) {
for (int i = 1; i < arr.size(); ++i) {
int temp = arr[i];
int left = 0;
int right = i - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] < temp) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
for (int j = i - 1; j >= left; --j) {
arr[j + 1] = arr[j];
}
arr[left] = temp;
}
}