LeetCode215——the kth largest element——heap sort

这道题是寻找一串数组中第k个最大的元素,题目要求如下:
在这里插入图片描述
特别强调,不需要寻找不同的元素。这道题可以使用排序算法直接来解决,手写快排之后返回nums[k - 1]即可。当然这道题还可以选择使用STL库直接解决,就像这样:

// using generic algorithm and lambda expression to solve the problem
sort(nums.begin(), nums.end(), [](const int& a, const int& b) -> bool {
    
    return a > b;});
return nums[k - 1];

而且这样的速度还很快,所以在很多工程中,如果没有特殊需求,应该尽可能多的使用STL的设施。

这道题写在blog上还是为了想复习一下堆排序的写法,考虑到要求,显然需要构造的是一个大顶堆。涉及到堆,很多博客在画图解的时候使用的是二叉树的形式,这可以便于理解。但实际上我们的堆排序操作都是在一个线性数组上完成的,这点不可以糊涂。而且,堆一般使用完全二叉树来存储这样一条重要的性质可以让我们在编写代码时通过父亲结点的索引快速计算出左右孩子的索引值,就是:

LeftChild.Index = 2 * Parent.Index + 1;
RightChild.Index = 2 * Parent.Index + 2;

堆的重要操作其实只有一个,就是自上而下的调整堆(adjustHeap),思路也很简单,从某一个元素开始自上而下的调整:比较这个结点和其左右儿子结点的值大小,如果大于其两个儿子,调整到此结束;否则和较大的儿子结点交换数值。交换数值之后仍然存在是否大于交换后的两个儿子节点的值的疑问,所以仍需判断继续向下迭代下去。直到数组遍历完成或者父亲结点大于其两个儿子结点时结束,代码如下:

    // adjust the head from a certain element(top-down)
    void adjustHeap(vector<int>& nums, int Index, int Length)
    {
    
    
        // index of the left child
        int Left = Index * 2 + 1;
        while(Left < Length)
        {
    
    
            // get the larger element of left and right child
            int Larger = ((Left + 1 < Length) && (nums[Left] < nums[Left + 1])) ? Left + 1 : Left;

            // swap the parent node with the larger child(if child > parent), otherwise jump out of loop
            if(nums[Larger] <= nums[Index])
                break;
        
            // go on top-down adjustment
            swap(nums, Index, Larger);
            Index = Larger;
            Left = Index * 2 + 1;
        }
    }

完成了这一步,剩下一步就是堆的初始化(堆的构建),也就是把任何一个给定的输入序列通过上面的方法调整成一个大顶堆。其实思路很简单,就是自底向上的一个个调用adjustHeap函数。为什么自底向上建堆?因为adjustHeap作用之后可以保证自Index以后的所有元素满足大顶堆。自底向上建堆才可以保证最终形成的是一个大顶堆,而且没有孩子的结点不需要调整。所以我们从第一个有孩子的结点(nums.size() / 2 - 1)开始进行堆的构建:

 	// initialize and build the heap(down-top)
    void buildHeap(vector<int>& nums)
    {
    
    
        for(int Start = nums.size() / 2 - 1 ; Start >= 0 ; --Start)
            adjustHeap(nums, Start, nums.size());
    }

至此,大顶堆构建完毕,剩下的就是依次从堆顶取出前k个元素,每取出一个就将其放置在数组尾部不再考虑,并从堆顶调整一次堆。重复k-1次即可(k-1是因为建堆时就算一次):

int findKthLargest(vector<int>& nums, int k) {
    
    
        // head sort method
        buildHeap(nums);
        auto Length = nums.size() - 1;
        while(--k)
        {
    
    
            swap(nums, 0, Length--);
            adjustHeap(nums, 0, Length + 1);
        }
        return nums[0];
    }

这就是第k个最大的元素的堆排序解法,借此复习一下堆的构建和调整。

Guess you like

Origin blog.csdn.net/zzy980511/article/details/115832399