✨前言✨
文章目录
🚄1 快速排序
🚅1.1 快排原理
1 从待排序区间选择一个数,作为基准值
2 遍历整个待排序区间,将比基准值小的(可以包含相等的)放到基准值的左边,将比基准值大的(可以包含相等的)放到基准值的右边;
3 采用分治思想对左右两个小区间,对左右两个小区间按照同样的方式来处理,直到小区间的长度==1,那么表面已经有序,如果长度为零,则说明没有数据!
🚈1.2 递归实现
通过了解快排的原理,那么就可以编写如下代码(我们一开始以数列中第一个数为基准值,寻找基准在数列中所在的位置):
public class TestDemo1 {
public static void main(String[] args) {
int[] array = {67,7,32,98,1,0,45,23,234,6746,123};
Quack(array);
System.out.println(Arrays.toString(array));
}
/**
* 快排递归写法
* 时间复杂度: 最好 O(N*logN) 最差(有序) O(N*N)
* 空间复杂度: O(logN)
* 稳定性:不稳定
* @param array 待排序数列
*/
public static void Quack(int[] array){
QuackSort(array,0,array.length-1);
}
public static void QuackSort(int[] array,int left,int right){
//递归的终止条件,当左等于大于右时,说明此时序列有序
if(left>=right){
return;
}
//找基准
int privot = getprivot(array,left,right);
//递归左边
QuackSort(array,left,privot-1);
//递归右边
QuackSort(array,privot+1,right);
}
public static int getprivot(int[] array,int start,int end){
int tmp = array[start];
while (start<end){
while (start<end&&array[end]>=tmp){
end--;
}
array[start]=array[end];
while (start<end&&array[start]<=tmp){
start++;
}
array[end]=array[start];
}
array[start]=tmp;
return start;
}
}
🚝1.3 对快排进行优化(三数取中法)
对于快排的优化,其实就是优化基准:
public class TestDemo1 {
public static void main(String[] args) {
int[] array = {67,7,32,98,1,0,45,23,234,6746,123};
Quack(array);
System.out.println(Arrays.toString(array));
}
/**
* 快排递归写法
* 时间复杂度: 最好 O(N*logN) 最差(有序) O(N*N)
* 空间复杂度: O(logN)
* 稳定性:不稳定
* @param array
*/
public static void Quack(int[] array){
QuackSort(array,0,array.length-1);
}
public static void Swap(int[] array,int i,int j){
int tmp = array[i];
array[i]=array[j];
array[j]=tmp;
}
public static int getMid(int[] array,int a,int b){
int mid = a+((b-a)>>>1);
if(array[a]>array[b]){
if(array[mid]>array[a]){
return a;
}else if(array[mid]<array[b]){
return b;
}else{
return mid;
}
}else{//array[a]<array[b]
if(array[mid]<array[a]){
return a;
}else if(array[mid]>array[b]){
return b;
}else{
return mid;
}
}
}
public static void QuackSort(int[] array,int left,int right){
//递归的终止条件,当左等于大于右时,说明此时序列有序
if(left>=right){
return;
}
//获取三个值中间值的小标
int mid = getMid(array,left,right);
//在把中间值放到一开始的位置
Swap(array,left,mid);
//找基准
int privot = getprivot(array,left,right);
//递归左边
QuackSort(array,left,privot-1);
//递归右边
QuackSort(array,privot+1,right);
}
public static int getprivot(int[] array,int start,int end){
int tmp = array[start];
while (start<end){
while (start<end&&array[end]>=tmp){
end--;
}
array[start]=array[end];
while (start<end&&array[start]<=tmp){
start++;
}
array[end]=array[start];
}
array[start]=tmp;
return start;
}
}
优化总结:
1 快排的优化实质上就是在对基准进行优化,所以这里采用了三数取中法进行优化
2 可以结合我们之前所学的直接插入排序进行优化,对于插入排序,之前已经了解到数列越有序越快,那么就可以当调整的长度小于某个数的时候,可以直接利用直接插入排序
🚃1.4 非递归实现
对于非递归的实现,我们此时还是一样根据原理需要找到基准值,然后再利用数据结构中栈去解决(核心的思想也是利用找基准去解决):
代码如下:
//非递归实现
public static void nonQuackSort(int[] array){
int start = 0;
int end = array.length-1;
Stack<Integer> stack = new Stack<>();
//找基准
int privot = getprivot(array,start,end);
//确保右边至少有两个可排序的数,否则就是只有一个数,就是有序的
if(privot-1>start){
stack.push(start);
stack.push(privot-1);
}
//确保左边至少有两个可排序的数,否则就是只有一个数,就是有序的
if(privot+1<end){
stack.push(privot+1);
stack.push(end);
}
while (!stack.isEmpty()){
end=stack.pop();
start=stack.pop();
privot = getprivot(array,start,end);
if(privot-1>start){
stack.push(start);
stack.push(privot-1);
}
if(privot+1<end){
stack.push(privot+1);
stack.push(end);
}
}
}
🚋二 归并排序
🚌2.1 归并的核心思想
给定两个有序数组,那么如何将他们合并成为一个有序的数组呢?其实在合并的过程中就体现出归并排序的初步思想原理,就是依据这样的一个思想。
代码如下:
public static int[] conbine(int[] array1,int[] array2){
//创建一个新的数组来存储合并之后的数据
int[] array = new int[array1.length+array2.length];
//记录合并数组的下标
int i = 0;
//第一个数组的第一个下标
int s1 = 0;
//第一个数组的最后一个下标
int e1 = array1.length-1;
//第二个数组的第一个下标
int s2 = 0;
//第二个数组的最后一个下标
int e2 = array2.length-1;
//将两个数组中的数据按照从小到大放入新的数组中
while (s1<=e1&&s2<=e2){
if(array1[s1]<array2[s2]){
array[i++]=array1[s1++];
}else{
array[i++]=array2[s2++];
}
}
//可能存在第一个数组没放完或者第二个数组的数据没放完
while (s1<=e1){
array[i++]=array1[s1++];
}
while (s2<=e2){
array[i++]=array2[s2++];
}
return array;
}
🚎2.2 归并排序的递归实现
public static void mergeSort(int[] array){
merge(array,0,array.length-1);
}
public static void merge(int[] array,int low,int high){
int mid = low+((high-low)>>>1);
//递归的终止条件
if(low>=high){
return;
}
//左边递归进行分解
merge(array,low,mid);
//右边递归进行分解
merge(array,mid+1,high);
//开始归并
Conbine(array,low,mid,high);
}
//归并的核心思想
public static void Conbine(int[] array,int low,int mid,int high){
int[] tmp = new int[high-low+1];
int i = 0;
int s1 = low;
int e1 = mid;
int s2 = mid+1;
int e2 = high;
while (s1<=e1&&s2<=e2){
while (s1<=e1&&s2<=e2){
if(array[s1]<array[s2]){
tmp[i++]=array[s1++];
}else{
tmp[i++]=array[s2++];
}
}
//可能存在第一个数组没放完或者第二个数组的数据没放完
while (s1<=e1){
tmp[i++]=array[s1++];
}
while (s2<=e2){
tmp[i++]=array[s2++];
}
}
for (int j = 0; j < i; j++) {
//注意数组下标,当遇到右边数组合并时,它的第一个下标已经不是零了
array[j+low]=tmp[j];
}
}
🚍2.3 归并排序的非递归实现
非递归排序,所采用的思想就是先分组,先是一个一个一组,在变成两个一组,最后整个数组为一组时,此时就是一个有序的数组。
public static void nonmergeSort(int[] array){
//组数(一开始一个一组)
int gap = 1;
//只有组数大于等于数组的长度,那么此时说明数组已经有序
while (gap<array.length){
//每重新分组,遍历一遍数组,进行归并排序
for (int i = 0; i < array.length-1; i+=2*gap) {
int left = i;
int mid = left+gap-1;
//注意这时mid可能会越界
if (mid>=array.length){
mid=array.length-1;
}
int right = mid+gap;
if(right>=array.length){
right=array.length-1;
}
//开始归并
Conbine(array,left,mid,right);
}
//循环结束,组数乘2
gap=gap*2;
}
}
public static void Conbine(int[] array,int low,int mid,int high){
int[] tmp = new int[high-low+1];
int i = 0;
int s1 = low;
int e1 = mid;
int s2 = mid+1;
int e2 = high;
while (s1<=e1&&s2<=e2){
while (s1<=e1&&s2<=e2){
if(array[s1]<array[s2]){
tmp[i++]=array[s1++];
}else{
tmp[i++]=array[s2++];
}
}
//可能存在第一个数组没放完或者第二个数组的数据没放完
while (s1<=e1){
tmp[i++]=array[s1++];
}
while (s2<=e2){
tmp[i++]=array[s2++];
}
}
for (int j = 0; j < i; j++) {
//注意数组下标,当遇到右边数组合并时,它的第一个下标已经不是零了
array[j+low]=tmp[j];
}
}
🚗三 其他排序(非基于比较)
🚖3.1 计数排序
计数排序详解
🚘3.2 基数排序
基数排序详解
🚔3.3 桶排序
桶排序详解
🚉4 总结
可以通过一个表格列举出这些排序的特点,根据排序的需求,选择合适的排序方式!