Skip to content

Commit

Permalink
更新题解列表
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Sep 13, 2023
1 parent 2876ed0 commit 765d916
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 42 deletions.
70 changes: 36 additions & 34 deletions Solutions/0215. 数组中的第K个最大元素.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,50 +114,52 @@ class Solution:
import random

class Solution:
# 从 arr[low: high + 1] 中随机挑选一个基准数,并进行移动排序
def randomPartition(self, arr: [int], low: int, high: int):
# 随机哨兵划分:从 nums[low: high + 1] 中随机挑选一个基准数,并进行移位排序
def randomPartition(self, nums: [int], low: int, high: int) -> int:
# 随机挑选一个基准数
i = random.randint(low, high)
# 将基准数与最低位互换
arr[i], arr[low] = arr[low], arr[i]
# 以最低位为基准数,然后将序列中比基准数大的元素移动到基准数右侧,比他小的元素移动到基准数左侧。最后将基准数放到正确位置上
return self.partition(arr, low, high)
nums[i], nums[low] = nums[low], nums[i]
# 以最低位为基准数,然后将数组中比基准数大的元素移动到基准数右侧,比他小的元素移动到基准数左侧。最后将基准数放到正确位置上
return self.partition(nums, low, high)

# 以最低位为基准数,然后将序列中比基准数大的元素移动到基准数右侧,比他小的元素移动到基准数左侧。最后将基准数放到正确位置上
def partition(self, arr: [int], low: int, high: int):
pivot = arr[low] # 以第 1 为为基准数
i = low + 1 # 从基准数后 1 位开始遍历,保证位置 i 之前的元素都小于基准数
# 哨兵划分:以第 1 位元素 nums[low] 为基准数,然后将比基准数小的元素移动到基准数左侧,将比基准数大的元素移动到基准数右侧,最后将基准数放到正确位置上
def partition(self, nums: [int], low: int, high: int) -> int:
# 以第 1 位元素为基准数
pivot = nums[low]

for j in range(i, high + 1):
# 发现一个小于基准数的元素
if arr[j] < pivot:
# 将小于基准数的元素 arr[j] 与当前 arr[i] 进行换位,保证位置 i 之前的元素都小于基准数
arr[i], arr[j] = arr[j], arr[i]
# i 之前的元素都小于基准数,所以 i 向右移动一位
i, j = low, high
while i < j:
# 从右向左找到第 1 个小于基准数的元素
while i < j and nums[j] >= pivot:
j -= 1
# 从左向右找到第 1 个大于基准数的元素
while i < j and nums[i] <= pivot:
i += 1
# 将基准节点放到正确位置上
arr[i - 1], arr[low] = arr[low], arr[i - 1]
# 返回基准数位置
return i - 1
# 交换元素
nums[i], nums[j] = nums[j], nums[i]

# 将基准数放到正确位置上
nums[j], nums[low] = nums[low], nums[j]
return j

def quickSort(self, arr, low, high, k):
size = len(arr)
def quickSort(self, nums: [int], low: int, high: int, k: int, size: int) -> [int]:
if low < high:
# 按照基准数的位置,将序列划分为左右两个子序列
pi = self.randomPartition(arr, low, high)
if pi == size - k:
return arr[size - k]
if pi > size - k:
# 对左子序列进行递归快速排序
self.quickSort(arr, low, pi - 1, k)
if pi < size - k:
# 对右子序列进行递归快速排序
self.quickSort(arr, pi + 1, high, k)

return arr[size - k]
# 按照基准数的位置,将数组划分为左右两个子数组
pivot_i = self.randomPartition(nums, low, high)
if pivot_i == size - k:
return nums[size - k]
if pivot_i > size - k:
self.quickSort(nums, low, pivot_i - 1, k, size)
if pivot_i < size - k:
self.quickSort(nums, pivot_i + 1, high, k, size)

return nums[size - k]


def findKthLargest(self, nums: List[int], k: int) -> int:
return self.quickSort(nums, 0, len(nums) - 1, k)
size = len(nums)
return self.quickSort(nums, 0, len(nums) - 1, k, size)
```

### 思路 2:复杂度分析
Expand Down
121 changes: 113 additions & 8 deletions Solutions/0315. 计算右侧小于当前元素的个数.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,122 @@

## 题目大意

给定一个整数数组 `nums`
**描述**给定一个整数数组 $nums$

要求:返回一个新数组 `counts` 。其中 `counts[i]` 的值是 `nums[i]` 右侧小于 `nums[i]` 的元素的数量。
**要求**:返回一个新数组 $counts$ 。其中 $counts[i]$ 的值是 $nums[i]$ 右侧小于 $nums[i]$ 的元素的数量。

**说明**

- $1 \le nums.length \le 10^5$。
- $-104 \le nums[i] \le 10^4$。

**示例**

- 示例 1:

```python
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (21)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
```

- 示例 2:

```python
输入:nums = [-1]
输出:[0]
```

## 解题思路

可以用树状数组解决。
### 思路 1:归并排序

在使用归并排序对数组进行排序时,每当遇到 $left\underline{}nums[left\underline{}i] \le right\underline{}nums[right\underline{}i]$ 时,意味着:在合并前,左子数组当前元素 $left\underline{}nums[left\underline{}i]$ 右侧一定有 $left\underline{}i$ 个元素比 $left\underline{}nums[left\underline{}i]$ 小。则我们可以在归并排序的同时,记录 $nums[i]$ 右侧小于 $nums[i]$ 的元素的数量。

1. 将元素值、对应下标、右侧小于 nums[i] 的元素的数量存入数组中。
2. 对其进行归并排序。
3. 当遇到 $left\underline{}nums[left\underline{}i] \le right\underline{}nums[right\underline{}i]$ 时,记录 $left\underline{}nums[left\underline{}i]$ 右侧比 $left\underline{}nums[left\underline{}i]$ 小的元素数量,即:`left_nums[left_i][2] += right_i`
4. 当合并时 $left\underline{}nums[left\underline{}i]$ 仍有剩余时,说明 $left\underline{}nums[left\underline{}i]$ 右侧有 $right\underline{}i$ 个小于 $left\underline{}nums[left\underline{}i]$ 的元素,记录下来,即:`left_nums[left_i][2] += right_i`
5. 根据下标及右侧小于 $nums[i]$ 的元素的数量,组合出答案数组,并返回答案数组。

### 思路 1:代码

首先对数组进行离散化处理。把原始数组中的数据映射到 `[0, len(nums) - 1]` 这个区间。
```python
class Solution:
# 合并过程
def merge(self, left_nums, right_nums):
nums = []
left_i, right_i = 0, 0
while left_i < len(left_nums) and right_i < len(right_nums):
# 将两个有序子数组中较小元素依次插入到结果数组中
if left_nums[left_i] <= right_nums[right_i]:
nums.append(left_nums[left_i])
# left_nums[left_i] 右侧有 right_i 个比 left_nums[left_i] 小的
left_nums[left_i][2] += right_i
left_i += 1
else:
nums.append(right_nums[right_i])
right_i += 1

# 如果左子数组有剩余元素,则将其插入到结果数组中
while left_i < len(left_nums):
nums.append(left_nums[left_i])
# left_nums[left_i] 右侧有 right_i 个比 left_nums[left_i] 小的
left_nums[left_i][2] += right_i
left_i += 1

# 如果右子数组有剩余元素,则将其插入到结果数组中
while right_i < len(right_nums):
nums.append(right_nums[right_i])
right_i += 1

# 返回合并后的结果数组
return nums

# 分解过程
def mergeSort(self, nums) :
# 数组元素个数小于等于 1 时,直接返回原数组
if len(nums) <= 1:
return nums

mid = len(nums) // 2 # 将数组从中间位置分为左右两个数组
left_nums = self.mergeSort(nums[0: mid]) # 递归将左子数组进行分解和排序
right_nums = self.mergeSort(nums[mid:]) # 递归将右子数组进行分解和排序
return self.merge(left_nums, right_nums) # 把当前数组组中有序子数组逐层向上,进行两两合并


def countSmaller(self, nums: List[int]) -> List[int]:
size = len(nums)

# 将元素值、对应下标、右侧小于 nums[i] 的元素的数量存入数组中
nums = [[num, i, 0] for i, num in enumerate(nums)]
nums = self.mergeSort(nums)
ans = [0 for _ in range(size)]

- 然后逆序顺序从数组 `nums` 中遍历元素 `nums[i]`。计算其离散化后的排名 `index`,查询比 `index` 小的数有多少个。将其记录到答案数组的对应位置 `ans[i]` 上。
- 然后在树状数组下标为 `index` 的位置上,更新值为 `1`
for num in nums:
ans[num[1]] = num[2]

return ans
```

### 思路 1:复杂度分析

- **时间复杂度**:$O(n \times \log n)$。
- **空间复杂度**:$O(n)$。

重复上述步骤直到遍历完所有元素。最后输出答案数组即可。
### 思路 2:树状数组

## 代码
1. 首先对数组进行离散化处理。把原始数组中的数据映射到 $[0, len(nums) - 1]$ 这个区间。
2. 然后逆序顺序从数组 $nums$ 中遍历元素 $nums[i]$。
1. 计算其离散化后的排名 $index$,查询比 $index$ 小的数有多少个。将其记录到答案数组的对应位置 $ans[i]$ 上。
2. 然后在树状数组下标为 $index$ 的位置上,更新值为 $1$。
3. 遍历完所有元素,最后输出答案数组 $ans$ 即可。

### 思路 2:代码

```python
import bisect
Expand Down Expand Up @@ -69,3 +169,8 @@ class Solution:
return ans
```

### 思路 2:复杂度分析

- **时间复杂度**:$O(n \times \log n)$。
- **空间复杂度**:$O(n)$。

0 comments on commit 765d916

Please sign in to comment.