[C++][Leetcode][TopK]前K大問題+前K高頻(堆+map)

前兩天面試的時候,面試官問了前K大的問題:先是找前K大數,其次是前K高頻數。按照面試官的思路一道一道展開~

對總體過程中思考的知識點進行一個總結:

  • 爲什麼快排常用?
    • 在大規模數據的時候,快速排序只會線性增長,而堆排序增加幅度很大,會遠遠大於線性。
    • 堆排序指針尋址會耗費很多時間,但是快速排序的話只是移動到前後位置。
    • 參考博文爲什麼快排最好
  • 介紹一下C++構造函數?
    • 構造函數沒有返回值
    • 構造函數用作初始化
    • 構造函數可以重載
  • 點操作符和箭頭操作符的區別?
    • 點操作符的對象是實體
    • 箭頭操作符的對象是指針
  • C++ 優先隊列 堆的使用方式?如何自定義?
  • 爲什麼sort函數自定義cmp,在類裏必須加static關鍵字?
    • sort中的比較函數compare要聲明爲靜態成員函數或全局函數,不能作爲普通成員函數。非靜態成員函數是依賴於具體對象的,而std::sort這類函數是全局的,無法再sort中調用非靜態成員函數。靜態成員函數或者全局函數是不依賴於具體對象的, 可以獨立訪問,無須創建任何對象實例就可以訪問。同時靜態成員函數不可以調用類的非靜態成員。參考博文

 

目錄

1.  Leetcode 215. 數組中的第K個最大元素

2. Leetcode 703. 數據流中的第K大元素

3. Leetcode 面試題 17.14. 最小K個數

4. Leetcode 347. 前 K 個高頻元素

 


1.  Leetcode 215. 數組中的第K個最大元素

  • 題目描述

在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。

示例 1:

輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5
示例 2:

輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4
說明:

你可以假設 k 總是有效的,且 1 ≤ k ≤ 數組的長度。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

  • 思路分析

這個問題有一種非常常見的思路,就是排序,然後取第K個元素即可。最好的時間複雜度是nlogn。但是這個思路,顯然不能讓面試官滿意。說到排序,我們常用的就是快速排序。排序排序有一個partion函數,設定一個哨兵,每次把一個數組分成兩部分,前部分小於哨兵元素(或者大於),後部分大於哨兵元素(或者小於)。這樣每次調用一次partion函數就可以將一個元素放在其應該在的位置上。我們要求取的是第K大的元素,我們並不關心前k-1個元素是否有序。所以我們可以採用partion函數來尋找第K大的元素。按照通常的解法,我們選取第一個元素作爲哨兵。經過一輪partion,如果當前元素的下標小於k-1,表示我們要找的第K大的元素在當前元素的後半部分,否則反之。這樣我們最多調用K次partion就可以找到第K大的元素,時間複雜度爲Klogn.

  • 代碼設計
class Solution {
public:
    void partion(vector<int>&nums,int left,int right,int k)
    {
        if(left<right)
        {
            int privot=nums[left];
            int i=left;
            int j=right;
            while(i!=j)
            {
                while(i<j&&nums[j]<=privot)
                {
                     --j;
                }
                if(i<j)
                {
                    nums[i++]=nums[j];//nums[i]原本存在是privot 已經保存了
                }  
                while(i<j&&nums[i]>privot)
                {
                     ++i;
                }
                if(i<j)
                {
                     nums[j--]=nums[i];
                }      
            }
            //cout<<"p:"<<privot<<endl;
            nums[i]=privot;
            if(i<k-1)
                partion(nums,i+1,right,k);
            else if(i>k-1)
                partion(nums,left,i-1,k);
            else
                return;
    
        }
    }
    int findKthLargest(vector<int>& nums, int k) {
        int n=nums.size();
        if(n<=0||n<k)
            return 0;

        //不關心數組是否有序 只關心是第k個
        partion(nums,0,n-1,k);
        return nums[k-1];
        
    }
};

這時候,面試官接着問,如果是數據流呢?你事先並不知道有多少個元素?這時候,我們只知道要求前K大。

2. Leetcode 703. 數據流中的第K大元素

  • 題目描述

設計一個找到數據流中第K大元素的類(class)。注意是排序後的第K大元素,不是第K個不同的元素。

你的 KthLargest 類需要一個同時接收整數 k 和整數數組nums 的構造器,它包含數據流中的初始元素。每次調用 KthLargest.add,返回當前數據流中第K大的元素。

示例:

int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3);   // returns 4
kthLargest.add(5);   // returns 5
kthLargest.add(10);  // returns 5
kthLargest.add(9);   // returns 8
kthLargest.add(4);   // returns 8
說明:
你可以假設 nums 的長度≥ k-1 且k ≥ 1。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

  •  思路分析

既然不知道有多少數,我們就想到了堆。我們只需要維護一個大小爲K的最小堆,那麼堆定元素就是第K大的元素。最小堆的堆頂元素小於堆裏的其他元素。使用STL的優先隊列構建堆。

  • 代碼設計
class KthLargest {
public:

    priority_queue< int ,vector< int >,greater< int > >maxheap;
    int K;
    KthLargest(int k, vector<int>& nums) {
           int n=nums.size();
           K=k;
           if(n<k)
           {
               for(int i=0;i<n;++i)
                    maxheap.push(nums[i]);
           }
           else
           {
               for(int i=0;i<k;++i)
                    maxheap.push(nums[i]);
                for(int i=k;i<n;++i)
                {
                    if(maxheap.size()==k&&maxheap.top()<nums[i])
                    {
                        maxheap.pop();
                        maxheap.push(nums[i]);
                    }
                }

           }
    }
    
    int add(int val) {
        if(maxheap.size()<K)
            maxheap.push(val);
        else
        {
            if(maxheap.size()==K)
            {
                if(maxheap.top()<val)
                {
                    maxheap.pop();
                    maxheap.push(val);
                }
                    
            }
        }
        return maxheap.top();
    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

3. Leetcode 面試題 17.14. 最小K個數

  • 題目描述

設計一個算法,找出數組中最小的k個數。以任意順序返回這k個數均可。

示例:

輸入: arr = [1,3,5,7,2,4,6,8], k = 4
輸出: [1,2,3,4]
提示:

0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/smallest-k-lcci
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

  • 代碼設計
class Solution {
public:
    void partion(vector<int>&nums,int left,int right,int k)
    {
        if(left<right)
        {
            int privot=nums[left];
            int i=left;
            int j=right;
            while(i!=j)
            {
                while(i<j&&nums[j]>=privot)
                    --j;
                
                if(i < j)
                {
                    nums[i++]=nums[j];
                }
                
                while(i < j&&nums[i]<privot)
                {
                    ++i;
                }
                if(i<j)
                {
                    nums[j--]=nums[i];
                }
            }
            nums[i]=privot;
            if(i<k-1)
                partion(nums,i+1,right,k);
            else if(i>k-1)
                partion(nums,left,i-1,k);
            else
                return;
        }
    }
    vector<int> smallestK(vector<int>& arr, int k) {
        int n=arr.size();
        vector<int>res;
        if(n<=0||n<k)
            return res;

        partion(arr,0,n-1,k);
        for(int i=0;i<k;++i)
            res.push_back(arr[i]);

        return res;
        

    }
};

這時候,面試官說,我們再來看一道題吧,說返回前k高頻個高頻出現的元素。

4. Leetcode 347. 前 K 個高頻元素

  • 題目描述

給定一個非空的整數數組,返回其中出現頻率前 k 高的元素。

示例 1:

輸入: nums = [1,1,1,2,2,3], k = 2
輸出: [1,2]
示例 2:

輸入: nums = [1], k = 1
輸出: [1]
說明:

你可以假設給定的 k 總是合理的,且 1 ≤ k ≤ 數組中不相同的元素的個數。
你的算法的時間複雜度必須優於 O(n log n) , n 是數組的大小。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/top-k-frequent-elements
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

 

  • 思路分析
    • 第一種思路:使用map存儲元素以及其出現的頻數。創建一個vector存儲map裏的元素對。根據第二個元素的大小進行進行降序排序,然後返回前K個元素即可。
    • 第二種思路:使用map存儲元素以及其出現的頻數。維護一個最小堆,大小爲K,存儲的元素爲map裏的元素對。然後返回這個最小堆裏的元素即可。
  • 代碼設計
class Solution {
public:

    struct cmp {
	   bool operator()(const pair<int,int>&a,const pair<int,int>&b)
       {
            return a.second>b.second;
       }
		   
    };
    vector<int> topKFrequent(vector<int>& nums, int k) {
        map<int,int>m;
        vector<int>res;
        priority_queue<pair<int,int>,vector<pair<int,int>>,cmp >q;
        for(int i=0;i<nums.size();++i)
        {
            m[nums[i]]++;
        }

       int map_size=m.size();//map的大小

       //維護一個大小爲K的堆
       map<int,int> ::iterator it;
       for(it=m.begin();it!=m.end();++it)
       {
           if(q.size()<k)
                q.push(make_pair(it->first,it->second));
            else
            {
    
                if(q.size()==k&q.top().second<it->second)
                {
                    q.pop();
                    q.push(make_pair(it->first,it->second));
                }
            }
       }

       while(!q.empty())
       {
           res.push_back(q.top().first);
           q.pop();
       }

        return res;
        


    }
};
class Solution {
public:
    static bool cmp(const pair<int,int>&a,const pair<int,int>&b)
    {
        return a.second > b.second;
    }
    vector<int> topKFrequent(vector<int>& nums, int k) {
        map<int,int>m;
        vector<int>res;
       
        for(int i=0;i<nums.size();++i)
        {
            m[nums[i]]++;
        }

        vector<pair<int,int>>v;
        map<int,int>::iterator it;
        for(it=m.begin();it!=m.end();++it)
        {
            v.push_back(make_pair(it->first,it->second));
        }

        sort(v.begin(),v.end(),cmp);

        if(k>v.size())
            k=v.size();
        cout<<v.size()<<endl;
      
        for(int i=0;i<k;++i)
        {
            res.push_back(v[i].first);
        }

        return res;
        


    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章