2021-10-21java:六千字快速复习七种常用排序
Posted 只yao为你发光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021-10-21java:六千字快速复习七种常用排序相关的知识,希望对你有一定的参考价值。
文章目录
一、插入排序
1.原理
将整个区间分为两个区间:1.有序区间 2.无序区间,每次将无序区间的第一个元素,在有序区间的合适位置插入.
2.代码实现
public static void insertSort(int[] array){
//有序区间[0,i)
//无序区间[i,array.length)
for(int i = 1; i < array.length;i++){
int v = array[i];//无序区间第一个数
int j = i-1;//有序区间最后一个下标
for(; j >= 0 && array[j] > v;j--){
array[j+1] = array[j];
}
//如果不交换此时下标在i-1
array[j+1] = v;
}
}
时间复杂度为O(N^2) ;空间复杂度为O(1)
稳定性 : 稳定排序; 如果array[j] > v换成array[j] >= v就变成不稳地的了.
特点: 1.数组越短,速度越快
2.数组越接近有序,速度越快
二、希尔排序
1.原理
进阶版本的插入排序,先分组,再针对每个组进行排序,逐渐缩小组的个数,最终整个数组就有序了.
2.代码实现
public static void shellSort(int[] arr){
int gap = arr.length / 2;//将数组分为gap组
while (gap > 1 ){
//传入组数和数组,带组数的插入排序
insertSortGap(arr,gap);
gap = gap / 2;
}
insertSortGap(arr,1);
}
private static void insertSortGap(int[] arr, int gap) {
//[0,i)为已排序区间;[i,arr.length)为未排序区间
for(int i = gap ;i < arr.length;i++){
int v = arr[i];
int cur = i - gap;
for( ; cur >= 0&& arr[cur]> v ;cur -= gap){
arr[cur+gap] = arr[cur];
}
arr[cur + gap] = v;
}
}
时间复杂度为O(N^1.3);空间复杂度为O(1)
时间复杂度最好为O(N),最坏为O(N^2);
特点:插入排序的优化版
稳定性 : 不稳定排序
三、选择排序
1.原理
基于打擂台的方式:选数组首元素为擂主,循环遍历数组剩余元素,如果发现比擂主小的元素,就交换两个数.
遍历完成后让擂主加一,重复前面的动作.
2.代码实现
public static void selectSort(int[] arr){
for(int bound = 0; bound < arr.length; bound++) {
//bound 位置元素作为擂主,循环进行比较
//如果打擂成功就和擂主交换
for(int cur = bound + 1 ; cur < arr.length; cur++) {
if(arr[bound] > arr[cur]){
int tmp = arr[bound];
arr[bound] = arr[cur];
arr[cur] = tmp ;
}
}
}
}
时间复杂度:O(N^2) ;空间复杂度:O(1);
稳定性 : 不稳定排序
四、堆排序
1.原理
升序排列:将数组建一个大堆,然后将堆顶元素和堆底元素交换,重新生成一个大堆,并且将堆的大小减一,每一次循环
都会让数组的最后一个元素是较大者,重复这个过程就实现了堆排序. 数组就是升序.
2.代码实现
public static void heapSort(int[] arr){
//第一步先将传入的数组创建成一个大堆
createHeap(arr);
//循环将堆顶元素与堆底元素交换,并且调整堆
//当堆中是剩一个元素的时候也就不用循环了,所以少循环一次 -1
for(int i = 0 ;i < arr.length -1 ;i ++){
//交换堆顶和堆底元素
//堆顶下标为0 ;堆底下标为arr.length - i -1
swap(arr , 0 ,arr.length - i -1 );
//交换完成再进行向下调整堆; 让堆的大小每次小 1
shiftDown(arr , arr.length - i -1 , 0 );
}
}
private static void shiftDown(int[] arr, int heapLength, int index) {
int parent = index;
int child = 2 * parent + 1;
while( child < heapLength){
//判断parent左右子树较大者
if( child + 1 < heapLength && arr[child + 1] > arr[child]){
child = child + 1;
}
//判断孩子是否大于,如果大于交换,否则满足大堆直接break
if( arr[child] > arr[parent]){
swap(arr, child ,parent);
}else{
break;
}
parent = child;
child = 2 * parent + 1;
}
}
private static void createHeap(int[] arr) {
int cur = (arr.length - 1 - 1) / 2 ;
for( int i = cur ; i >= 0 ;i--){
shiftDown(arr, arr.length, i);
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp ;
}
时间复杂度:O(N*log(N)) ;空间复杂度:O(1);
稳定性 : 不稳定排序
五、冒泡排序
1.原理
升序排列:将一个无序的区间相邻进行比较,较大的放在后面,循环比较,一次循环可以把最大的放在数组最后,下一次
循环可以将较大者放到次后位置,循环N次就得到了一个升序数组.
2.代码实现
public static void bubbleSort(int[] arr) {
//循环 i 次
for(int i = 0 ; i < arr.length ;i++) {
//每循环一次会确定一个较大值
//注意数组下标越界bound + 1 < arr.length - i
for(int bound = 0 ;bound + 1 < arr.length - i ;bound++){
if(arr[bound + 1] < arr[bound]){
int tmp = arr[bound];
arr[bound] = arr[bound + 1];
arr[bound + 1 ] = tmp;
}
}
}
}
时间复杂度:O(N^2) ;空间复杂度:O(1);
稳定性 : 稳定排序
六、快速排序
1.原理
升序排列:1.在待排数组中选取一个数作为基准值(一般选最左侧或最右侧作为基准值;
2.比基准值大的放在基准值的右边,比基准值小的放在基准值的左边
3.在递归排序基准值的左边,递归基准值的右边
2.代码实现
递归版本
public static void quickSort(int[] arr) {
//借助helper方法进行递归
//为了代码简单设置成前闭后闭区间[left,right]
quickSortHelper(arr ,0 ,arr.length - 1);
}
private static void quickSortHelper(int[] arr, int left, int right) {
//判断数组元素有几个,如果有0个或者1个就无需排序,直接return;
if(left >= right){
return ;
}
//通过partition对数组进行分区左侧为小于基准值,右侧为大于基准值
//index 为整理后left和right重合的位置
int index = partition(arr ,left ,right);
quickSortHelper(arr ,left , index - 1);
quickSortHelper(arr , index + 1,right);
}
private static int partition(int[] arr, int left, int right) {
int i = left ;
int j = right ;
int base = arr[right]; //将数组最后一个元素设为基准值
//left < right 说明数组还没判断完
while( i < j){
while ( i < j && arr[i] <= base){
i++ ;
//循环结束后,要么i和j重回,要么i下标是一个比base值大的数
}
while ( i < j && arr[j] >= base){
j-- ;
//循环结束要么j和i重合,要么j下标是一个比base小的数
}
//交换i和j下标的元素
swap(arr,i ,j);
}
//循环结束要将i或j(i和j此时在同一位置)位置的元素和right(基准值)元素进行交换
swap(arr, i , right);
//返回基准值位置
return i;
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp ;
}
时间复杂度:O(N*log(N)) ;空间复杂度:O(log(N))主要是因为递归;
稳定性 : 不稳定排序
非递归版本
public static void quickSortByLoop(int[] arr) {
//通过栈来模拟实现递归
Stack<Integer> stack = new Stack<>();
//将数组左右位置下标入栈
stack.push(arr.length - 1);
stack.push(0);
while (!stack.empty()){
//出栈顺序和入栈顺序相反
int left = stack.pop();
int right = stack.pop();
//如果左右区间之间只有一个数证明已经有序 直接进行下一次循环
if( left >= right){
continue ;
}
int index = partition(arr , left ,right);
//排序后的右区间[ index + 1 ,right]
stack.push(right);
stack.push(index + 1);
//排序后的左区间[left ,index - 1]
stack.push(index - 1);
stack.push(left);
}
}
private static int partition(int[] arr, int left, int right) {
int i = left ;
int j = right ;
int base = arr[right]; //将数组最后一个元素设为基准值
//left < right 说明数组还没判断完
while( i < j){
while ( i < j && arr[i] <= base){
i++ ;
//循环结束后,要么i和j重回,要么i下标是一个比base值大的数
}
while ( i < j && arr[j] >= base){
j-- ;
//循环结束要么j和i重合,要么j下标是一个比base小的数
}
//交换i和j下标的元素
swap(arr,i ,j);
}
//循环结束要将i或j(i和j此时在同一位置)位置的元素和right(基准值)元素进行交换
swap(arr, i , right);
//返回基准值位置
return i;
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp ;
}
时间复杂度:O(N*log(N)) ;空间复杂度:O(log(N))
稳定性 : 不稳定排序
七、归并排序
1.原理
升序排列:将数组分成若干个子数组,若子数组仅剩一个元素那么他就是有序的,然后将这些已有的子序列合并,得到
一个有序的数列.而将两个有序表合成一个有序表就是"二路归并".
2.代码实现
递归实现
public static void mergeSort(int[] arr){
//创建一个helper方法帮助完成递归
mergeSortHelper(arr ,0 ,arr.length);
}
private static void mergeSortHelper(int[] arr, int low, int high) {
if( high - low <= 1){
return;
}
//求出中间值
int mid = (low + high) / 2;
//这个递归执行完 [low,mid)就已经排序好了
mergeSortHelper(arr,low ,mid);
//这个递归执行完[mid,high)就已经排序好了
mergeSortHelper(arr,mid , high);
//对[low, high)进行排序
merge(arr ,low ,mid ,high);
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] output = new int[high - low];
int cur1 = low;
int cur2 = mid;
//记录output数组存放了多少元素
int outputIndex = 0 ;
while (cur1 < mid && cur2 < high){
//这里写成 > 才能保证稳定性
if(arr[cur1] > arr[cur2]){
output[outputIndex] = arr[cur2];
outputIndex++;
cur2 ++;
}else {
output[outputIndex] = arr[cur1];
outputIndex ++;
cur1 ++;
}
}
//上面循环结束不是cur1到mid就是cur2到high
//再写两个循环将剩余的元素写入output数组中
while (cur1 < mid){
output[outputIndex] = arr[cur1];
outputIndex ++;
cur1 ++;
}
while (cur2 < high){
output[outputIndex] = arr[cur2];
outputIndex ++;
cur2 ++;
}
//将output数组中的元素放回arr数组
for(int i = 0 ;i < high - low ;i ++){
arr[low + i ] = output[ i];
}
}
非递归实现
public static void mergeSortByLoop(int[] arr) {
//定义一个gap变量进行分组
for (int gap = 1 ; gap < arr.length;gap *= 2){
for(int i = 0 ;i < arr.length ;i = i + 2 * gap){
int low = i ;
int mid = i + gap;
int high = i + 2* gap;
if(mid > arr.length){
mid = arr.length;
}
if(high > arr.length){
high = arr.length;
}
merge(arr , low , mid ,high);
}
}
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] output = new int[high - low];
int cur1 = low;
int cur2 = mid;
//记录output数组存放了多少元素
int outputIndex = 0 ;
while (cur1 < mid && cur2 < high){
//这里写成 > 才能保证稳定性
if(arr[cur1] > arr[cur2]){
output[outputIndex] = arr[cur2];
outputIndex++;
cur2 ++;
}else {
output[outputIndex] = arr[cur1];
outputIndex ++;
cur1 ++;
}
}
//上面循环结束不是cur1到mid就是cur2到high
//再写两个循环将剩余的元素写入output数组中
while (cur1 < mid){
output[outputIndex] = arr[cur1];
outputIndex ++;
cur1 ++;
}
while (cur2 < high){
output[outputIndex] = arr[cur2];
outputIndex ++;
cur2 ++;
}
//将output数组中的元素放回arr数组
for(int i = 0 ;i < high - low 以上是关于2021-10-21java:六千字快速复习七种常用排序的主要内容,如果未能解决你的问题,请参考以下文章