update readme

This commit is contained in:
ranwenjie
2021-11-06 22:03:15 +08:00
parent 4b5e2d8b80
commit ad152b7814
10 changed files with 147 additions and 81 deletions
+85 -55
View File
@@ -3,21 +3,32 @@
**排序的稳定性** 是指对于相等的元素,排序之后,任然保存2个元素的位置没有变,就是稳定的排序,反之就是不稳定排序。
**交换排序算法**
* 交换排序算法
* 桶排序
### 交换排序算法
* 冒泡排序 :没轮确定一个最大的数排后面
* 插入排序
* 选择排序 :每轮选择最小的数排前面
* 希尔排序
* 快排 : 选最后一个作为pivot(基数), 将数据分为 左边小于 pivot, 右边大于 pivot
* 归并排序 :
* 堆排序
* 快排 : 选最后一个作为pivot(基数), 将数据分为 左边小于 pivot, 右边大于 pivot; 分治递归, 二叉树的前序遍历思路
* 归并排序 : 将无序的数组,分成2个子数组分别排序,然后再merge ; 分治递归, 二叉树的后续遍历思路
* 插入排序 :
* 希尔排序 :
* 堆排序 :
**线性排序算法**
### 线性排序算法
* 桶排序
### 小结
常见的排序算法都是比较排序,比较排序的时间复杂度通常为 O(n^2) 或 O(nlogn)
但是如果带排序的数字有一些特俗性时,我们可以根据这来设计更加优化的排序算法。
# 交换排序算法
排序算法的复杂度由 `比较的次数` 和 `交换的次数` 一起决定。
@@ -31,6 +42,8 @@
在a【i,n】中最小的元素和 a[i]交换位置。空间复杂度O(1),时间复杂度 O(n^2)
![](./selectsort.gif)
### 冒泡排序
@@ -40,50 +53,41 @@
这样,即使是排好序的拿冒泡排序排序,比较的时间复杂度O(n^2)
### 插入排序
1. 第一个元素算作已经排好
2. 取下一个元素,从已经排好的序列元素中,从后向前扫描
3. 如果排好序的元素大于 新元素,排好序的元素移到下一个位置
4. 重复3,直到直到最后的插入位置
5. 重复2
类似插入扑克牌的效果
最坏的情况: 待排序的是一个逆序排放的数组,这样导致每一轮都要移动元素;此时复杂度是是0(n^2)
最好的情况: 待排序的是一已经顺序排放的数字,此时只需要做一轮比较就够了 0(n)。因此可以看到,**对大部分数据已经有序这样的数组排序,使用`插入排序`非常有优势**
空间复杂度O(1)
### 希尔排序
递减增量排序算法,对`插入排序`的改进,实质是分组插入排序,又叫`缩小增量排序`
1. 先将待排数列分割成若干子序列(增量为m)
2. 对每个子序列使用`插入排序`
3. 减小增量,再排序
4. 对全体元素做一次`插入排序`
`希尔排序`提升排序的奥秘就在于`数据元素越有序,使用插入排序效率越高`
![](./bubblesort.gif)
### 快速排序
时间复杂度 平均复杂度 O(n * logn) , 最坏 O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
递归一次,pivot 左边都比它小,右边都比它大。这是递归,分治的思想。
> 快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历
> 快速排序就是个二叉树的前序遍历思路,归并排序就是个二叉树的后序遍历思路
代码框架如下
```
void quicksort(int *a, int left, int right){
if (left<right){//加上这个,不然有死循环,造成堆栈溢出,这也是递归结束条件
int i = partion(a,left,right);//使得局部有序,i作为分隔
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
}
```
对 A[p...r] :
0. 选择最后一个元素作为 pivot (基准)
1. 分解:A[p..q-1] A[q+1..r],使得 A[p...q-1]< A[q] <A[q+1..r]
2. 解决:递归调用 快排,,对子数组A[p..q-1],A[q+1..r]排序
2. 解决:递归调用 快排,对子数组A[p..q-1],A[q+1..r]排序
3. 合并(子问题相互独立的,因此用分治算法就可以了)
具体步骤:
@@ -92,7 +96,8 @@
2. 重排数列,比 pivot 小得排左边,比pivot大的排右边,相等的随便。 一句话就是`挖坑填数`
3. 递归的,使用相同的方式,重排左右两边的子序列
扫描过程分2种:
扫描过程分2种:
1. 挖坑排序,2头向中间扫描,先从后向前找,再从前向后找。
2. 单向扫描
@@ -139,12 +144,7 @@
```
平均复杂度 O(n * logn)
最坏 O(n^2)
空间复杂度
快速排序是对冒泡排序的改进,划分交换排序。
![](./qsort.gif)
### 归并排序merge
@@ -176,6 +176,40 @@ void sort(int[] nums, int lo, int hi) {
}
```
![](./mergesort.gif)
### 插入排序
1. 第一个元素算作已经排好
2. 取下一个元素,从已经排好的序列元素中,从后向前扫描
3. 如果排好序的元素大于 新元素,排好序的元素移到下一个位置
4. 重复3,直到直到最后的插入位置
5. 重复2
类似插入扑克牌的效果
最坏的情况: 待排序的是一个逆序排放的数组,这样导致每一轮都要移动元素;此时复杂度是是0(n^2)
最好的情况: 待排序的是一已经顺序排放的数字,此时只需要做一轮比较就够了 0(n)。因此可以看到,**对大部分数据已经有序这样的数组排序,使用`插入排序`非常有优势**
空间复杂度O(1)
### 希尔排序
递减增量排序算法,对`插入排序`的改进,实质是分组插入排序,又叫`缩小增量排序`
1. 先将待排数列分割成若干子序列(增量为m)
2. 对每个子序列使用`插入排序`
3. 减小增量,再排序
4. 对全体元素做一次`插入排序`
`希尔排序`提升排序的奥秘就在于`数据元素越有序,使用插入排序效率越高`
![](./shellsort.gif)
@@ -235,6 +269,10 @@ void sort(int[] nums, int lo, int hi) {
推排序还可以用来求 top-K 大(小)的问题。
#排序动画演示
接下来,我们看一组排序的动画,你看看能不能猜到他们使用了什么排序算法完成。
![](./qsort.gif)
@@ -255,7 +293,7 @@ void sort(int[] nums, int lo, int hi) {
# 线性排序算法
上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN.而非基于比较的排序,可以突破NlogN的时间下限。当然,非比较的排序,也是需要有一些限定条件的。
上面的算法都是基于比较的排序,时间复杂度最好也是 NlogN. 而非基于比较的排序,可以突破NlogN的时间下限。当然,非比较的排序,也是需要有一些限定条件的。
### 桶排序 bucket sort
@@ -266,14 +304,6 @@ void sort(int[] nums, int lo, int hi) {
# 小结
常见的排序算法都是比较排序,比较排序的时间复杂度通常为 O(n^2) 或 O(nlogn)
但是如果带排序的数字有一些特俗性时,我们可以根据这来设计更加优化的排序算法。