常见排序算法解析

一、快速排序
基本思想:
快速排序是一种分治思想的排序算法,它的基本思想是选取一个基准元素,将待排序数组划分为左右两个子数组,其中左边的元素都小于基准元素,右边的元素都大于基准元素,然后再对左右子数组分别进行递归排序,最终得到一个有序的数组。

算法实现:

  1. 选取基准元素pivot,一般选择第一个元素或者随机选择一个元素。
  2. 将数组分为两部分,左边的元素都小于等于pivot,右边的元素都大于pivot。
  3. 对左右两个子数组递归执行步骤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. 取出堆顶元素:将堆顶元素与最后一个元素交换位置,然后将堆的大小减1,再对堆顶元素进行下沉操作,使其重新成为一个大顶堆(或小顶堆)。
  3. 重复步骤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);
    }
}
posted @   生日宴请梅长苏  阅读(47)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
点击右上角即可分享
微信分享提示