https://blog.csdn.net/weixin_30337157/article/details/95953304
https://www.bilibili.com/video/BV1Cz411B7qd?p=17
排序算法的分类
按照不同的排序原则,可以分为以下几类
1)插入排序:直接插入排序、希尔排序
2)交换排序:冒泡排序、快速排序
3)选择排序:简单(直接)选择排序、树形选择排序、堆排序
4)归并排序
分类标准并不是绝对的,简单排序顾名思义就是比较简单的排序算法,是必须得掌握的
插入排序
-
直接插入排序
就像打扑克牌的时候理手牌的方法
将所有元素分为已排序的和未排序的两种,然后找到未排序中的第一个元素作为待排序元素
倒叙遍历已排序元素,以此与待排序元素比较
直到找到一个元素小于待排序元素,就把待排序元素放到该位置,其他元素向后移动一位
1)Java实现public class StraightInsertionSort { public static void main(String[] args){ // 生成一个数组 int[] numArray = new int[]{4,3,2,10,12,1,5,6}; // 打印数组 for(int k = 0; k<numArray.length;k++){ System.out.println(numArray[k]); } // 开始排序 StraightInsertionSort SIS = new StraightInsertionSort(); SIS.Sort(numArray); } public void Sort(int[] a){ // 排序方法主体 int tmp = 0; // 临时遍历,用于存待排序元素 for(int i=1;i<a.length;i++){ tmp = a[i]; for(int j=i-1;j>=0;j--){ // 倒叙比较 if(a[j]>tmp){ // 如果前面的数大于待排序数 a[j+1] = a[j]; a[j] = tmp; } else{ break; // 这个break是很有必要的 } } } // 输出排序后的序列 System.out.println("///"); for(int k = 0; k<a.length;k++){ System.out.println(a[k]); } } }
2)Python实现
#!/usr/bin/python def StraightInsertionSort(a): for i in range(1,len(a)): # 位置i上的就是待排序元素 tmp=a[i] for j in range(i-1,-1,-1): if a[j]>tmp: a[j+1]=a[j] a[j]=tmp else: break print(a) StraightInsertionSort([4,3,2,10,12,1,5,6])
3)算法分析
考虑最坏情况,每一个待插入元素都要和前面的所有元素比较并交换位置
所以比较的次数 = 交换的次数 = (N-1)+(N-2)+(N-3)+…+2+1 = N2/2-N/2
总执行次数 = (N2/2-N/2)*2 = N2-N
所以时间复杂度为O(N2) -
希尔排序
先按照增长量分组,然后对每一组进行简单插入排序
如下图,h为增长量初始值为5,则9和4为一组,1和8为一组,以此类推进行简单插入排序
然后减少增长量,直到增长量=1,就成为了简单插入排序了
h的初始值和减少量一般设定为数组长度/2
1)Java实现public class ShellSort { public static void main(String[] args) { // 生成一个数组 int[] numArray = new int[]{9, 1, 2, 5, 7, 4, 8, 6, 3}; // 打印数组 for (int k = 0; k < numArray.length; k++) { System.out.println(numArray[k]); } // 开始排序 ShellSort SS = new ShellSort(); SS.Sort(numArray); } public void Sort(int[] a) { for (int step = a.length / 2; step >= 1; step /= 2) { for (int i = 0; i < a.length; i++) { for (int j = i; j >=step ; j -= step) { if (a[j] < a[j-step]) { int tmp = a[j-step]; a[j-step] = a[j]; a[j] = tmp; } else break; } } } // 输出排序后的序列 System.out.println("///"); for (int k = 0; k < a.length; k++) { System.out.println(a[k]); } } }
2)Python实现
def ShellSort(a): # 初始步长 step = len(a)//2 while step > 0: # 按步长进行插入排序 for i in range(step, len(a)): for j in range(i-step,-1,0-step): if a[j]>a[i]: tmp = a[j] a[j]=a[i] a[i]=tmp else: break step = step // 2 print(a) ShellSort([9, 1, 2, 5, 7, 4, 8, 6, 3])
3)算法分析
由于每次排序的增长量都不同,所以暂时只能使用事后分析统计方法来统计,这里就不赘述
交换排序
-
冒泡排序
个人倾向于把冒泡排序称为“最大元素优先排序”
意思就是每次大循环,都是将当时状态下最大的元素排好序
1)Java实现
import java.util.Random; public class BubbleSort { public static void main(String[] args){ // 生成一个数组 int[] numArray = new int[]{4,5,6,3,2,1}; // 打印数组 for(int k = 0; k<numArray.length;k++){ System.out.println(numArray[k]); } // 开始排序 BubbleSort BS = new BubbleSort(); BS.Sort(numArray); } public void Sort(int[] a){ for(int i=0;i<a.length;i++){ for(int j=0;j<a.length-i-1;j++){ if(a[j]>a[j+1]){ int tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp; } } } System.out.println("///"); for(int k = 0; k<a.length;k++){ System.out.println(a[k]); } } }
2)Python实现
def BubbleSort(a): for i in range(len(a)): for j in range(len(a)-i-1): if a[j]>a[j+1]: tmp = a[j] a[j] = a[j+1] a[j+1] = tmp print(a) BubbleSort([4,5,6,3,2,1])
3)算法分析
冒泡排序使用了两层循环
考虑最坏情况,每一个待插入元素都要和前面的所有元素比较并交换位置
所以比较的次数 = 交换的次数 = (N-1)+(N-2)+(N-3)+…+2+1 = N2/2-N/2
总执行次数 = (N2/2-N/2)*2 = N2-N
所以时间复杂度为O(N2) -
快速排序
个人理解为“两头往中间找算法”
1)Java实现public class QuickSort { public static void main(String[] args){ // 生成一个数组 int[] numArray = new int[]{6,1,2,7,9,3,4,5,8}; // 开始排序 Sort(numArray, 0, numArray.length-1); // 打印数组 System.out.println("/"); for(int k = 0; k<numArray.length;k++){ System.out.println(numArray[k]); } } public static void Sort(int[] a,int low,int high){ if(low>=high){//跳出递归的条件 return; } int flag = a[low]; int right = high; int left = low; while (left<right){ while(flag<=a[right] && left<right){ right--; } while(flag>=a[left] && left<right){ left++; } if(left<right){ int tmp = a[right]; a[right] = a[left]; a[left] = tmp; } } a[low] = a[left]; a[left] = flag; Sort(a,low,left-1); Sort(a,left+1,high); } }
2)Python实现
def QuickSort(a, low, high): if low >= high: return left = low right = high flag = a[low] while left < right: while flag <= a[right] and left < right: right -= 1 while flag >= a[left] and left < right: left += 1 if left < right: tmp = a[left] a[left] = a[right] a[right] = tmp a[low] = a[left] a[left] = flag Sort.QuickSort(a, low, left - 1) Sort.QuickSort(a, left + 1, high)
3)算法分析
最好情况下,待排序序列本身就是有序的
此时递归的次数t = log2n,所以时间复杂度T(n)=n*log2n
最坏情况下,待排序序列是倒序的
此时T(n)=n2
选择排序:简单(直接)选择排序
根据要排序的位置,来找到相对应大小的元素
以下图为例
首先排位置1的元素,就在后面的元素中找到最小的元素,与第一个元素交换位置
然后找排位置2的元素,就在后面的元素中找第二大的元素,与第二个元素交换位置
1)Java实现
public class SwapSort {
public static void main(String[] args){
// 生成一个数组
int[] numArray = {6,1,2,7,9,3,4,5,8};
// 开始排序
Sort(numArray);
// 打印数组
System.out.println("/");
for(int k = 0; k<numArray.length;k++){
System.out.println(numArray[k]);
}
}
public static void Sort(int[] a){
for(int i = 0;i<a.length-1;i++){
int min = i;
for(int j=i+1;j<a.length-1;j++){
if(a[j]<a[min]){
min = j;
}
}
int tmp = a[i];
a[i] = a[min];
a[min] = tmp;
}
}
}
2)Python实现
def SwapSort(a):
for i in range(len(a)):
min = i
for j in range(min+1,len(a)):
if a[min]>a[j]:
tmp = a[min]
a[min]=a[j]
a[j]=tmp
3)算法分析
算法中有两层循环,T(n)=n+n*(n-min-1) = O(n2)
归并排序
归并排序的想法其实和快速排序差不多,都是分组
区别就是分组依据不同
快速排序是依靠一个元素的值来分组,比该元素大的一组,比该元素小的一组,所以在分组之前要进行交换的操作
归并排序是考数组的位置来分组,比如下图就直接从中间一刀劈开,分成的两个组其实并没有大小关系
自己称之为“中间劈开后用三个指针一个临时数列归并排序法”
合并时的操作如下图
1)Java实现
public class MergeSort {
public static void main(String[] args){
// 生成一个数组
int[] numArray = {6,1,2,7,9,3,4,5,8};
// 临时数组
int[] tmpArray = new int[numArray.length];
// 开始排序,分为两个部分,一个是分开的部分,一个是归并的部分
Sort(numArray,0,numArray.length-1,tmpArray);
// 打印数组
System.out.println("/");
for(int k = 0; k<numArray.length;k++){
System.out.println(numArray[k]);
}
}
public static void Sort(int[] a,int low,int high,int[] tmp){
// 这个函数递归起到的作用其实就只是切分数组而已
// 真正排序的函数时merge()
if(low<high){
int mid = (high+low)/2;
Sort(a,low,mid,tmp);
Sort(a,mid+1,high,tmp);
merge(a,low,mid,high,tmp);
}
}
public static void merge(int[] a,int low,int mid,int high,int[] tmp){
// 排序且合并两个数组
// 定义三个指针
int i = low;
int p1 = low;
int p2 = mid+1;
while (p1<=mid && p2<=high){
if(a[p1]<a[p2]){
tmp[i++] = a[p1++];
}else {
tmp[i++] = a[p2++];
}
}
while (p1<=mid){
// 当p1还没有遍历完时,把剩下的元素直接放入tmp中
tmp[i++] = a[p1++];
}
while(p2<=high){
tmp[i++] = a[p2++];
}
for(int index = low;index<=high;index++){
a[index]= tmp[index];
}
}
}
2)Python实现
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def Sort(a, low, high, tmp):
if low < high:
mid = (low+high) // 2
Sort(a, low, mid, tmp)
Sort(a, mid + 1, high, tmp)
Merge(a, low, mid, high, tmp)
def Merge(a, low, mid, high, tmp):
p = low
pp = mid + 1
ppp = low # 指向临时数组的指针
while p <= mid and pp <= high:
if a[p] < a[pp]:
tmp[ppp] = a[p]
ppp = ppp + 1
p = p + 1
else:
tmp[ppp] = a[pp]
ppp = ppp + 1
pp = pp + 1
# 当p1没有遍历完时
while p <= mid:
tmp[ppp] = a[p]
ppp = ppp + 1
p = p + 1
while pp <= high:
tmp[ppp] = a[pp]
ppp = ppp + 1
pp = pp + 1
# 将临时数组复制到原数组中
for i in range(low, high+1):
a[i] = tmp[i]
a = [6, 1, 2, 7, 9, 3, 4, 5, 8]
tmp = [0]*len(a)
Sort(a, 0, len(a) - 1, tmp)
print(a)
# input = input("Press <enter>")
3)算法分析
T(n)=n*(log2n)
排序的稳定性
- 稳定性的定义
数组中有若干元素,其中A元素和B元素相等,并且A元素在B元素前面
如果使用某种排序算法之后,能保证A元素依然在B元素前面,则称该算法是稳定的 - 稳定性的意义
当一组数据需要多次排序的时候,稳定性很有必要
比如要排序的内容是一组商品对象,第一次按照价格排序,第二次按照销量排序
如果使用的排序算法是具有稳定性的,就可以使得相同销量的对象依旧保持着价格高低的顺序展现
只有销量不同的才需要重新排序,减少系统开销 - 常见算法的稳定性
直接插入排序:最稳定的
希尔排序:最不稳定的
冒泡排序:稳定的
快速排序:不稳定
直接选择排序:不稳定
归并排序:稳定