leetcode刷题之堆
今天終于開啟的第二個專題的刷題之旅堆,不過第一個專題棧還有一個小問題沒解決就是利用遞減棧去解決接雨水的問題,雖然那道題我用動態規劃的問題解決出來了,我記得看到過一道面試題,問棧和堆有什么區別。通過搜索網上的資料總結如下。
棧(stack)由系統分配內存,速度較快,但是自己無法掌握。
堆:一般用兩種方法來申請內存。new和malloc 我們自己申請出來的內存,相對于棧來說速度較慢,但是使用起來比較方便。
堆主要由大頂堆和小頂堆,其分布結構類似于二叉搜索樹,又和二叉搜索樹有所不同,大頂堆父節點大于其子結點(左右)即使遞減排列的。小頂堆和大頂堆相反,按照升序排列。構建大小頂堆的時候一般要和優先隊列結合起來定義其形式如下
priority_queue<type,container,functional>
其中type為數據類型,container是容器的類型,需要注意的是container必須用數組去實現。比如vector、deque等但是不能用list?
functional就是比較方式
關于優先隊列的講解與實現,這是一篇十分好的文章。
https://www.cnblogs.com/huashanqingzhu/p/11040390.html
這是個比較簡單的題目,其實這道題最容易想到的就是我們排序之后,返回nums[nums.size()-k],但是復雜度要比用堆解決高些。用小頂堆去解決leetcode上官方題解是正確的但是在講解過程說的是大頂堆其實是錯的。我們通常用小頂堆去尋找最大值,因為小頂堆的堆頂存放的是最小元素,在向堆插入元素是按照遞增的順序插入的,因此求第k個最大元素,直接讓堆中有k個元素,堆頂即是第k個最大值,因為堆中始終存放的是前k個最大值。堆頂又是最小的。有了這道題作為鋪墊,下面的那一道題相對容易一些。
class Solution { public:int findKthLargest(vector<int>& nums, int k) {int res=0;priority_queue<int,vector<int>,greater<int> >tmp;if(nums.size()==0 ||k>nums.size())//邊界條件處理return 0;for(int i=0;i<nums.size();i++){tmp.push(nums[i]);if(tmp.size()>k)tmp.pop();}res=tmp.top();return res;} };解法如下,思路比較簡單,就是通過建立哈希表 來統計元素出現的頻率,利用小頂堆來記錄k個出現頻率最高的元素。如果新的元素出現的次數大于堆頂元素則將該元素入堆否則不做處理。稍微有點難度的地方在于構建小頂堆的時候需要重載類函數。代碼如下
class Solution { public: typedef pair<int,int> tmp;//定義多個相同的pair類型對象,用typedef簡化聲明 struct cmp{bool operator()(const tmp &a,const tmp &b)//重載類函數用于構建小頂堆{return a.second>b.second;} };vector<int> topKFrequent(vector<int>& nums, int k) {unordered_map <int,int>hash;for(auto i:nums){hash[i]++;}priority_queue<tmp,vector<tmp>,cmp> s;//構建小頂堆 精髓在于 vector<tmp>for(auto item:hash)// 將哈希表和小堆頂結合{if(s.size()<k)s.push(item);else if(item.second>s.top().second){s.pop();s.push(item);}}vector<int> res(s.size(),0);while(!s.empty()){res[s.size()-1]=s.top().first;s.pop();}return res;} };?
有了上面兩個題的鋪墊這個題可以直接想到用小頂堆來存放數據,存完之后把小頂堆的數據逐一取出。涉及到鏈表的操作。
鏈表的操作待學習和記錄
class Solution { public:struct cmp{ //對新的數據類型的<進行重寫bool operator()(ListNode *a,ListNode *b){return a->val > b->val;}};ListNode* mergeKLists(vector<ListNode*>& lists) {priority_queue<ListNode* ,vector<ListNode*> , cmp> heapk;for(auto p:lists){if(p!=NULL){heapk.push(p);}}ListNode *pHead = new ListNode(-1);ListNode *pCur = pHead;while(!heapk.empty()){ListNode *top = heapk.top();heapk.pop();pCur->next = top;pCur = pCur->next;if(top->next!=NULL){heapk.push(top->next);} }pCur = pHead->next;delete pHead;return pCur;} };這道題我一開始的想法就是暴力解法很容易想到,就是把元素逐個后移,和當前元素的后兩個值進行比較,保存結果,代碼如下
class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> res;int tmp=0;//int n=nums.size();if (nums.size() == 0){return res;}if (k == 1)//長度為1,就是自身{return nums;}for(int i=0;i<=nums.size()-k;i++){if(i+k<nums.size()){for(int j=i;j<i+k;j++){tmp=max(tmp,nums[j]);}}if(i+k==nums.size()){for(int j=i;j<nums.size();j++)tmp=max(tmp,nums[j]);}res.push_back(tmp);tmp=0;}return res;} };但是算法時間復雜度o(nk)還是比較高的,不過leetcode那個雙向隊列我實在是沒有想出來,看題解看了好一會才明白。其思想和大頂堆的思想有點相似。就是保證隊頭元素始終是最大值。我一開始怎么也想不想明白如何保持滑動窗口總是小于等于k的。后來看了別人的題解才明白,先求出來第一個滑動窗口內的最大值,此時滑動窗口已經定義好了,然后逐漸后移,如果當前元素大于隊尾元素,則將隊尾元素出隊直到不大于隊尾元素為止。如果當前元素小于隊尾元素,要判斷從當前元素往前數k-1個值是否和隊頭元素相等,如果相等是要移除隊頭元素的,因為當前元素比隊尾元素小是要入隊的,保證滑動窗口內有k個元素就要擠掉最前頭的元素。代碼如下:
class Solution { public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> res;if (nums.size() == 0){return res;}if (k == 1)//長度為1,就是自身{return nums;}deque<int> maxque;maxque.push_back(nums[0]);int maxk = nums[0];for (int i = 1; i < k; i++){if (i == nums.size()) //萬一k比nums的個數還要大{res.push_back(maxk);return res;}if (maxk < nums[i]){maxk = nums[i];}while (!maxque.empty() && nums[i] > maxque.back())//從尾部開始把每個小于nums[i]的數去除。{maxque.pop_back();}maxque.push_back(nums[i]);}res.push_back(maxk);//第一個窗口的最大值for (int i = k; i < nums.size(); i++){if (nums[i - k] == maxque.front())//nums[i-k]移出窗口,如果隊列頭是nums[i-k]就要去除掉{maxque.pop_front();}while (!maxque.empty() && nums[i] > maxque.back())//從尾部開始把每個小于nums[i]的數去除。{maxque.pop_back();}maxque.push_back(nums[i]);res.push_back(maxque.front());}return res;} };?
總結
以上是生活随笔為你收集整理的leetcode刷题之堆的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: php获取url返回的json,【求助】
- 下一篇: 常见的机器学习算法
