【总结笔记】快排与堆排

快速排序 【不稳定算法】

  • 1)要有哨兵,左右指针指向的值都是跟哨兵比较;
  • 2)里边两个while都不能等于,才能实现右边调换后,从左边开始找起,左边调用后,从右边找起;
  • 3)二分时,一定是 quickSort(vec, startId, left-1); quickSort(vec, left+1, endId);
    不能包括中分节点,否则会出现无休止递归,陷入递归栈爆的问题。
  • 4)在循环里面,当前的 left/right 就是哨兵的预位置,该当该位置的左边都小于哨兵,右边都大于等于哨兵时,才将哨兵安顿下来。
  • 【非稳定算法】将序列分成两段,比大于等于哨兵的放右边,小于哨兵的放左边。因此当有重复元素,以两个连续的5为例子,当left移动到第一个5时,会将其换到右边的子序列。因此快速排序不是稳定的算法。
void quickSort(vector<int> &vec, int startId, int endId) {
        if (startId >= endId)
            return;
        int left = startId, right = endId;
        int soldier = vec[left], tmp;
        while (left < right) {
            while (left < right && vec[right] >= soldier)  // 大于等于target放右边
                --right;
            if (left < right) 
                vec[left++] = vec[right];
            while (left < right && vec[left] < soldier)   // 小于target放左边
                ++left;
            if (left < right) 
                vec[right--] = vec[left];
        }
        vec[left] = soldier;
        quickSort(vec, startId, left-1);
        quickSort(vec, left+1, endId);
    }

int main()
{
    int arr[] = {5,3,7,6,4,1,0,2,9,5,8};
    vector<int> vec;
    for (auto data : arr) 
        vec.push_back(data);
    int length = vec.size();
    quickSort(vec, 0, length-1);
    for (auto data : vec)
        cout << data << " "; 
    return 0;
}

215 数组中的第 K 个最大元素

基于快排求第 K 个最大元素,首先可以无需对下标为 n-k 的节点的左边进行排序 if (lft>nums.size()-k) quickSort(nums, left, lft-1, k);
时间复杂度为 O(n) 【证明复杂,参照算法导论 9.2 期望为线性的选择算法】
空间复杂度位O(logn) 【递归栈的高度】

堆排序 【不稳定算法】

堆排序的精髓之处是基于数组,以下标为 i 的元素为堆顶,其左孩子下标为 2*i + 1,右孩子下标为 2*i + 2。
堆排序算法过程:
(1)建大/小顶堆:以第 n/2..1 个结点为堆顶进行最大堆化处理。
(2)将堆顶元素(下标为0)与堆尾元素(下标为 heapsize-1)互换,每交换1次,堆尾下标减1,直到所有元素排序完毕

  • 重点:最大堆化处理算法
    若左右孩子的最大值大于堆顶,则将其与堆顶元素交换。由于交换后会破坏以最大孩子结点为堆顶的堆的最大堆性质,因此,还要对最大孩子结点进行堆调整。
    值得注意的是,需要判断左右孩子是否不在给定的堆大小范围内。

215 数组中的第 K 个最大元素

class Solution {
private:
    void buildMaxHeap(vector<int>& nums, int heapSize) {
        for (int curId=heapSize/2; curId>=0; --curId) {
            maxHeapify(nums, heapSize, curId);
        }
    }
    void maxHeapify(vector<int>& nums, int heapSize, int parent) {
        int lchild = 2*parent + 1, rchild = 2*parent + 2, maxPtr = parent;
        if (lchild < heapSize && nums[lchild] > nums[maxPtr])
            maxPtr = lchild;
        if (rchild < heapSize && nums[rchild] > nums[maxPtr])
            maxPtr = rchild;
        if (maxPtr != parent) {
            swap(nums[parent], nums[maxPtr]);
            maxHeapify(nums, heapSize, maxPtr);
        }
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size(), cnt = 0;
        int heapSize = n;
        buildMaxHeap(nums, heapSize);
        for (int i=n-1; i>=0 && cnt<k; --i,++cnt) {
            swap(nums[0], nums[i]);
            --heapSize;
            maxHeapify(nums, heapSize, 0);
        }
        return nums[n-k];
    }
};

时间复杂度为 O(nlogn) —— 参照算法导论
空间复杂度为 O(n) —— 递归栈大小等于树的高度

posted @ 2022-06-22 14:39  MasterBean  阅读(86)  评论(0)    收藏  举报