自顶向下建堆 从根结点开始,然后一个一个的把结点插入堆中。当把一个新的结点插入堆中时,需要对结点进行调整,以保证插入结点后的堆依然是大根堆。 时间复杂度为$O(n\log_{2}{n})$
自底向上建堆 初始化建堆只需要对二叉树的非叶子节点调用Adjust()函数,由下至上,由右至左选取非叶子节点来调用Adjust()函数。那么倒数第二层的最右边的非叶子节点就是最后一个非叶子结点。
如果仅从下面展示的代码上直观观察,会得出构造二叉堆的时间复杂度为O(n)的结果,这个结果是错的,虽然该算法外层套一个n次循环,而内层套一个分治策略下的复杂度的循环,该思考方法犯了一个原则性错误,那就是构建二叉堆是自下而上的构建,每一层的最大纵深总是小于等于树的深度的,因此,该问题是叠加问题,而非递归问题。
假设高度为k,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较然后交换(如果顺序是对的就不用交换);倒数第三层呢,则会选择其子节点进行比较和交换,如果没交换就可以不用再执行下去了。如果交换了,那么又要选择一支子树进行比较和交换;高层也是这样逐渐递归。
n个元素的完全二叉树,树高h为$log_{2}{(n+1)}$;
最下层非叶结点所需时间: $T(h-1) = 2^{(h-1)} * 1$;
第i层元素所需时间为$T(i) = 2^{i} * (h-i)$;
展开
求和公式为等差数列和等比数列的乘积
使用错位相减法可得
将$h= log_{2}{(n+1)}$代入S
可得到$S=O(n)$
时间复杂度为$O(n)$
插入 时间复杂度:$O(\log_{2}{n})$
删除 时间复杂度:$O(\log_{2}{n})$
priority_queue 接口
empty()
size()
push()
top()
pop()
声明方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 priority_queue<int > pq; priority_queue<int ,vector <int >,less<int >> pq; priority_queue<int ,vector <int >,greater<int >> pq; class KthLargest { struct cmp { bool operator () (int &x,int &y) { return x>y; } }; private : int k; priority_queue<int ,vector <int >,KthLargest::cmp> pq; public : KthLargest(int k, vector <int >& nums) { this ->k = k; int n = nums.size(); for (int i = 0 ; i < n; i ++){ if (pq.size()<k){pq.push(nums[i]);} else if (pq.top()<nums[i]){pq.pop();pq.push(nums[i]);} } } int add (int val) { if (pq.size()<this ->k){pq.push(val);} else if (pq.top()<val){pq.pop();pq.push(val);} return pq.top(); } }; class Solution {public : int findKthLargest (vector <int >& nums, int k) { int n = nums.size(); auto cmp=[](int a,int b){return a>b;}; priority_queue<int,vector<int>,decltype(cmp)> pq(cmp); for (int i = 0 ; i < n; i ++){ if (pq.size()<k){pq.push(nums[i]);} else if (pq.top()<nums[i]){pq.pop();pq.push(nums[i]);} } return pq.top(); } };