Skip to content

Commit

Permalink
doc: 01-02-912
Browse files Browse the repository at this point in the history
  • Loading branch information
ZL-Asica committed Nov 26, 2024
1 parent 3b5d6bf commit 8879004
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 24 deletions.
183 changes: 183 additions & 0 deletions docs/01-Array/02-Array-Sort/Questions/05-Sort_an_Array.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
---
title: '912. Sort an Array(排序数组)'
description: 'Given an array of integers nums, sort the array in ascending order and return it.'
keywords:
- algorithms
- leetcode
- array
- sorting
- Sort an Array
- Divide and Conquer
- Heap (Priority Queue)
- Merge Sort
- Bucket Sort
- Radix Sort
- Counting Sort
slug: /problems/sort-an-array
---

题目链接:
[912. Sort an Array](https://leetcode.com/problems/sort-an-array/)

> Difficulty: Medium
>
> Topics: Arrary, Sorting, Divide and Conquer, Heap (Priority Queue), Merge Sort, Bucket Sort, Radix Sort, Counting Sort.
## Question

Given an array of integers `nums`, sort the array in ascending order and return it.

You must solve the problem **without using any built-in** functions in `O(nlog(n))` time complexity and with the smallest space complexity possible.

**Example 1:**

```plaintext
Input: nums = [5,2,3,1]
Output: [1,2,3,5]
Explanation: After sorting the array, the positions of some numbers are not changed (for example, 2 and 3), while the positions of other numbers are changed (for example, 1 and 5).
```

**Example 2:**

```plaintext
Input: nums = [5,1,1,2,0,0]
Output: [0,0,1,1,2,5]
Explanation: Note that the values of nums are not necessairly unique.
```

**Constraints:**

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

## 解题思路

### 方法1:

#### 思路

看到排序,优先考虑快速排序。但这道题我用快速排序超时了,所以我就想试一下 [希尔排序(shell sort)](https://en.wikipedia.org/wiki/Shellsort)

希尔排序是基于插入排序的改进算法,引入了 gap 的概念,每次都对 gap 进行缩小,直到回归到 1 也就是传统的插入排序。

#### 代码

```python
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
n = len(nums)
gap = n // 2 # 初始步长

# 不断缩小步长,直到为 1
while gap > 0:
for i in range(gap, n):
temp = nums[i] # 暂存当前元素
j = i
# 对当前子序列进行插入排序
while j >= gap and nums[j - gap] > temp:
nums[j] = nums[j - gap]
j -= gap
nums[j] = temp # 插入到正确位置
gap //= 2 # 缩小步长
return nums
```

#### 复杂度

- 时间:$O(n\log{n})$,因为希尔排序的时间复杂度是 $O(n\log{n})$。
- 空间:$O(1)$,因为我们只使用了常数个额外空间。这点比快速排序要好(因为那个要递归)。

### 方法2:

#### 思路

排序的话因为我看到示例中有很多重复出现的元素,就想到了[记数排序(Counting Sort)](https://en.wikipedia.org/wiki/Counting_sort)

那么在这里我们可以先开一个 `defaultdict` 来统计每个元素出现的次数,然后再根据这个次数来构建排序后的数组。

#### 代码

```python
from collections import defaultdict


class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
nums_min, nums_max = min(nums), max(nums)

# 记录每个元素出现的次数
size = nums_max - nums_min + 1 # 最大计数长度
counts = defaultdict(int)
for num in nums:
counts[num - nums_min] += 1

# 计算每个元素的位置(index/下标)
for i in range(1, size):
counts[i] += counts[i - 1]

# 构建排序后的数组(空的,都是 0)
res = [0 for _ in range(len(nums))]

# 反向遍历原数组,根据 counts 数组来确定元素的位置
for i in range(len(nums) - 1, -1, -1):
num = nums[i]
res[counts[num - nums_min] - 1] = num
# 放到 res 数组中,这个元素的位置就要往前移动一位
counts[nums[i] - nums_min] -= 1

return res
```

#### 复杂度

- 时间:$O(n + k)$,其中 $n$ 是数组的长度,$k$ 是数组中元素的范围。
- 空间:$O(n + k)$,我们需要额外的空间来存储计数数组和排序后的数组。

### 方法3:

#### 思路

我们再换一个思路,这次我们用[桶排序(Bucket Sort)](https://en.wikipedia.org/wiki/Bucket_sort)

桶排序的基本思想就是将整个 `nums` 数组分割到若干个大小为 `bucket_size` (在我们这里用的5)的桶中,然后对每个桶进行插入排序,最后将所有的桶合并起来。当然,在分配的时候是按照元素的范围来分配的(也就是当前元素与最小元素的差值整除 `bucket_size`)。这样的话,我们就可以保证每个桶中的区间范围都比较小,从而可以使用插入排序。

#### 代码

```python
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def insertionSort(nums): # 插入排序,对每个桶进行排序
for i in range(1, len(nums)):
temp = nums[i]
j = i
while j > 0 and nums[j - 1] > temp:
nums[j] = nums[j - 1]
j -= 1
nums[j] = temp

# 获取最大值和最小值
nums_min, nums_max = min(nums), max(nums)

# 每个桶的大小
bucket_size = 5

# 桶的个数,总范围 / 桶的大小
bucket_count = (nums_max - nums_min) // bucket_size + 1
buckets = [[] for _ in range(bucket_count)]

# 根据元素的范围分配到不同的桶中
for num in nums:
buckets[(num - nums_min) // bucket_size].append(num)

res = []
for bucket in buckets:
insertionSort(bucket) # 对每个桶进行插入排序
res.extend(bucket) # 合并所有的桶

return res
```

#### 复杂度

- 时间:$O(n)$,当输入元素个数为 `n` 时,桶的个数为 `m` 时,每个桶里的数据个数就是 $k = \frac{n}{m}$,每个桶内部使用插入排序,时间复杂度为 $O(k \log{k})$,所以总的时间复杂度为 $O(n + m \cdot k \log{k})$,当桶的个数 `m` 接近 `n` 时,时间复杂度就是 $O(n)$。
- 空间:$O(n + m)$,我们需要额外的空间来存储桶和排序后的数组。
48 changes: 24 additions & 24 deletions docs/01-Array/02-Array-Sort/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,49 +24,49 @@ slug: ./

## d. 希尔排序题目

| 题号 | 标题 | 题解 | 难度 |
| :--- | :------------------------------------------- | :----------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 0506 | [Relative Ranks(相对名次)][relative-ranks] | [Python](./) | 简单 |
| 题号 | 标题 | 题解 | 难度 |
| :--- | :------------------------------------------- | :---------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 0506 | [Relative Ranks(相对名次)][relative-ranks] | [Python](./) | 简单 |

## e. 归并排序题目

| 题号 | 标题 | 题解 | 难度 |
| :------------ | :------------------------------------------------------------------------------------------------------- | :----------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 0088 | [Merge Sorted Array(合并两个有序数组)][merge-sorted-array] | [Python](./) | 简单 |
| 剑指 Offer 51 | [数组中的逆序对][szzdnxd] | [Python](./) | 困难 |
| 0315 | [Count of Smaller Numbers After Self(计算右侧小于当前元素的个数)][count-of-smaller-numbers-after-self] | [Python](./) | 困难 |
| 题号 | 标题 | 题解 | 难度 |
| :------------ | :------------------------------------------------------------------------------------------------------- | :---------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 0088 | [Merge Sorted Array(合并两个有序数组)][merge-sorted-array] | [Python](./) | 简单 |
| 剑指 Offer 51 | [数组中的逆序对][szzdnxd] | [Python](./) | 困难 |
| 0315 | [Count of Smaller Numbers After Self(计算右侧小于当前元素的个数)][count-of-smaller-numbers-after-self] | [Python](./) | 困难 |

## f. 快速排序题目

| 题号 | 标题 | 题解 | 难度 |
| :--- | :----------------------------------------------- | :----------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 0169 | [Majority Element(多数元素)][majority-element] | [Python](./) | 简单 |
| 题号 | 标题 | 题解 | 难度 |
| :--- | :----------------------------------------------- | :---------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 0169 | [Majority Element(多数元素)][majority-element] | [Python](./) | 简单 |

## g. 堆排序题目

| 题号 | 标题 | 题解 | 难度 |
| :------------ | :------------------------------------------------------------------------------------------ | :---------------------------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 0215 | [Kth Largest Element in an Array(数组中的第K个最大元素)][kth-largest-element-in-an-array] | [Python](./Questions/03-Kth_Largest_Element_in_an_Array.md) | 中等 |
| 剑指 Offer 40 | [最小的k个数][zui-xiao-de-kge-shu-lcof] | [Python](./) | 简单 |

## h. 计数排序题目

| 题号 | 标题 | 题解 | 难度 |
| :--- | :----------------------------------------------------------- | :----------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 1122 | [Relative Sort Array(数组的相对排序)][relative-sort-array] | [Python](./) | 简单 |
| 题号 | 标题 | 题解 | 难度 |
| :--- | :----------------------------------------------------------- | :---------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 1122 | [Relative Sort Array(数组的相对排序)][relative-sort-array] | [Python](./) | 简单 |

## i. 桶排序题目

| 题号 | 标题 | 题解 | 难度 |
| :--- | :------------------------------------------------------------------- | :----------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./) | 中等 |
| 0220 | [Contains Duplicate III(存在重复元素 III)][contains-duplicate-iii] | [Python](./) | 困难 |
| 0164 | [Maximum Gap(最大间距)][maximum-gap] | [Python](./) | 困难 |
| 题号 | 标题 | 题解 | 难度 |
| :--- | :------------------------------------------------------------------- | :---------------------------------------- | :--- |
| 0912 | [Sort an Array(排序数组)][sort-an-array] | [Python](./Questions/05-Sort_an_Array.md) | 中等 |
| 0220 | [Contains Duplicate III(存在重复元素 III)][contains-duplicate-iii] | [Python](./) | 困难 |
| 0164 | [Maximum Gap(最大间距)][maximum-gap] | [Python](./) | 困难 |

## j. 基数排序题目

Expand Down

0 comments on commit 8879004

Please sign in to comment.