題目原文css
設計一個找到數據流中第 k
大元素的類(class)。注意是排序後的第 k
大元素,不是第 k
個不一樣的元素。java
請實現 KthLargest
類:python
KthLargest(int k, int[] nums)
使用整數 k
和整數流 nums
初始化對象。int add(int val)
將 val
插入數據流 nums
後,返回當前數據流中第 k
大的元素。
示例:面試
輸入: ["KthLargest", "add", "add", "add", "add", "add"] [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]] 輸出: [null, 4, 5, 5, 8, 8] 解釋: KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]); kthLargest.add(3); // return 4 kthLargest.add(5); // return 5 kthLargest.add(10); // return 5 kthLargest.add(9); // return 8 kthLargest.add(4); // return 8
提示:
1 <= k <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
add
方法 104
次k
大元素時,數組中至少有 k
個元素嘗試解答算法
寒假月期間的力扣主題是滑動窗口,大部分題目比較弟弟,但也有個別題目或新穎清奇或夯實基礎,例如這道題雖然是簡單難度,可是其主要考察的內容是經典的topK,topK的標準解法通常是寫一個堆數據結構,博主仍是太菜了沒有想到這種解法,解答費了很多時間,最後檢驗一共十個測試用例,卡在了測試時間複雜度的第七個超長用例沒有AC,上垃圾代碼:數組
1 class KthLargest { 2 public int k; 3 public int[] nums; 4 public KthLargest(int k, int[] nums) { 5 this.k = k; 6 Arrays.sort(nums); 7 this.nums = nums; 8 // for(int i=0;i<nums.length;i++){ 9 // System.out.print(this.nums[i]); 10 // } 11 } 12 13 public int add(int val) { 14 int[] new_nums = new int[nums.length+1]; 15 boolean vias = false; 16 int new_index = 0; 17 int index = 0; 18 while(new_index<new_nums.length && index<nums.length){ 19 if(val>nums[index]){ 20 new_nums[new_index] = nums[index]; 21 } 22 else{ 23 if(!vias){ 24 new_nums[new_index] = val; 25 index--; 26 vias = true; 27 } 28 else{ 29 new_nums[new_index] = nums[index]; 30 } 31 } 32 new_index++; 33 index++; 34 } 35 if(!vias){ 36 new_nums[new_nums.length-1] = val; 37 } 38 for(int i=0;i<new_nums.length;i++){ 39 System.out.print(new_nums[i]); 40 System.out.print(' '); 41 } 42 System.out.println(); 43 this.nums = new_nums; 44 return new_nums[new_nums.length-k]; 45 } 46 } 47 48 /** 49 * Your KthLargest object will be instantiated and called as such: 50 * KthLargest obj = new KthLargest(k, nums); 51 * int param_1 = obj.add(val); 52 */
標準題解數據結構
1 class KthLargest: 2 3 def __init__(self, k: int, nums: List[int]): 4 self.heap = [] 5 self.k = k 6 for num in nums: 7 heapq.heappush(self.heap,num) 8 if len(self.heap) > k: 9 heapq.heappop(self.heap) 10 11 12 def add(self, val: int) -> int: 13 heapq.heappush(self.heap,val) 14 if len(self.heap) > self.k: 15 heapq.heappop(self.heap) 16 return self.heap[0] 17 18 19 ##做者:MiloMusiala 20 ##連接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/solution/python-dong-hua-shou-xie-shi-xian-dui-by-ypz2/ 21 ##來源:力扣(LeetCode) 22 ##著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
方法二:手寫實現
咱們經過本身手寫來詳細刨析一下堆這個數據結構究竟是怎麼實現的。
堆的特色:
內部數據是有序的
能夠彈出堆頂的元素,大頂堆就是彈出最大值,小頂堆就是彈出最小值
每次加入新元素或者彈出堆頂元素後,調整堆使之從新有序僅須要O(logn)的時間
支持在線算法
堆的本質:
它是一個徹底二叉樹
實現的時候咱們不須要建造一個樹,改用一個數組便可
那麼咱們是如何把一個徹底二叉樹和一個數組關聯到一塊兒的呢?
給樹的節點編號,節點的編號就是元素在數組中的下標
幻燈片1.JPG
因而咱們發現一個很重要的結論:
已知一個節點的編號爲index,那麼它的父節點的編號爲:
father_index=⌊index−12⌋father\_index = \lfloor {index-1 \over 2}\rfloor father_index=⌊2index−1⌋
左孩子節點的編號爲
left_index=index∗2+1left\_index = index * 2 + 1 left_index=index∗2+1
右孩子節點的編號爲
right_index=index∗2+2right\_index = index * 2 + 2 right_index=index∗2+2
如何調整堆
1.添加元素
把新數據添加到樹的最後一個元素,也就是數組的末尾
把末尾節點向上調整app
一、初始化函數
1 def __init__(self,desc=False): 2 """ 3 初始化,默認建立一個小頂堆 4 """ 5 self.heap = [] 6 self.desc = desc
二、堆的大小測試
@property def size(self): return len(self.heap)
三、返回堆頂元素
1 def top(self): 2 if self.size: 3 return self.heap[0] 4 return None
四、添加元素
1 def push(self,item): 2 """ 3 添加元素 4 第一步,把元素加入到數組末尾 5 第二步,把末尾元素向上調整 6 """ 7 self.heap.append(item) 8 self._sift_up(self.size-1)
五、彈出堆頂元素
1 def pop(self): 2 """ 3 彈出堆頂 4 第一步,記錄堆頂元素的值 5 第二步,交換堆頂元素與末尾元素 6 第三步,刪除數組末尾元素 7 第四步,新的堆頂元素向下調整 8 第五步,返回答案 9 """ 10 item = self.heap[0] 11 self._swap(0,self.size-1) 12 self.heap.pop() 13 self._sift_down(0) 14 return item
六、判斷兩個元素的大小關係,這裏有個小trick
1 def _smaller(self,lhs,rhs): 2 return lhs > rhs if self.desc else lhs < rhs 3 4 5 做者:MiloMusiala 6 連接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/solution/python-dong-hua-shou-xie-shi-xian-dui-by-ypz2/ 7 來源:力扣(LeetCode) 8 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
七、向上調整
1 def _sift_up(self,index): 2 """ 3 向上調整 4 若是父節點和當前節點知足交換的關係 5 (對於小頂堆是父節點元素更大,對於大頂堆是父節點更小), 6 則持續將當前節點向上調整 7 """ 8 while index: 9 parent = (index-1) // 2 10 11 if self._smaller(self.heap[parent],self.heap[index]): 12 break 13 14 self._swap(parent,index) 15 index = parent
八、向下調整
1 def _sift_down(self,index): 2 """ 3 向下調整 4 若是子節點和當前節點知足交換的關係 5 (對於小頂堆是子節點元素更小,對於大頂堆是子節點更大), 6 則持續將當前節點向下調整 7 """ 8 # 若存在子節點 9 while index*2+1 < self.size: 10 smallest = index 11 left = index*2+1 12 right = index*2+2 13 14 if self._smaller(self.heap[left],self.heap[smallest]): 15 smallest = left 16 17 if right < self.size and self._smaller(self.heap[right],self.heap[smallest]): 18 smallest = right 19 20 if smallest == index: 21 break 22 23 self._swap(index,smallest) 24 index = smallest
九、交換兩個元素
1 def _swap(self,i,j): 2 self.heap[i],self.heap[j] = self.heap[j],self.heap[i] 3 4 5 做者:MiloMusiala 6 連接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/solution/python-dong-hua-shou-xie-shi-xian-dui-by-ypz2/ 7 來源:力扣(LeetCode) 8 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
完整代碼:
1 class Heap: 2 def __init__(self,desc=False): 3 """ 4 初始化,默認建立一個小頂堆 5 """ 6 self.heap = [] 7 self.desc = desc 8 9 @property 10 def size(self): 11 return len(self.heap) 12 13 def top(self): 14 if self.size: 15 return self.heap[0] 16 return None 17 18 def push(self,item): 19 """ 20 添加元素 21 第一步,把元素加入到數組末尾 22 第二步,把末尾元素向上調整 23 """ 24 self.heap.append(item) 25 self._sift_up(self.size-1) 26 27 def pop(self): 28 """ 29 彈出堆頂 30 第一步,記錄堆頂元素的值 31 第二步,交換堆頂元素與末尾元素 32 第三步,刪除數組末尾元素 33 第四步,新的堆頂元素向下調整 34 第五步,返回答案 35 """ 36 item = self.heap[0] 37 self._swap(0,self.size-1) 38 self.heap.pop() 39 self._sift_down(0) 40 return item 41 42 def _smaller(self,lhs,rhs): 43 return lhs > rhs if self.desc else lhs < rhs 44 45 def _sift_up(self,index): 46 """ 47 向上調整 48 若是父節點和當前節點知足交換的關係 49 (對於小頂堆是父節點元素更大,對於大頂堆是父節點更小), 50 則持續將當前節點向上調整 51 """ 52 while index: 53 parent = (index-1) // 2 54 55 if self._smaller(self.heap[parent],self.heap[index]): 56 break 57 58 self._swap(parent,index) 59 index = parent 60 61 def _sift_down(self,index): 62 """ 63 向下調整 64 若是子節點和當前節點知足交換的關係 65 (對於小頂堆是子節點元素更小,對於大頂堆是子節點更大), 66 則持續將當前節點向下調整 67 """ 68 # 若存在子節點 69 while index*2+1 < self.size: 70 smallest = index 71 left = index*2+1 72 right = index*2+2 73 74 if self._smaller(self.heap[left],self.heap[smallest]): 75 smallest = left 76 77 if right < self.size and self._smaller(self.heap[right],self.heap[smallest]): 78 smallest = right 79 80 if smallest == index: 81 break 82 83 self._swap(index,smallest) 84 index = smallest 85 86 def _swap(self,i,j): 87 self.heap[i],self.heap[j] = self.heap[j],self.heap[i] 88 89 class KthLargest: 90 91 def __init__(self, k: int, nums: List[int]): 92 self.heap = Heap() 93 self.k = k 94 for num in nums: 95 self.heap.push(num) 96 if self.heap.size > k: 97 self.heap.pop() 98 99 100 def add(self, val: int) -> int: 101 self.heap.push(val) 102 if self.heap.size > self.k: 103 self.heap.pop() 104 return self.heap.top() 105 106 107 108 ##做者:MiloMusiala 109 ##連接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/solution/python-dong-hua-shou-xie-shi-xian-dui-by-ypz2/ 110 ##來源:力扣(LeetCode) 111 ##著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
思路差距
topK問題在各大互聯網面試題目中比較常見,通常是往一串有序數據中頻繁執行插入、刪除的操做,爲優化此類操做,通常都會使用堆這類數據結構,各語言對於堆都有現成的類,好比java語言的PriorityQueue,python語言的heapq,菜雞博主認爲如果爲了提升代碼水平,應該作到理解堆的原理並能手撕堆數據結構。
技術差距
手撕堆數據結構、堆排序還需練習