每日一题:排序数组(冒泡,插入,选择,快排,归并,堆排)

给你一个整数数组 nums,请你将该数组升序排列。

简单的排序题,顺便总结一下常用的排序算法。
LeetCode快排/堆排/归并题解

快速排序

最常见的排序,面试也比较喜欢问。注意快排中的partition操作可以用来求第K
大的数字。
快排的思路是每次迭代把小于(大于)某个元素的数全部放在它前面,把大于(小于)某个元素的数全部放在它后面。
快速排序是不稳定排序

/*
快速排序
*/
class Solution {
public:
    int myPartition(vector<int>& nums, int high, int low){
        int pivot = nums[high], local = low - 1;
        for (int i = low; i < high; i++){
            if (nums[i] < pivot){
                swap(nums[i], nums[++local]);
            }
        }
        swap(nums[high], nums[++local]);
        return local;
    }

    void quickSort(vector<int>& nums, int high, int low){
        if (low < high){
            int middel = myPartition(nums, high, low);
            quickSort(nums, high, middel + 1);
            quickSort(nums, middel-1, low);
        }
    }

    vector<int> sortArray(vector<int>& nums) {
        vector<int> ans(nums);
        quickSort(ans, ans.size()-1, 0);
        return ans;
    }
};

时间复杂度:O(nlogn);(快排的时间复杂度不固定,有最优时间复杂度和最差时间复杂度)
空间复杂度:O(1);(快速排序可以是原地排序算法);

冒泡排序

每次把前面最大的元素放在最后,冒泡排序是稳定排序。

/*
冒泡排序
*/
class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        vector<int> ans(nums);
        int arrLen = ans.size();
        for (int i = arrLen - 1; i > 0; i--){
            for (int j = 0; j < i; j++){
                if (ans[j] > ans[j+1]){
                    swap(ans[j], ans[j+1]);
                }
            }
        }
        return ans;
    }
};

时间复杂度:O(n^2);
空间复杂度:O(1);(冒泡排序可以是原地排序算法)

插入排序

每次把一个新的数插入到一个新的序列中,插入排序是稳定排序。

/*
插入排序
*/
class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        vector<int> ans(nums);
        int arrLen = ans.size();
        for (int i = 1; i < arrLen; i++){
            int j;
            for (j = 0; j < i; j++){
                if (ans[i] <= ans[j]){
                    break;
                }
            }
            int temp = ans[i];
            for (int k = i; k > j; k--){
                ans[k] = ans[k-1];
            }
            ans[j] = temp;
        }
        return ans;
    }
};

时间复杂度:O(n^2);
空间复杂度:O(1);(可以是原地排序算法);

选择排序

每次从剩余的元素选择出一个最大/最小的元素放在当前位置。选择排序不是稳定排序。

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        vector<int> ans(nums);
        for (int i = 0; i < ans.size() - 1; i++){
            int index = i, minNum = ans[i];
            for (int j = i + 1; j < ans.size(); j++){
                if (ans[j] < minNum){
                    index = j;
                    minNum = ans[j];
                }
            }
            swap(ans[i], ans[index]);
        }
        return ans;
    }
};

时间复杂度:O(n^2);
空间复杂度:O(1);(可以是原地排序算法);

归并排序

归并排序利用了分治的思想来对序列尽心排序。对一个长为n的待排序的序列,先将其分解成两个长度为 n 2 \frac{n}{2} 的子序列。每次先递归调用函数使两个子序列有序,然后我们再线性合并两个有序的子序列使整个序列有序。归并排序是稳定的排序算法。

class Solution {
    vector<int> tmp; // 因为要采用递归,所以把用于归并的tmp数组放在递归函数外部

    void mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right) return;
        int mid = (left + right) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);
        int i = left, j = mid + 1;
        int cnt = 0;
        while (i <= mid && j <= right){
            if (nums[i] < nums[j]) {
                tmp[cnt++] = nums[i++];
            }
            else {
                tmp[cnt++] = nums[j++];
            }
        }

        while (i <= mid){
            tmp[cnt++] = nums[i++];
        }
        while (j <= right){
            tmp[cnt++] = nums[j++];
        }
        for (int i = 0; i < right - left + 1; i++){
            nums[i+left] = tmp[i];
        }
    }

public:
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize((int)nums.size(), 0);
        mergeSort(nums, 0, (int)nums.size() - 1);
        return nums;
    }
};

时间复杂度:O(nlogn);
空间复杂度:O(n);(需要辅助数组tmp);

堆排序

先维护一个最大堆,然后每次把堆顶元素挪出堆,放在当前数字序列末尾,这样它就被放到了排序后正确的位置上。因为堆的维护牵扯交换操作,所以堆排序不是稳定排序。一般来说堆排序需要三个函数:
1.建堆;
2.堆化
3.总的排序函数;

class Solution {
    void maxHeapify(vector<int>& nums, int i, int len) {
        for (; (i << 1) + 1 <= len;) {
            int lson = (i << 1) + 1;
            int rson = (i << 1) + 2;
            int large;
            // 堆排序常用的三个元素的大小比较顺序;
            if (lson <= len && nums[lson] > nums[i]) {
                large = lson;
            }
            else {
                large = i;
            }
            if (rson <= len && nums[rson] > nums[large]) {
                large = rson;
            }

            if (large != i) {
                swap(nums[i], nums[large]);
                i = large;
            }
            else break; //堆化每次只操作一个元素,所以直接break;
        }
    }

    void buildMaxHeap(vector<int>& nums, int len) {
        // 采用从后往前堆化;
        for (int i = len / 2; i >= 0; i--){
            maxHeapify(nums, i, len);
        }
    }

    void heapSort(vector<int>& nums) {
        int len = (int)nums.size() - 1;
        buildMaxHeap(nums, len);
        for (int i = len; i >= 1; i--) {
            swap(nums[i], nums[0]);
            len -= 1;
            maxHeapify(nums, 0, len);
        }
    }

public:
    vector<int> sortArray(vector<int>& nums) {
        heapSort(nums);
        return nums;
    }
};

时间复杂度:O(nlogn);
空间复杂度:O(1);(原地排序算法,只需要常数空间保存一些变量);

发布了76 篇原创文章 · 获赞 10 · 访问量 8239

猜你喜欢

转载自blog.csdn.net/weixin_38742280/article/details/105260084