理论与力扣应用题——排序

当用数组表示存储 n n n个元素的堆时,叶结点下标分别是 [ n / 2 ] + 1 [n/2]+1 [n/2]+1 [ n / 2 ] + 2 [n/2]+2 [n/2]+2,…, n n n

表示堆的数组 A A A包含两个属性: A . l e n g t h A.length A.length表示数组元素的个数, A . h e a p − s i z e A.heap-size A.heapsize表示有多少个堆元素存储在该数组中。

  • 给定结点的下标 i i i,计算它的父结点、左孩子和右孩子的下标:
 PARENT(i)
 	return [i/2]
 
 LEFT(i)
 	return 2i
 
 RIGHT(i)
 	return 2i+1
维护堆的性质
  • MAX-HEAPIFY过程:维护最大堆性质的重要过程,其时间复杂度为 O ( l g n ) O(lgn) O(lgn),它的输入是一个数组 A A A和一个下标 i i i

在调用MAX-HEAPIFY的时候,假定根结点为LEFT( i i i)和RIGHT( i i i)的二叉树都是最大堆,这时 A [ i ] A[i] A[i]有可能小于其孩子,这样就违背了最大堆的性质。MAX-HEAPIFY通过让 A [ i ] A[i] A[i]的值在最大堆中“逐渐下降”,从而使得以下标 i i i为根结点的子树重新遵循最大堆的性质

MAX-HEAPITY(A, i)
	l = LEFT(i)
	r = RIGHT(i)
	if l <= A.heap-size and A[l] > A[i]
		largest = l
	else largest = i
	if r <= A.heap-size and A[r] > A[largest]
		largest = r
	if largest ≠ i
		exchange A[i] with A[largest]
		MAX-HEAPIFY(A, largest)

在程序的每一步中,从 A [ i ] A[i] A[i] A [ L E F T ( i ) ] A[LEFT(i)] A[LEFT(i)] A [ R I G H T ( i ) ] A[RIGHT(i)] A[RIGHT(i)]中选出最大的,并将其下标存储在 l a r g e s t largest largest中,如果 A [ i ] A[i] A[i]是最大的,那么以 i i i为根结点的子树已经是最大堆,程序结束,否则,最大元素是 i i i的某个孩子结点,则交换 A [ i ] A[i] A[i] A [ l a r g e s t ] A[largest] A[largest]的值,从而使 i i i及其孩子都满足最大堆的性质,在交换后,下标为 l a r g e s t largest largest的结点的值是原来的 A [ i ] A[i] A[i],于是以该结点为根的子树又有可能会违反最大堆的性质。因此,需要对该子树递归调用MAX-HEAPIFY

建堆
  • BUILD-MAX-HEAP过程:具有线性时间复杂度,其时间复杂度为 O ( n l g n ) O(nlgn) O(nlgn),功能是从无序的输入数据数组中构造一个最大堆

可以用自底向上的方法利用过程MAX-HEAPIFY把一个大小为 n = A . l e n g t h n=A.length n=A.length的数组 A [ 1.. n ] A[1..n] A[1..n]转换为最大堆。子数组 A ( [ n / 2 ] + 1.. n ) A([n/2]+1..n) A([n/2]+1..n)中的元素都是树的叶结点,BUILD-MAX-HEAP过程就是对树中的其他结点都调用一次MAX-HEAPIFY

BUILD-MAX-HEAP(A)
	A.heap-size = A.length
	for i = [A.length/2] downto 1
		MAX-HEAPIFY(A, i)

每次调用MAX-HEAPIFY的时间复杂度是 O ( l g n ) O(lgn) O(lgn)BUILD-MAX-HEAP需要 O ( n ) O(n) O(n)次这样的调用,所以总的时间复杂度是 O ( n l g n ) O(nlgn) O(nlgn)

堆排序算法
  • HEAPSORT过程:事件复杂度为 O ( n l g n ) O(nlgn) O(nlgn),功能是对一个数组进行原址排序

初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组 A [ 1.. n ] A[1..n] A[1..n]建成最大堆,其中 n = A . l e n g t h n=A.length n=A.length。因为数组中的最大元素总在根结点 A [ 1 ] A[1] A[1]中,通过把它与 A [ n ] A[n] A[n]进行互换,然后从堆中去掉结点 n n n(这一操作可以通过减少 A . h e a p − s i z e A.heap-size A.heapsize的值来实现),对于新的根结点调用MAX-HEAPIFY进行最大堆的维护,从而在 A [ 1.. n − 1 ] A[1..n-1] A[1..n1]上构造一个新的最大堆,堆排序算法会不断重复这一过程,直到堆的大小从 n − 1 n-1 n1降到2。

HEAPSORT(A)
	BUILD-MAX-HEAP(A)
	for i = A.length downto 2
		exchange A[1] with A[i]
		A.heap-size = A.heap-size - 1
		MAX-HEAPIFY(A, 1)
C++ SLT中的priority_queue
  • priority_queue基本用法

头文件<queue>

priority_queue< type, container, function>
  • type:类型
  • container:实现优先队列的底层容器(缺省)
  • function:元素之间的比较方式(缺省)

对于container,要求必须是数组形式实现的容器,例如vectordeque,而不能使list。在STL中,默认情况下(不加后面两个参数)是以vector为容器,以 operator< 为比较方式,所以在只使用第一个参数时,优先队列默认是一个最大堆,每次输出的堆顶元素是此时堆中的最大元素

  • 大顶堆
 priority_queue<int,vector<int>,less<int> > Q;
  • 小顶堆
 priority_queue<int,vector<int>,greater<int> > Q;
  • 操作
  (1)取堆顶操作:Q.top()
  (2)判断堆空操作:Q.empty()
  (3)添加元素入堆:Q.push(int x)
  (4)元素弹出堆:Q.pop()
  (5)求堆中元素个数:Q.size()
应用:求数组中的第k大元素

按上方的理论进行的代码编写

class Solution {
public:
    void maxHeapify(vector<int>& a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a[i], a[largest]);
            maxHeapify(a, largest, heapSize);
        }
    }

    void buildMaxHeap(vector<int>& a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    int findKthLargest(vector<int>& nums, int k) {
        int heapSize = nums.size();
        buildMaxHeap(nums, heapSize);
        for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
            swap(nums[0], nums[i]);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }
};

使用C++ STL库进行求解

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // 构造一个空的优先队列,此优先队列是一个小顶堆
        priority_queue<int,vector<int>,greater<int> > Q;
        for (int i = 0; i < nums.size(); i++){
            if (Q.size() < k){
                Q.push(nums[i]);
            } else if (nums[i] > Q.top()) {
                Q.pop();
                Q.push(nums[i]);
            }
        }
        return Q.top();
    }
};
应用:求数组中的第k小元素
class Solution {
public:
    void minHeapify(vector<int>& a, int i, int heapSize) {
        int l = 2 * i + 1, r = 2 * i + 2, min = i;
        if (l < heapSize && a[l] < a[min]) {
            min = l;
        }
        if (r < heapSize && a[r] < a[min]) { // 这里不能写a[i],只能写a[min]
            min = r;
        }
        if (min != i) {
            swap(a[i], a[min]);
            minHeapify(a, min, heapSize);
        }
    }

    void buildMinHeap(vector<int>& a, int heapSize) {
        for (int i=(int)heapSize/2; i >= 0; i--) {
            minHeapify(a, i, heapSize);
        }
    }

    int findKthMinimum(vector<int>& nums, int k) {
        int heapSize = nums.size();
        buildMinHeap(nums, heapSize);
        for (int i = nums.size() - 1; i >= nums.size() - k + 1; i--) {
            swap(nums[0], nums[i]);
            heapSize--;
            minHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }
};
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, less<int> > Q;
        for (int i = 0; i < nums.size(); i++) {
            if (Q.size() < k) {
                Q.push(nums[i]);
            } else if (nums[i] < Q.top()) {
                Q.pop();
                Q.push(nums[i]);
            }
        }
        return Q.top();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值