|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[775. 全局倒置与局部倒置](https://leetcode.cn/problems/global-and-local-inversions/solution/by-ac_oier-jc7a/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「树状数组」、「数学」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给你一个长度为 `n` 的整数数组 `nums`,表示由范围 $[0, n - 1]$ 内所有整数组成的一个排列。 |
| 10 | + |
| 11 | +全局倒置 的数目等于满足下述条件不同下标对 (i, j) 的数目: |
| 12 | + |
| 13 | +* 0ドル <= i < j < n$ |
| 14 | +* $nums[i] > nums[j]$ |
| 15 | + |
| 16 | +局部倒置 的数目等于满足下述条件的下标 i 的数目: |
| 17 | + |
| 18 | +* 0ドル <= i < n - 1$ |
| 19 | +* $nums[i] > nums[i + 1]$ |
| 20 | + |
| 21 | +当数组 `nums` 中 全局倒置 的数量等于 局部倒置 的数量时,返回 `true` ;否则,返回 `false` 。 |
| 22 | + |
| 23 | +示例 1: |
| 24 | +``` |
| 25 | +输入:nums = [1,0,2] |
| 26 | + |
| 27 | +输出:true |
| 28 | + |
| 29 | +解释:有 1 个全局倒置,和 1 个局部倒置。 |
| 30 | +``` |
| 31 | +示例 2: |
| 32 | +``` |
| 33 | +输入:nums = [1,2,0] |
| 34 | + |
| 35 | +输出:false |
| 36 | + |
| 37 | +解释:有 2 个全局倒置,和 1 个局部倒置。 |
| 38 | +``` |
| 39 | + |
| 40 | +提示: |
| 41 | +* $n == nums.length$ |
| 42 | +* 1ドル <= n <= 10^5$ |
| 43 | +* 0ドル <= nums[i] < n$ |
| 44 | +* `nums` 中的所有整数 互不相同 |
| 45 | +* `nums` 是范围 $[0, n - 1]$ 内所有数字组成的一个排列 |
| 46 | + |
| 47 | +--- |
| 48 | + |
| 49 | +### 树状数组 |
| 50 | + |
| 51 | +根据题意,对于每个 $nums[i]$ 而言: |
| 52 | + |
| 53 | +* 其左边比它大的 $nums[j]$ 的个数,是以 $nums[i]$ 为右端点的"全局倒置"数量,统计所有以 $nums[i]$ 为右端点的"全局倒置"数量即是总的"全局倒置"数量 `a` |
| 54 | + |
| 55 | +* 同时我们可以将每个 $nums[i]$ 与前一个值进行比较,从而统计总的"局部倒置"数量 `b`,其中 $i$ 的取值范围为 $[1, n - 1)$ |
| 56 | + |
| 57 | +一个容易想到的做法是利用「树状数组」,虽然该做法没有利用到核心条件「$nums$ 是一个 $[0, n - 1]$ 的排列」,但根据数据范围 $n$ 可知该复杂度为 $O(n\log{n})$ 的做法可过,且依赖的条件更少,适用范围更广。 |
| 58 | + |
| 59 | +代码: |
| 60 | +```Java |
| 61 | +class Solution { |
| 62 | + int n; |
| 63 | + int[] tr; |
| 64 | + int lowbit(int x) { |
| 65 | + return x & -x; |
| 66 | + } |
| 67 | + void add(int x) { |
| 68 | + for (int i = x; i <= n; i += lowbit(i)) tr[i]++; |
| 69 | + } |
| 70 | + int query(int x) { |
| 71 | + int ans = 0; |
| 72 | + for (int i = x; i > 0; i -= lowbit(i)) ans += tr[i]; |
| 73 | + return ans; |
| 74 | + } |
| 75 | + public boolean isIdealPermutation(int[] nums) { |
| 76 | + n = nums.length; |
| 77 | + tr = new int[n + 10]; |
| 78 | + add(nums[0] + 1); |
| 79 | + int a = 0, b = 0; |
| 80 | + for (int i = 1; i < n; i++) { |
| 81 | + a += query(n) - query(nums[i] + 1); |
| 82 | + b += nums[i] < nums[i - 1] ? 1 : 0; |
| 83 | + add(nums[i] + 1); |
| 84 | + } |
| 85 | + return a == b; |
| 86 | + } |
| 87 | +} |
| 88 | +``` |
| 89 | +* 时间复杂度:$O(n\log{n})$ |
| 90 | +* 空间复杂度:$O(n)$ |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +### 数学 |
| 95 | + |
| 96 | +解法一中并没有利用到核心条件「$nums$ 是一个 $[0, n - 1]$ 的排列」,我们可以从该条件以及两类倒置的定义出发进行分析。 |
| 97 | + |
| 98 | +#### 提示一:由"局部倒置"组成的集合为由"全局倒置"组成的集合的子集 |
| 99 | + |
| 100 | +任意一个"局部倒置"均满足"全局倒置"的定义,因此要判定两者数量是否相同,可转换为统计是否存在「不满足"局部倒置"定义的"全局倒置"」。 |
| 101 | + |
| 102 | +#### 提示二:何为不满足"局部倒置"定义的"全局倒置" |
| 103 | + |
| 104 | +结合题意,若存在坐标 $j$ 和 $i,ドル满足 $nums[j] > nums[i]$ 且 $j + 1 < i,ドル那么该倒置满足"全局倒置"定义,且不满足"局部倒置"定义。 |
| 105 | + |
| 106 | +若存在这样的逆序对,不满足,则有两类倒置数量不同。 |
| 107 | + |
| 108 | +#### 提示三:考虑「如何构造」或「如何避免构造」不满足"全局倒置"定义的"局部倒置" |
| 109 | + |
| 110 | +如果我们能够总结出「如何构造」或「如何避免构造」一个不满足"全局倒置"定义的"局部倒置" 所需的条件,问题可以转换为检查 `nums` 是否满足这样的条件,来得知 `nums` 是否存在不满足"全局倒置"定义的"局部倒置"。 |
| 111 | + |
| 112 | +我们可以结合「$nums$ 是一个 $[0, n - 1]$ 的排列」来分析,若需要避免所有 $nums[j] > nums[i]$ 的逆序对均不满足 $j + 1 < i,ドル只能是所有逆序对均由相邻数值产生。 |
| 113 | + |
| 114 | +代码: |
| 115 | +```Java |
| 116 | +class Solution { |
| 117 | + public boolean isIdealPermutation(int[] nums) { |
| 118 | + for (int i = 0; i < nums.length; i++) { |
| 119 | + if (Math.abs(nums[i] - i) >= 2) return false; |
| 120 | + } |
| 121 | + return true; |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | +* 时间复杂度:$O(n)$ |
| 126 | +* 空间复杂度:$O(1)$ |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +### 最后 |
| 131 | + |
| 132 | +这是我们「刷穿 LeetCode」系列文章的第 `No.775` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 133 | + |
| 134 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 135 | + |
| 136 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 137 | + |
| 138 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 139 | + |
0 commit comments