常见排序算法解析
一、快速排序
基本思想:
快速排序是一种分治思想的排序算法,它的基本思想是选取一个基准元素,将待排序数组划分为左右两个子数组,其中左边的元素都小于基准元素,右边的元素都大于基准元素,然后再对左右子数组分别进行递归排序,最终得到一个有序的数组。
算法实现:
- 选取基准元素pivot,一般选择第一个元素或者随机选择一个元素。
- 将数组分为两部分,左边的元素都小于等于pivot,右边的元素都大于pivot。
- 对左右两个子数组递归执行步骤1和步骤2,直到子数组的长度为1或者0。
时间复杂度:
快速排序的时间复杂度为O(nlogn),其中n为数组长度。这是因为快速排序每次将数组分为两部分,每部分的长度为n/2,递归的深度为logn,因此时间复杂度为O(nlogn)。
空间复杂度:
快速排序的空间复杂度为O(logn),其中logn为递归的深度。这是因为快速排序每次只需要保存一个基准元素的位置,而每次递归只需要保存左右子数组的起始和结束位置,因此空间复杂度为O(logn)。
稳定性:
快速排序是一种不稳定的排序算法,因为在交换元素的过程中,可能会改变相同元素的相对顺序。
适用场景:
快速排序适用于大规模数据的排序,特别是在数据量较大时,快速排序的效率比较高。同时,快速排序也适用于数据集合中存在重复元素的情况。
快速排序对排序数据的要求不高,可以对任意类型的数据进行排序。但是,如果待排序数组中存在大量重复元素,会影响快速排序的效率。
C++源码:
void quickSort(vector<int>& nums, int left, int right) {
if (left >= right) return;
int pivot = nums[left];
int i = left, j = right;
while (i < j) {
while (i < j && nums[j] >= pivot) j--;
while (i < j && nums[i] <= pivot) i++;
swap(nums[j] , nums[i]);
}
swap(nums[i] , nums[left]);
quickSort(nums, left, i - 1);
quickSort(nums, i + 1, right);
}
二、插入排序
基本思想:
插入排序是一种简单直观的排序算法,其基本思想是将待排序的数据分为已排序和未排序两部分,每次从未排序的数据中取出一个元素,插入到已排序的数据中的正确位置,直到所有元素都被插入到已排序的数据中。
算法实现:
插入排序算法的实现可以采用两种方式:迭代和递归。迭代实现的插入排序算法如下:
void insertionSort(int arr[], int n) {
int i, j, key;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
递归实现的插入排序算法如下:
void insertionSortRec(int arr[], int n) {
if (n <= 1) {
return;
}
insertionSortRec(arr, n - 1);
int last = arr[n - 1];
int j = n - 2;
while (j >= 0 && arr[j] > last) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = last;
}
时间复杂度:
插入排序算法的时间复杂度为O(n2),其中n为待排序数据的数量。在最坏情况下,即待排序数据已经有序时,插入排序算法的时间复杂度为O(n2)。在最好情况下,即待排序数据已经按照从小到大的顺序排好序时,插入排序算法的时间复杂度为O(n)。
空间复杂度:
插入排序算法的空间复杂度为O(1),即不需要额外的空间来存储数据。
稳定性:
插入排序算法是一种稳定的排序算法,即在排序过程中相等的元素的相对位置不会发生改变。
适用场景:
插入排序算法适用于小规模的数据排序,比如几百或几千个元素的排序。对于大规模的数据排序,插入排序算法的效率较低,不如快速排序、归并排序等高效的排序算法。
C++源码:
下面是用C++实现的插入排序算法代码:
#include <iostream>
using namespace std;
void insertionSort(int arr[], int n) {
int i, j, key;
for (i = 1; i < n; i++) {
key = arr[i];
j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
int main() {
int arr[] = { 12, 11, 13, 5, 6 };
int n = sizeof(arr) / sizeof(arr[0]);
insertionSort(arr, n);
cout << "Sorted array: \n";
for (int i = 0; i < n; i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
三、堆排序
堆排序(Heap Sort)是一种基于完全二叉树的排序算法,它的基本思想是将待排序的序列构造成一个大顶堆(或小顶堆),然后依次取出堆顶元素,直到堆为空,取出的元素就是排好序的序列。
算法实现:
- 构建堆:从最后一个非叶子节点开始,将其与其子节点比较,如果子节点比它大(或小),则交换位置,然后继续向下比较,直到该节点为叶子节点为止。然后再处理下一个非叶子节点,直到根节点为止。这样就构建出了一个大顶堆(或小顶堆)。
- 取出堆顶元素:将堆顶元素与最后一个元素交换位置,然后将堆的大小减1,再对堆顶元素进行下沉操作,使其重新成为一个大顶堆(或小顶堆)。
- 重复步骤2,直到堆为空。
时间复杂度:O(nlogn),其中n为待排序序列的长度。其中,构建堆的时间复杂度为O(n),取出堆顶元素并重建堆的时间复杂度为O(logn),因此总的时间复杂度为O(nlogn)。
空间复杂度:O(1),堆排序是原地排序算法,不需要额外的空间。
稳定性:不稳定。因为在构建堆的过程中,可能会交换相同元素的位置,从而破坏相同元素之间的相对顺序。
适用场景:堆排序适用于数据量较大的排序任务,因为它的时间复杂度比较稳定,不会像快速排序那样出现最坏情况,而且不需要额外的空间。
C++源码实现:
void heapify(vector<int>& nums, int i, int n) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && nums[l] > nums[largest])
largest = l;
if (r < n && nums[r] > nums[largest])
largest = r;
if (largest != i) {
swap(nums[i], nums[largest]);
heapify(nums, largest, n);
}
}
void heapSort(vector<int>& nums) {
int n = nums.size();
for (int i = n / 2 - 1; i >= 0; i--)
heapify(nums, i, n);
for (int i = n - 1; i >= 0; i--) {
swap(nums[0], nums[i]);
heapify(nums, 0, i);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器