插入排序是一种简单直观的排序算法,适合用于排序小规模数据集。它的基本思想是通过构建一个有序序列,将未排序的数据逐个插入到已排序的序列中。以下是插入排序的基本步骤:
1. 从第一个元素开始,该元素可以认为已经被排序。
2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
4. 重复步骤3,直到找到已排序的元素小于或等于新元素的位置。
5. 将新元素插入到该位置后。
6. 重复步骤2~5。
以下是插入排序的一个简单实现(以Python为例):
```python
def insertion_sort(arr):
for i in range(1, len(arr)): # 从第一个元素开始,所以从索引1开始
key = arr[i] # 记录当前要插入的元素
j = i - 1
# 将大于key的元素向后移动
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key # 插入元素
return arr
# 示例使用
arr = [12, 11, 13, 5, 6]
sorted_arr = insertion_sort(arr)
print("排序后的数组:", sorted_arr)
```
这个算法的时间复杂度为 \(O(n^2)\),其中 \(n\) 是数组的长度。在数组基本有序的情况下,插入排序效率较高。希望这个解释能帮助你理解插入排序的工作原理!
当然,下面是对这段插入排序代码的逐行逐词解释:
```python
def insertion_sort(arr):
```
- `def`: 定义一个函数。
- `insertion_sort`: 函数的名称。
- `arr`: 函数的参数,表示要排序的数组。
```python
for i in range(1, len(arr)): # 从第一个元素开始,所以从索引1开始
```
- `for`: 开始一个循环。
- `i`: 循环变量,表示当前要处理的元素的索引。
- `in`: 用于指定循环的范围。
- `range(1, len(arr))`: 生成从1到数组长度减1的数字序列。我们从索引1开始,因为索引0的元素可以认为是已经被排序的。
- `len(arr)`: 获取数组的长度。
```python
key = arr[i] # 记录当前要插入的元素
```
- `key`: 临时变量,用于存储当前要插入到已排序部分的元素。
- `arr[i]`: 获取数组中索引为`i`的元素。
```python
j = i - 1
```
- `j`: 初始化为`i`的前一个索引,用于遍历已排序部分。
- `i - 1`: 计算当前索引的前一个位置。
```python
while j >= 0 and arr[j] > key:
```
- `while`: 开始一个循环,只要条件为真就继续执行。
- `j >= 0`: 确保索引`j`在数组范围内。
- `and`: 逻辑与操作符。
- `arr[j] > key`: 检查已排序部分的当前元素是否大于`key`。
```python
arr[j + 1] = arr[j]
```
- `arr[j + 1]`: 将当前元素的下一个位置设置为当前元素。
- `=`: 赋值操作。
- `arr[j]`: 获取当前已排序部分的元素。
```python
j -= 1
```
- `j -= 1`: 将`j`的值减1,用于向前移动到下一个元素。
```python
arr[j + 1] = key # 插入元素
```
- `arr[j + 1]`: 将`key`插入到正确的位置。
- `=`: 赋值操作。
- `key`: 放置在正确位置的当前元素。
```python
return arr
```
- `return`: 返回排序后的数组。
- `arr`: 已排序的数组。
```python
# 示例使用
arr = [12, 11, 13, 5, 6]
```
- `arr = [12, 11, 13, 5, 6]`: 定义一个要排序的数组。
```python
sorted_arr = insertion_sort(arr)
```
- `sorted_arr`: 存储排序后的数组。
- `insertion_sort(arr)`: 调用`insertion_sort`函数,对`arr`进行排序。
```python
print("排序后的数组:", sorted_arr)
```
- `print`: 输出函数。
- `"排序后的数组:"`: 输出的提示信息。
- `sorted_arr`: 打印排序后的数组。
这段代码实现了插入排序算法,用于对数组`arr`进行排序并输出结果。
好的,让我们一步步地跟踪插入排序算法如何对数组 `[12, 11, 13, 5, 6]` 进行排序。
### 初始状态
- 数组:`[12, 11, 13, 5, 6]`
### 第一次迭代 (`i = 1`)
- `key = arr[1] = 11`
- `j = 0`
- 比较 `arr[j] (12)` 和 `key (11)`:
- 因为 `12 > 11`,所以将 `arr[j + 1] = arr[j]`,数组变为 `[12, 12, 13, 5, 6]`
- `j -= 1`,所以 `j = -1`
- 插入 `key` 到 `arr[j + 1]`,即 `arr[0] = 11`,数组变为 `[11, 12, 13, 5, 6]`
### 第二次迭代 (`i = 2`)
- `key = arr[2] = 13`
- `j = 1`
- 比较 `arr[j] (12)` 和 `key (13)`:
- 因为 `12 <= 13`,不需要移动元素
- 插入 `key` 到 `arr[j + 1]`,即 `arr[2] = 13`(保持不变),数组仍为 `[11, 12, 13, 5, 6]`
### 第三次迭代 (`i = 3`)
- `key = arr[3] = 5`
- `j = 2`
- 比较 `arr[j] (13)` 和 `key (5)`:
- 因为 `13 > 5`,将 `arr[j + 1] = arr[j]`,数组变为 `[11, 12, 13, 13, 6]`
- `j -= 1`,所以 `j = 1`
- 比较 `arr[j] (12)` 和 `key (5)`:
- 因为 `12 > 5`,将 `arr[j + 1] = arr[j]`,数组变为 `[11, 12, 12, 13, 6]`
- `j -= 1`,所以 `j = 0`
- 比较 `arr[j] (11)` 和 `key (5)`:
- 因为 `11 > 5`,将 `arr[j + 1] = arr[j]`,数组变为 `[11, 11, 12, 13, 6]`
- `j -= 1`,所以 `j = -1`
- 插入 `key` 到 `arr[j + 1]`,即 `arr[0] = 5`,数组变为 `[5, 11, 12, 13, 6]`
### 第四次迭代 (`i = 4`)
- `key = arr[4] = 6`
- `j = 3`
- 比较 `arr[j] (13)` 和 `key (6)`:
- 因为 `13 > 6`,将 `arr[j + 1] = arr[j]`,数组变为 `[5, 11, 12, 13, 13]`
- `j -= 1`,所以 `j = 2`
- 比较 `arr[j] (12)` 和 `key (6)`:
- 因为 `12 > 6`,将 `arr[j + 1] = arr[j]`,数组变为 `[5, 11, 12, 12, 13]`
- `j -= 1`,所以 `j = 1`
- 比较 `arr[j] (11)` 和 `key (6)`:
- 因为 `11 > 6`,将 `arr[j + 1] = arr[j]`,数组变为 `[5, 11, 11, 12, 13]`
- `j -= 1`,所以 `j = 0`
- 比较 `arr[j] (5)` 和 `key (6)`:
- 因为 `5 <= 6`,不需要移动元素
- 插入 `key` 到 `arr[j + 1]`,即 `arr[1] = 6`,数组变为 `[5, 6, 11, 12, 13]`
### 最终结果
- 排序完成后的数组是 `[5, 6, 11, 12, 13]`
这个过程展示了插入排序如何通过逐步构建一个有序的数组部分来完成排序。
插入排序的时间复杂度分析主要包括两个方面:**最佳情况** 和 **最坏情况**。我们来一步步分析这两种情况。
### 最佳情况
在最佳情况下,数组已经是有序的。对于一个长度为 \( n \) 的数组:
1. 外层循环运行 \( n-1 \) 次(从索引 1 到 \( n-1 \))。
2. 内层 `while` 循环的条件 `arr[j] > key` 总是为假,因为数组已经有序。
3. 因此,内层 `while` 循环不会执行移动操作。
**时间复杂度**:
- 外层循环:运行 \( n-1 \) 次。
- 内层循环:0 次(因为数组有序)。
总的来说,最佳情况下的时间复杂度是 \( O(n) \)。
### 最坏情况
在最坏情况下,数组是按降序排列的。对于一个长度为 \( n \) 的数组:
1. 外层循环运行 \( n-1 \) 次。
2. 内层 `while` 循环在每次迭代中都要比较并移动元素,直到找到合适的位置插入当前元素。
3. 第 \( i \) 次外层循环中,内层 `while` 循环最多需要比较 `i` 次,因为当前元素需要插入到已排序部分的最前面。
**时间复杂度**:
- 外层循环:运行 \( n-1 \) 次。
- 内层循环:对于第 \( i \) 次外层循环,内层循环运行 \( i \) 次。
计算总的比较和移动操作数:
- \[
\sum_{i=1}^{n-1} i = 1 + 2 + 3 + \ldots + (n-1) = \frac{(n-1) \times n}{2}
\]
因此,最坏情况下的时间复杂度为 \( O(n^2) \)。
### 平均情况
在平均情况下,数组元素是随机排列的。对每个元素,平均来说需要移动大约一半的已排序部分元素。虽然求具体的平均复杂度有些复杂,但通常认为:
- 平均情况下时间复杂度是 \( O(n^2) \),因为内层循环的平均执行次数在逐渐增加。
### 空间复杂度
- 插入排序是就地排序算法,因此空间复杂度是 \( O(1) \),只需要常数级别的额外存储空间。
总结来说,插入排序的时间复杂度在最佳情况下为 \( O(n) \),而在最坏和平均情况下为 \( O(n^2) \)。