十大排序算法之快速排序、堆排序、归并排序

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

面试中常见的三种排序算法,通俗易懂。

一、快速排序

快速排序主要步骤如下:
(1)首先选定一个基准值pivot,然后将数组元素逐个与pivot比较,从而将数组分成左右两部分。
(2)一次比较过后将大于或等于pivot的数据集中到数组右边,小于pivot的数据集中到数组的左边。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个pivot,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,对左右两边元素进行递归排序,当左右两边递归完成后就完成了对整个数组的排序。
(5)快速排序优化:快速排序最坏时间复杂度为O(Nlog(N)),即当数组元素基本有序时快速排序会退化成简单插入排序。为了避免这种情况可以对快速排序进行优化(参考中国大学MOOC浙江大学陈越),通过median3函数,每次排序前将pivot位置上的元素置为三个数的中位数。

	void median3(vector<int>& arr, int left, int right) {
		/*将pivot位置上元素置为三者中的中位数*/
        int mid = (left + right) / 2;
        if (arr[left] > arr[mid]) swap(arr[left], arr[mid]);
        if (arr[right] < arr[mid]) swap(arr[right], arr[mid]);
        if (arr[left] < arr[mid]) swap(arr[left], arr[mid]);
        }
	void quickSort(vector<int>& arr, int left, int right) {
        if (left >= right) return;
        median3(arr, left, right);
        
        int i = left + 1; int j = right;
        while (true) {
            while (i <= j && arr[i] <= arr[left]) i++;
            while (i <= j && arr[j] >= arr[left]) j--;
            if (i >= j) break;
            swap(arr[i], arr[j]);
        }
        swap(arr[left], arr[j]);
        quickSort(arr, left, j - 1);
        quickSort(arr, j + 1, right);
    }
    void Qsort(vector<int>& arr){ /*统一接口*/
    	quickSort(arr, 0, arr.size() - 1);
    }

二、堆排序

堆排序主要流程如下(以最大堆为例)
(1)最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
(2)创建最大堆(Build Max Heap):将堆中的所有数据重新排序
(3)堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

void heapBuild(vector<int>& nums, int parent, int end_Index) {/*调整最大堆*/

    int left_c = 2 * parent + 1; int right_c = 2 * parent + 2; //左右孩子节点坐标
    int max_c = parent; //用于记录左右孩子中较大的坐标

    if (left_c <= end_Index && nums[left_c] > nums[max_c]) max_c = left_c;
    if (right_c <= end_Index && nums[right_c] > nums[max_c]) max_c = right_c;

    if (max_c != parent) { //如果父节点比孩子节点小,则调整使父子节点,其满足最大堆
        swap(nums[parent], nums[max_c]);
        heapBuild(nums, max_c, end_Index); //循环调整
    }
}
void heapSort(vector<int>& nums) {/*堆排序*/
    if (nums.size() == 1 || nums.empty()) return; //特例

    int end = nums.size() - 1; int parent = (end - 1) / 2;

    for (int i = parent; i >= 0; i--) { // 从最后一个父节点开始建立最大堆
        heapBuild(nums, i, end);
    }

    for (int ind = end; ind >= 0; ind--) { // 每次将最大值元素移动到数组末尾
        swap(nums[ind], nums[0]);
        heapBuild(nums, 0, ind - 1); // 每次移动以后都要重新维护最大堆
    }

二、归并排序

归并排序的主要思想是分治法。主要过程是:

(1) 将n个元素从中间切开,分成两部分。(左边可能比右边多1个数) 将步骤1分成的两部分
(2) 再分别进行递归分解。直到所有部分的元素个数都为1。
(3)从最底层开始逐步合并两个排好序的数列(合并两个有序数组)。

void MergeSort_recursion(vector<int>& input_vec, int start, int end) { /*递归*/
    if (start >= end) return;

    int mid = (start + end) >> 1;
    MergeSort_recursion(input_vec, start, mid);
    MergeSort_recursion(input_vec, mid + 1, end);

    int start1 = start, end1 = mid, start2 = mid + 1, end2 = end;
    vector<int> temp;
    while (start1 <= mid && start2 <= end)
        temp.push_back(input_vec[start1] < input_vec[start2] ? input_vec[start1++] : input_vec[start2++]);
    while (start1 <= mid)
        temp.push_back(input_vec[start1++]);
    while (start2 <= end)
        temp.push_back(input_vec[start2++]);
    for (int k = 0; k < temp.size(); k++)
        input_vec[start + k] = temp[k];
}

void MergeSort_iterate(vector<int>& input_vec) { /*迭代*/
    for (int step = 1; step <= input_vec.size(); step *= 2) {
        int len = input_vec.size() % (2 * step) == 0 ? input_vec.size() : input_vec.size() + 2 * step;
        
        for (int j = 0; j < len; j += 2 * step) {
            vector<int> temp;
            int start1 = j, start2 = j + step;
            while (start1 < j + step && start1 < input_vec.size() &&
                start2 < j + step * 2 && start2 < input_vec.size())
                temp.push_back(input_vec[start1] < input_vec[start2] ? input_vec[start1++]
                    : input_vec[start2++]);
            while (start1 < j + step && start1 < input_vec.size())
                temp.push_back(input_vec[start1++]);
            while (start2 < j + step * 2 && start2 < input_vec.size())
                temp.push_back(input_vec[start2++]);
            for (int k = 0; k < temp.size(); k++)
                input_vec[j + k] = temp[k];
        }
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值