ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

leetcode常规算法题复盘(第十六期)——数据流中的第 K 大元素

2021-03-02 22:33:07  阅读:134  来源: 互联网

标签:index self 元素 复盘 heap 数据流 节点 leetcode def


题目原文

703. 数据流中的第 K 大元素

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

  • 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  */

标准题解

方法一:使用heapq库

 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.添加元素

    把新数据添加到树的最后一个元素,也就是数组的末尾
    把末尾节点向上调整

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  1. 弹出堆顶
  • 交换根节点与最后一个节点的值
  • 删除最后一个节点
  • 把根节点向下调整

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

所以需要写的函数有

1、初始化

1     def __init__(self,desc=False):
2         """
3         初始化,默认创建一个小顶堆
4         """
5         self.heap = []
6         self.desc = desc

2、堆的大小

    @property
    def size(self):
        return len(self.heap)

 

 3、返回堆顶元素

1     def top(self):
2         if self.size:
3             return self.heap[0]
4         return None

4、添加元素

1     def push(self,item):
2         """
3         添加元素
4         第一步,把元素加入到数组末尾
5         第二步,把末尾元素向上调整
6         """
7         self.heap.append(item)
8         self._sift_up(self.size-1)

5、弹出堆顶元素

 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

 

6、判断两个元素的大小关系,这里有个小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 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

7、向上调整

 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

8、向下调整

 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

9、交换两个元素

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,菜鸡博主认为若是为了提高代码水平,应该做到理解堆的原理并能手撕堆数据结构。

技术差距

手撕堆数据结构、堆排序还需练习

 

标签:index,self,元素,复盘,heap,数据流,节点,leetcode,def
来源: https://www.cnblogs.com/monkiki/p/14471764.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有