排序的概念(Sort)
排序的分类
排序可基于数组进行排列,也可基于比较排列(依据数据的大小进行排序),也有原地排序,排序是运用非常广泛的基础算法,也是典型的多态算法。
排序共有七大排序方法,分别为:
- 插入排序
- 希尔排序(插入排序优化版)
基于选择排序的有:
- 选择排序
- 堆排序
此外还有:
- 冒泡排序
- 快速排序
- 归并排序
今天会涉及到插入,冒泡,选择,堆排序
现在开始吧!!!
插入排序
代码实现
基本思想:
将要排序的部分分为【有序区间,无序区间】
每次执行的操作是:
- 1.抓无序区间的第一张牌
- 2.依次和有序区间的牌比较
- 3.选择合适的位置插入
package sort;
public class Sort{
public static void inserSort(long[] array){
//数组一共有array.length个
//故,子操作需执行array.length或array.length-1次
for(int i = 0;i < array.length-1;i++){
//有序区间【0,i+1)
//因i已经等于0,所以i已有序,故为i+1
//无序区间【i+1,array.length】
//所以抓取出来的数是i+1
long key = array[i+1];
int j;
//按倒序的顺序依次在有序区间进行比较[i,0]
for(int j = 1,j >= 0,j--){//j就是要与key进行比较,有三种情况:
//1.key<array[j] 把array[j]往后移,继续向前比较,key=array[j]
//2.key==array[j] 不移把key放入array[j]后,array[j+1]=key,不需要继续比较
//3.key>array[j] 把key放入array[j]后,array[j+1]=key,不需要继续比较
if(key < array[j]){
array[j+1] = array[j];
}else{
break;
}
array[j+1] = key;
}
}
}
测试代码
测试的四种情况:
- 1.随机生成的乱序数
- 2.已经有序
- 3.已经无序
- 4.完全相等
- 扩展:测试不同规模的数据(10/1000/100w/10亿等)
//测试
//构建随机生成的乱序数
public static long[] BuildRandomArray(){
Random random = new Random(20201018);
long[] array = new long[10];
for(int i = 0;i < 20; i++){
array[i] = random.nextInt(100);
}
return array;
}
}
//构建完全有序数组
public static long[] BuildOrderedArray(){
long[] array = BuildRandomArray();
Array.sort(array);
return array;
}
//构建完全逆序数组
public static long[] BuildReverseArray(){
long[] array = BuildOrderedArray();
swap(array,0,9);
swap(array,1,8);
swap(array,2,7);
swap(array,3,6);
swap(array,4,5);
return array;
}
//构建完全相等的数组
public static long[] BuildEqualArray(){
long[] array = new long[10];
for(int i = 0;i < 10;i++){
array[i] = 9;
}
return array;
}
//swap
public static void swap(long[] array,int i,int j){
long t = array[i];
array[i] = array[j];
array[j] = t;
}
//主调用函数
public static void main(string[] args){
long[] a1 = BuildRandomArray();
System.out.println(Arrays.toString(a1));
inserSort(a1);
System.out.println(Arrays.toString(a1));
}
时间复杂度
最好情况:O(n)有序排列数组
最坏情况:O(n^2)逆序排列数组
平均情况:O(n^2)
空间复杂度
最好/最坏/平均情况:O(1)
具有稳定性
冒泡排序
将区间中的1个大数依次排序到相应位置的过程称为冒泡过程。10个数需要9或10次冒泡过程。
基本思想与插入排序相同,都是将要排序的区间分为【无序区间,有序区间】
public static void bubbleSort(long[] array){
//需要array.length-1次冒泡过程
for(int i = 0;i < array.length-1;i++){
//无序【0,array.length-i)
//有序【array.length-i,array.length)
//当循环1次时,数组中有1个有序数,所以循环i次,有序数字有i个
//每次冒泡前,假设数组已经有序
boolean inSorted = true;
//进行冒泡排序
for(int j = 0;j < array.length-i-1;j++){
if(array[j] > array[j+1]){
swap(array,j,j++);
inSorted = false;
}
}
if(inSorted){
break;
}
}
}
}
时间复杂度:
- 最好情况:O(n) 有序数组(只走了一次内侧循环)
- 最坏情况:O(n^2) 逆序
- 平均情况:O(n^2)
空间复杂度:
- 最好/最坏/平均:O(1)
具有稳定性
选择排序
基本思想:
通过遍历完全乱序区间选出其最大或最小的数,将其放入有序区间,依次循环。
如何在无序区间进行选择:
- 1.遍历,找出最大数
- 2.和无序区间的最后一个数进行比较
public static void selectSort(long[] array){
for(int i =0; i < array.length-1;i++){
//有序区间:【array.length-i,array.length)
//无序区间:【0,array.length-i)
int maxIndex = 0;//假设最大数是array[0]
for(int j = 1;j < array.length-i;j++){
if(array[j] > array[maxIndex]){
maxIndex = j;
}
}
//期望maxIndex指向无序区间的最大的数的下标
swap(array,maxIndex,array.length-i-1);
}
时间复杂度:
最好/最坏/平均情况:O(n^2)
空间复杂度:
最好/最坏/平均情况:O(1)
不具备稳定性
堆排序
举个例子,已知一个数组【19,45,15,59,28,25,28,47,99,30】,建堆后为【99,59,28,47,30,25,15,19,45,28】如下图所示:
对其中数字找最大数交换得【28,59,28,47,30,25,15,19,45,99】从堆中挑出最大的数放入有序区间,对下标0的数根据其大小进行向下调整,但不包括有序区间
可得:
接着依次循环完成排序。
代码实现
public static void heapSort(long[] array){
//1.建大堆
createHeap(array,array.length);
//2.进行选择的过程,一共需要array.length-1组
for(int i = 0;i <array.length-1;i++){
//无序:【0,array.length-i】
swap(array,0,array.length-i-1);
//无序:【0,array.length-i-1】
adjustDown(array,array.length-i-1,0);
}
}
private static void adjustDown(long[] array,int size,int index){
while(2*index + 1 < size){
int maxindex = 2*index + 1;
if(maxIndex + 1 < size && array[maxIndex + 1] > array[maxIndex]{
maxIndex++;
}
if(array[index] >= array[maxIndex]){
break;
}
swap(array,index,maxIndex);
index = maxIndex;
}
}
private static void createHeap(long[] array,int size){
for(int i = (size-2) / 2;i >= 0;i--){
adjustDown(array,size,i);
}
}
时间复杂度
最好/最坏/平均情况:O(n*log(n))
空间复杂度
最好/最坏/平均:O(1)
不具备稳定性