diff --git a/problems/1014.best-sightseeing-pair.md b/problems/1014.best-sightseeing-pair.md index 0f2316caf..50efef571 100644 --- a/problems/1014.best-sightseeing-pair.md +++ b/problems/1014.best-sightseeing-pair.md @@ -23,126 +23,67 @@ https://leetcode-cn.com/problems/best-sightseeing-pair/description/ ## 思路 -这是一个很少见的直接考察`排序`的题目。 其他题目一般都是暗含`排序`,这道题则简单粗暴,直接让你排序。 -并且这道题目的难度是`Medium`, 笔者感觉有点不可思议。 - -我们先来看题目的限制条件,这其实在选择算法的过程中是重要的。 看到这道题的时候,大脑就闪现出了各种排序算法, -这也算是一个复习`排序算法`的机会吧。 - -题目的限制条件是有两个,第一是元素个数不超过 10k,这个不算大。 另外一个是数组中的每一项范围都是`-50k`到`50k`(包含左右区间)。 -看到这里,基本我就排除了时间复杂度为 O(n^2)的算法。 - -> 我没有试时间复杂度 O(n^2) 的解法,大家可以试一下,看是不是会 TLE。 - -剩下的就是基于比较的`nlogn`算法,以及基于特定条件的 O(n)算法。 - -由于平时很少用到`计数排序`等 O(n)的排序算法,一方面是空间复杂度不是常量,另一方面是其要求数据范围不是很大才行,不然会浪费很多空间。 -但是这道题我感觉可以试一下。 在这里,我用了两种方法,一种是`计数排序`,一种是`快速排序`来解决。 大家也可以尝试用别的解法来解决。 - -### 解法一 - 计数排序 - -时间复杂度 O(n)空间复杂度 O(m) m 为数组中值的取值范围,在这道题就是`50000 * 2 + 1`。 - -我们只需要准备一个数组取值范围的数字,然后遍历一遍,将每一个元素放到这个数组对应位置就好了, -放的规则是`索引为数字的值,value为出现的次数`。 - -这样一次遍历,我们统计出了所有的数字出现的位置和次数。 我们再来一次遍历,将其输出到即可。 - -![sort-an-array-1](../assets/problems/912.sort-an-array-1.png) - -### 解法二 - 快速排序 - -快速排序和归并排序都是分支思想来进行排序的算法, 并且二者都非常流行。 快速排序的核心点在于选择轴元素。 - -每次我们将数组分成两部分,一部分是比 pivot(轴元素)大的,另一部分是不比 pivot 大的。 我们不断重复这个过程, -直到问题的规模缩小的寻常(即只有一个元素的情况)。 - -快排的核心点在于如何选择轴元素,一般而言,选择轴元素有三种策略: +最简单的思路就是两两组合,找出最大的,妥妥超时,我们来看下代码: + +```python +class Solution: + def maxScoreSightseeingPair(self, A: List[int]) -> int: + n = len(A) + res = 0 + for i in range(n - 1): + for j in range(i + 1, n): + res = max(res, A[i] + A[j] + i - j) + return res +``` -- 数组最左边的元素 -- 数组最右边的元素 -- 数组中间的元素(我采用的是这种,大家可以尝试下别的) -- 数组随机一项元素 +我们思考如何优化。 其实我们可以遍历一遍数组,对于数组的每一项`A[j] - j` 我们都去前面找`最大`的 A[i] + i (这样才能保证结果最大)。 -![sort-an-array-2](../assets/problems/912.sort-an-array-2.png) +我们考虑使用动态规划来解决, 我们使用 dp[i] 来表示 数组 A 前 i 项的`A[i] + i`的最大值。 -(图片来自: https://www.geeksforgeeks.org/quick-sort/) +```python +class Solution: + def maxScoreSightseeingPair(self, A: List[int]) -> int: + n = len(A) + dp = [float('-inf')] * (n + 1) + res = 0 + for i in range(n): + dp[i + 1] = max(dp[i], A[i] + i) + res = max(res, dp[i] + A[i] - i) + return res +``` -> 图片中的轴元素是最后面的元素,而提供的解法是中间元素,这点需要注意,但是这并不影响理解。 +如上其实我们发现,dp[i + 1] 只和 dp[i] 有关,这是一个空间优化的信号。我们其实可以使用一个变量来记录,而不必要使用一个数组,代码见下方。 ## 关键点解析 -- 排序算法 -- 注意题目的限制条件从而选择合适的算法 - ## 代码 -计数排序: - -代码支持: JavaScript - -```js -/** - * @param {number[]} nums - * @return {number[]} - */ -var sortArray = function(nums) { - const counts = Array(50000 * 2 + 1).fill(0); - const res = []; - for (const num of nums) counts[50000 + num] += 1; - for (let i in counts) { - while (counts[i]--) { - res.push(i - 50000); - } - } - return res; -}; +```python +class Solution: + def maxScoreSightseeingPair(self, A: List[int]) -> int: + n = len(A) + pre = A[0] + 0 + res = 0 + for i in range(1, n): + res = max(res, pre + A[i] - i) + pre = max(pre, A[i] + i) + return res ``` -快速排序: - -代码支持: JavaScript - -```js -function swap(nums, a, b) { - const temp = nums[a]; - nums[a] = nums[b]; - nums[b] = temp; -} - -function helper(nums, start, end) { - if (start >= end) return; - const pivotIndex = start + ((end - start) >>> 1); - const pivot = nums[pivotIndex]; - let i = start; - let j = end; - while (i <= j) { - while (nums[i] < pivot) i++; - while (nums[j] > pivot) j--; - if (i <= j) { - swap(nums, i, j); - i++; - j--; - } - } - helper(nums, start, j); - helper(nums, i, end); -} - -/** - * @param {number[]} nums - * @return {number[]} - */ -var sortArray = function(nums) { - helper(nums, 0, nums.length - 1); - return nums; -}; +## 小技巧 + +Python 的代码如果不使用 max,而是使用 if else 效率目测会更高,大家可以试一下。 + +```python +class Solution: + def maxScoreSightseeingPair(self, A: List[int]) -> int: + n = len(A) + pre = A[0] + 0 + res = 0 + for i in range(1, n): + # res = max(res, pre + A[i] - i) + # pre = max(pre, A[i] + i) + res = res if res > pre + A[i] - i else pre + A[i] - i + pre = pre if pre > A[i] + i else A[i] + i + return res ``` - -## 扩展 - -- 你是否可以用其他方式排序算法解决 - -## 参考 - -- [QuickSort - geeksforgeeks](https://www.geeksforgeeks.org/quick-sort/)