wuyijia

导航

代码随想录算法训练营-贪心算法-4|406. 根据身高重建队列、452. 用最少数量的箭引爆气球、435. 无重叠区间

406. 根据身高重建队列

1. 一定要想如何确定一个维度,然后再按照另一个维度重新排列。

2. 先确定身高的维度,降序排列。

3. 按照身高排序之后,优先按身高高的people的k来插入,后序插入节点也不会影响前面已经插入的节点,最终按照k的规则完成了队列。

4. 局部最优:优先按身高高的people的k来插入。插入操作过后的people满足队列属性。

5. 全局最优:最后都做完插入操作,整个队列满足题目队列属性。

6. 插入的过程:

  • 插入[7,0]:[[7,0]]
  • 插入[7,1]:[[7,0],[7,1]]
  • 插入[6,1]:[[7,0],[6,1],[7,1]]
  • 插入[5,0]:[[5,0],[7,0],[6,1],[7,1]]
  • 插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
  • 插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
  • 时间复杂度:O(nlog n + n^2)
  • 空间复杂度:O(n)
# -x[0]表示降序,x[1]表示升序
 1 class Solution:
 2     def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
 3         # 先按照h维度的身高顺序从高到低排序。确定第一个维度
 4         # lambda返回的是一个元组:当-x[0](维度h)相同时,再根据x[1](维度k)从小到大排序
 5         people.sort(key=lambda x: (-x[0], x[1]))
 6         que = []
 7     
 8     # 根据每个元素的第二个维度k,贪心算法,进行插入
 9         # people已经排序过了:同一高度时k值小的排前面。
10         for p in people:
11             que.insert(p[1], p)
12         return que

452. 用最少数量的箭引爆气球

局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

  • 时间复杂度:O(nlog n),因为有一个快排。
  • 空间复杂度:O(1),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间。
 1 class Solution:
 2     def findMinArrowShots(self, points: List[List[int]]) -> int:
 3         if len(points) == 0: return 0
 4         points.sort(key=lambda x: x[0])
 5         result = 1
 6         for i in range(1, len(points)):
 7             if points[i][0] > points[i - 1][1]: # 气球i和气球i-1不挨着,注意这里不是>=
 8                 result += 1     
 9             else:
10                 points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界
11         return result

 435. 无重叠区间

1.按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。此时问题就是要求非交叉区间的最大个数。也可以按左边界进行排序。

  • 时间复杂度:O(nlog n) ,有一个快排
  • 空间复杂度:O(n),有一个快排,最差情况(倒序)时,需要n次递归调用。因此确实需要O(n)的栈空间
 1 class Solution:
 2     def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
 3         if not intervals:
 4             return 0
 5         
 6         intervals.sort(key=lambda x: x[0])  # 按照左边界升序排序
 7         count = 0  # 记录重叠区间数量
 8         
 9         for i in range(1, len(intervals)):
10             if intervals[i][0] < intervals[i - 1][1]:  # 存在重叠区间
11                 intervals[i][1] = min(intervals[i - 1][1], intervals[i][1])  # 更新重叠区间的右边界
12                 count += 1
13         
14         return count

763. 划分字母区间

在遍历的过程中相当于是要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。

可以分为如下两步:

  • 统计每一个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点
  • 时间复杂度:O(n)
  • 空间复杂度:O(1),使用的hash数组是固定大小
 1 class Solution:
 2     def partitionLabels(self, s: str) -> List[int]:
 3         last_occurrence = {}  # 存储每个字符最后出现的位置
 4         for i, ch in enumerate(s):
 5             last_occurrence[ch] = i
 6 
 7         result = []
 8         start = 0
 9         end = 0
10         for i, ch in enumerate(s):
11             end = max(end, last_occurrence[ch])  # 找到当前字符出现的最远位置
12             if i == end:  # 如果当前位置是最远位置,表示可以分割出一个区间
13                 result.append(end - start + 1)
14                 start = i + 1
15 
16         return result

 

 

posted on 2023-09-18 11:21  小吴要努力  阅读(5)  评论(0编辑  收藏  举报