Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions Contents/01.Array/02.Array-Sort/08.Array-Counting-Sort.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,56 @@
## 1. 计数排序算法思想

> 计数排序(Counting Sort)基本思想:
> **计数排序(Counting Sort)基本思想**
>
> 使用一个额外的数组 `counts`,其中第 `i` 个元素 `counts[i]` 是待排序数组 `arr` 中值等于 `i` 的元素个数。然后根据数组 `counts` 来将 `arr` 中的元素排到正确的位置。
> 使用一个额外的数组 `counts`,其中 `counts[i]` 表示原数组 `arr` 中值等于 `i` 的元素个数。然后根据数组 `counts` 来将 `arr` 中的元素排到正确的位置。

## 2. 计数排序算法步骤

- 找出待排序数组中最大值元素和最小值元素。
- 统计数组中每个值为 `i` 的元素出现的次数,存入数组的第 `i` 项。
- 对所有的计数累加(从 `counts` 中的第一个元素开始,每一项和前一项累加)。
- 反向填充目标数组:将每个元素 `i` 放在新数组的第 `counts[i]` 项,每放一个元素就要将 `counts[i] -= 1`。
1. 找出待排序序列中最大值元素 `arr_max` 和最小值元素 `arr_min`。
2. 定义大小为 `arr_max - arr_min + 1` 的数组 `counts`,初始时,`counts` 中元素值全为 `0`。
3. 遍历数组 `arr`,统计值为 `num` 的元素出现的次数。将其次数存入 `counts` 数组的第 `num - arr_min` 项(`counts[num - arr_min]` 表示元素值 `num` 出现的次数)。
4. 对所有的计数累加,从 `counts` 中的第一个元素开始,每一项和前一项相加。此时 `counts[i]` 表示值为 `i` 的元素排名。
5. 反向填充目标数组:
1. 逆序遍历数组 `arr`。对于每个元素值 `arr[i]`,其对应排名为 `counts[arr[i] - arr_min]`。
2. 根据排名,将 `arr[i]` 放在数组对应位置(因为数组下标是从 `0` 开始的,所以对应位置为排名减 `1`)。即 `res[counts[arr[i] - arr_min] - 1] = arr[i]`。
3. 放入之后, 将 `arr[i]` 的对应排名减 `1`,即 `counts[arr[i] - arr_min] -= 1`。

## 3. 计数排序动画演示

![](https://www.runoob.com/wp-content/uploads/2019/03/countingSort.gif)

## 4. 计数排序算法分析

- 当输入元素是 `n` 个 `0 ~ k` 之间的整数时,计数排序的时间复杂度为 $O(n + k)$。
- 由于用于计数的数组 `counts` 的长度取决于待排序数组中数据的范围(等于待排序数组最大值减去最小值再加 `1`)。所以计数排序对于数据范围很大的数组,需要大量的时间和内存
- 计数排序一般用于排序整数,不适用于按字母顺序排序人名
- 计数排序是 **稳定排序算法**。
- **时间复杂度**:$O(n + k)$。其中 $k$ 代表待排序序列的值域
- **空间复杂度**:$O(k)$。其中 $k$ 代表待排序序列的值域。由于用于计数的数组 `counts` 的长度取决于待排序数组中数据的范围(大小等于待排序数组最大值减去最小值再加 `1`)。所以计数排序算法对于数据范围很大的数组,需要大量的内存
- **计数排序适用情况**:计数排序一般用于整数排序,不适用于按字母顺序、人名顺序排序
- **排序稳定性**:计数排序是一种 **稳定排序算法**。

## 5. 计数排序代码实现

```Python
class Solution:
def countingSort(self, arr):
# 计算待排序序列中最大值元素 arr_max 和最小值元素 arr_min
arr_min, arr_max = min(arr), max(arr)
# 定义计数数组 counts,大小为 最大值元素 - 最小值元素 + 1
size = arr_max - arr_min + 1
counts = [0 for _ in range(size)]


# 统计值为 num 的元素出现的次数
for num in arr:
counts[num - arr_min] += 1

# 计算元素排名
for j in range(1, size):
counts[j] += counts[j - 1]

# 反向填充目标数组
res = [0 for _ in range(len(arr))]
for i in range(len(arr) - 1, -1, -1):
# 根据排名,将 arr[i] 放在数组对应位置
res[counts[arr[i] - arr_min] - 1] = arr[i]
# 将 arr[i] 的对应排名减 1
counts[arr[i] - arr_min] -= 1

return res
Expand Down
27 changes: 18 additions & 9 deletions Contents/01.Array/02.Array-Sort/09.Array-Bucket-Sort.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
## 1. 桶排序算法思想

> 桶排序(Bucket Sort)基本思想:
> **桶排序(Bucket Sort)基本思想**
>
> 将未排序的数组分到若干个「桶」中,每个桶的元素再进行单独排序。
> 将未排序数组分到若干个「桶」中,每个桶的元素再进行单独排序。

## 2. 桶排序算法步骤

- 将区间划分为 `n` 个相同大小的子区间,每个区间称为一个桶。
- 遍历数组,将每个元素装入对应的桶中
- 对每个桶内的元素单独排序(使用插入、归并、快排等算法)。
- 最后按照顺序将桶内的元素合并起来
1. 根据原始数组的值域范围,将数组划分为 `k` 个相同大小的子区间,每个区间称为一个桶。
2. 遍历原始数组元素,将每个元素装入对应区间的桶中
3. 对每个桶内的元素单独排序(使用插入、归并、快排等算法)。
4. 最后按照区间顺序将桶内的元素合并起来,完成排序

## 3. 桶排序图解演示

Expand All @@ -27,33 +27,42 @@

## 4. 桶排序算法分析

- 桶排序可以在线性时间内完成排序,当输入元素个数为 `n`,桶的个数是 `m` 时,每个桶里的数据就是 `k = n / m` 个。每个桶内排序的时间复杂度为 $O(k * log_2k)$。`m` 个桶就是 $m * O(k * log_2k) = m * O((n/m)*log_2(n/m)) = O(n*log_2(n/m))$。当桶的个数 `m` 接近于数据个数 `n` 时,$log_2(n/m)$ 就是一个较小的常数,所以排序桶排序时间复杂度接近于 $O(n)$。
- 由于桶排序使用了辅助空间,所以桶排序的空间复杂度是 $o(n + m)$。
- 如果桶内使用插入排序算法等稳定排序算法,则桶排序也是 **稳定排序算法**。
- **时间复杂度**:$O(n)$。当输入元素个数为 $n$,桶的个数是 $m$ 时,每个桶里的数据就是 $k = n / m$ 个。每个桶内排序的时间复杂度为 $O(k \times \log_2 k)$。$m$ 个桶就是 $m * O(k * log_2k) = m \times O((n / m) \times \log_2(n/m)) = O(n*log_2(n/m))$。当桶的个数 $m$ 接近于数据个数 $n$ 时,$log_2(n/m)$ 就是一个较小的常数,所以排序桶排序时间复杂度接近于 $O(n)$。
- **空间复杂度**:$O(n + m)$。由于桶排序使用了辅助空间,所以桶排序的空间复杂度是 $O(n + m)$。
- **排序稳定性**:如果桶内使用插入排序算法等稳定排序算法,则桶排序也是一种 **稳定排序算法**。

## 5. 桶排序代码实现

```Python
class Solution:
def insertionSort(self, arr):
# 遍历无序序列
for i in range(1, len(arr)):
temp = arr[i]
j = i
# 从右至左遍历有序序列
while j > 0 and arr[j - 1] > temp:
# 将有序序列中插入位置右侧的元素依次右移一位
arr[j] = arr[j - 1]
j -= 1
# 将该元素插入到适当位置
arr[j] = temp

return arr

def bucketSort(self, arr, bucket_size=5):
# 计算待排序序列中最大值元素 arr_max 和最小值元素 arr_min
arr_min, arr_max = min(arr), max(arr)
# 定义桶的个数为 (最大值元素 - 最小值元素) // 每个桶的大小 + 1
bucket_count = (arr_max - arr_min) // bucket_size + 1
# 定义桶数组 buckets
buckets = [[] for _ in range(bucket_count)]

# 遍历原始数组元素,将每个元素装入对应区间的桶中
for num in arr:
buckets[(num - arr_min) // bucket_size].append(num)

# 对每个桶内的元素单独排序,并合并到 res 数组中
res = []
for bucket in buckets:
self.insertionSort(bucket)
Expand Down
22 changes: 14 additions & 8 deletions Contents/01.Array/02.Array-Sort/10.Array-Radix-Sort.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## 1. 基数排序算法思想

> 基数排序(Radix Sort)基本思想:
> **基数排序(Radix Sort)基本思想**
>
> 将整数按位数切割成不同的数字,然后按每个位数分别比较进行排序。

Expand All @@ -10,33 +10,39 @@

下面我们以最低位优先法为例,讲解一下算法步骤。

- 遍历数组元素,获取数组最大值元素,并取得位数。
- 以个位元素为索引,对数组元素排序。
- 合并数组。
- 之后依次以十位,百位,…,直到最大值元素的最高位处值为索引,进行排序,并合并数组,最终完成排序。
1. 遍历数组元素,获取数组最大值元素,并取得位数。
2. 以个位元素为索引,对数组元素排序。
3. 合并数组。
4. 之后依次以十位,百位,…,最大值元素的最高位 为索引,进行排序,并合并数组,最终完成排序。

## 3. 基数排序动画演示

![](https://www.runoob.com/wp-content/uploads/2019/03/radixSort.gif)

## 4. 基数排序算法分析

- 基数排序的时间复杂度是 $O(k * n)$。其中 `n` 是待排序元素的个数,`k` 是数字位数。`k` 的大小取决于数字位的选择(十进制位、二进制位)和待排序元素所属数据类型全集的大小。
- 基数排序的空间复杂度是 $O(n + k)$。
- 基数排序是 **稳定排序算法**。
- **时间复杂度**:$O(k * n)$。其中 $n$ 是待排序元素的个数,$k$ 是数字位数。$k$ 的大小取决于数字位的选择(十进制位、二进制位)和待排序元素所属数据类型全集的大小。
- **空间复杂度**:$O(n + k)$。
- **排序稳定性**:基数排序是一种 **稳定排序算法**。

## 5. 基数排序代码实现

```Python
class Solution:
def radixSort(self, arr):
# 桶的大小为所有元素的最大位数
size = len(str(max(arr)))

# 从低位到高位依次遍历每一位,以各个数位值为索引,对数组进行按数位排序
for i in range(size):
# 使用一个长度为 10 的桶来存放各个位上的元素
buckets = [[] for _ in range(10)]
# 遍历数组元素,根据元素对应位上的值,将其存入对应位的桶中
for num in arr:
buckets[num // (10 ** i) % 10].append(num)
# 清空原始数组
arr.clear()
# 从桶中依次取出对应元素,并重新加入到原始数组
for bucket in buckets:
for num in bucket:
arr.append(num)
Expand Down