目录
💕💕插入排序💕💕每次插入一个数据💕💕
1. 直接插入排序😘😘直接插😘😘
2. 希尔排序😘😘利用堆插😘😘
💕💕选择排序💕💕每次选个最小值放前面💕💕
1. 直接选择排序👻👻直接找最小值👻👻
2. 堆排序👻👻利用堆找👻👻
💕💕交换排序💕💕每次交换💕💕
1. 冒泡排序👻👻超级简单👻👻
2. 快速排序👻👻面试多考👻👻
2.1 Hoare版👍👍Hoare这个人研究的👍👍
2.2 挖坑法👍👍挖个坑每次填坑👍👍
2.3 前后快慢指针法👍👍用前后俩指针👍👍
3. 快速排序升级版👻👻记住哦👻👻
3.3 非递归实现快排👻👻嘎嘎6👻👻
💕💕归并排序💕💕大文件分开排序💕💕
💕💕其他排序💕💕很少用💕💕
🐖🐖🐖🐖如果喜欢!!🐂🐂🐂🐂
🐖🐖🐖🐖欢迎关注!!🐂🐂🐂🐂
🐖🐖🐖🐖持续回访! !🐂🐂🐂🐂
CSDN主页:所有博客内容,Java大树逐渐成长
Gitee地址:想看博客代码??点击这里
QQ : 1939639916(有问题可以加好友,备注csdn)
-
插入排序
1. 直接插入排序
- 时间复杂度:O(N^2)(有序的时候最快可以达到O(n) )
- 空间复杂度:O(1)
- 稳定性:稳定
- 适用场景:数据基本有序的情况
- 代码:
public void insertSort(int[] arr) { if (arr.length <= 1) return; for (int i = 1; i < arr.length; i++) { int temp = arr[i];//暂时存放arr[i]的值,也就是要插入的元素 int j; for (int j = i-1;j>=0;j--){ if (arr[j]>temp){ arr[j+1]=arr[j];} else { break; } } arr[j] = temp; } }
2. 希尔排序
希尔排序其实是直接插入排序的升级版本
- 时间复杂度:大约在O(n ^ 1.25)到O(1.6 * n ^ 1.25)
- 空间复杂度:O(1)
- 稳定性:不稳定
- 适用场景:相对于直接插入排序,希尔排序更适合无序的数据,特别是数据量较大的时候,能较多节省运行时间
- 代码
public void shellSort(int[] array) { for (int gap = array.length / 2; gap >= 1; gap/=2) { shellInsertSort(array, gap); } } private void shellInsertSort(int[] array, int gap) { for (int i = gap;i<array.length;i++){ int temp = array[i]; int j = i-gap; for (;j>=0;j-=gap){ if (temp<array[j]){ array[j+gap] = array[j]; } else { break; } } array[j+gap] = temp; } }
-
选择排序
选择排序基本思想:
每次从待定元素中,选出最小的那个,然后放在序列的起始位置,继续排序后面的。
直接选择是每个找一遍选择,堆排序是利用大根堆,找最大的放到后面
1. 直接选择排序
- 时间复杂度 O(N^2)(对数据是否有序不敏感)
- 空间复杂度 O(1)
- 稳定性 不稳定
- 过程 每次选出一个最小值放到数据的第一个位置
- 代码
public void selectSort(int[] array) { for (int i = 0; i < array.length; i++) { int minIndex = i; for (int j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { minIndex = j;} } swap(array, minIndex, i); } } //升级优化版本:左右两边同时进行 public void selectSortPro(int[] array) { int left, right; for (left = 0, right = array.length - 1; left < right; left++, right--) { int minIndex = left; int maxIndex = right; for (int j = left ; j <= right; j++) { if (array[j] < array[minIndex]) { minIndex = j;} else if (array[j] > array[maxIndex]) { maxIndex = j;} } swap(array, left, minIndex); //如果left位置存放的是最大值,则下一步maxIndex的内容被掉包了,要有个if判断一下 if (left == maxIndex) { maxIndex = minIndex; } swap(array, right, maxIndex); } }
2. 堆排序
是直接选择排序的优化,相较于直接选择排序,通过堆的方式 选择 最小值放在前面
- 时间复杂度:O(N*logN)
- 空间复杂度: O ( 1)
- 稳定性: 不稳定
- 代码:
public void heapSort(int[] arr) { createdHeap(arr); for (int i = arr.length - 1; i > 0; i--) { swap(arr, i, 0); shiftDown(arr, 0, i); } } //建立大根树 private void createdHeap(int[] arr) { int len = arr.length; for (int i = len - 1; i >= 0; i--) { shiftDown(arr, i, len); } } 向下寻找 ---- 注意这个代码一定要立马能写出来 private void shiftDown(int[] arr, int parent, int len) { int son = parent * 2 + 1; while (son < len) { if (son + 1 < len &&arr[son]<arr[son+1]){ son++; } else { if (arr[parent] < arr[son]) { swap(arr, parent, son); parent = son; son = parent * 2 + 1; } else { break; } } } }
-
交换排序
基本思想:选出一个较大的数和一个较小的数,交换位置
将小数放前面,大数放到后面。
1. 冒泡排序
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:稳定
- 代码:
public void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) {//记住两个for的结束条件 for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j]>arr[j+1]){ swap(arr,j,j+1); } } } }
2. 快速排序
- 时间复杂度 O(N*logN)
- 空间复杂度 O(log2n)~O(n) ( 有序数据 的空间复杂度是O(N))
- 稳定性:不稳定
- 代码
2.1 Hoare版
适用场景:相较于希尔排序,更适合无序,因为是递归有序的情况可能造成堆满
方法:递归,基本写法和树类似
public void quicklySort(int[] arr) { quicklySort(arr, 0, arr.length - 1); } private void quicklySort(int[] arr, int left, int right) { if (left >= right) return; int root = hoare(arr, left, right); quicklySort(arr, root + 1, right); quicklySort(arr, left, root - 1); } private int hoare(int[] arr, int left, int right) { int root = left; while (right > left) {//为啥要取等号?不取就死循环了,一直交换 while (right>left && arr[right] >= arr[root]){ right--; } while (right>left && arr[left] <= arr[root]) { left++; } swap(arr, left, right);//这里适用交换,跟挖坑法有所区别 } swap(arr, root, left); return left; }
2.2 挖坑法
public void digQuicklySort(int[] arr){ digQuicklySort(arr,0,arr.length-1); }//脱裤子放屁,让使用者只需要传入一个数组就可以排序 private int digQuicklySort(int[] arr,int left,int right){ int root = dig(arr,left,right); digQuickluSort(arr,root+1,right); digQuickluSort(arr,left,root+1); return root; } private int dig(int[] arr,int left,int right){ int root = arr[left];//key存放基 while(left<right){//注意比较挖坑法与Hoare的区别 while(left<right&&arr[right]>=root){ right--; } arr[left] = arr[right];//这时候右边较大值,放到了左边,而坑到了右边 while(left<right&&arr[left]<= root){ left++; } arr[right] = arr[left];//这时候左边较小值又把右边的坑填了,而左边有了坑 } arr[left] = root;//把root基 补到中间的坑 return left; }
2.3 前后快慢指针法
public void pointerQuicklySort(int[] arr) { pointerQuicklySort(arr, 0, arr.length - 1); } private void pointerQuicklySort(int[] arr, int left, int right) { if (right<=left) return ; int root = pointer(arr, left, right); pointerQuicklySort(arr, root + 1, right); pointerQuicklySort(arr, left, root - 1); } private int pointer(int[] arr, int left, int right) { int slow = left + 1;//慢指针 for (int fast = left + 1; fast <= right; fast++) { if (arr[fast] < arr[left]) { swap(arr, fast, slow); slow++; } } swap(arr, left, slow - 1);//因为到最后slow在带交换的位置上,这位置上的值是大于arr[left]的 return slow-1;//注意这里也需要slow-1,因为slow位置是基应处位置的右边 }
3. 快速排序升级版
3.1 中位数做基
问题:快速排序不适合基本有序数据
原因:当元素有序的时候,会一直递归,递归的深度过深,使递归速度较慢。
解决方式:选取一个中位数作为 基,下面以挖坑法为例:
public void digQuicklySort(int[] arr){ digQuicklySort(arr,0,arr.length-1); } private int digQuicklySort(int[] arr,int left,int right){ int root = findMid(arr, left, right);//需找中位数 swap(arr, root, left);//将这三个数 的中位数 ,放到最左边,作为基使用。 root = dig(arr,left,right); digQuickluSort(arr,root+1,right); digQuickluSort(arr,left,root+1); return root; } private int findMid(int[] arr, int left, int right) { int mid = (left + right) / 2; //寻找左边为 left mid right 这三个数的下标 if (arr[right] > arr[left]) { if (arr[mid] > arr[right]) return right; else if (arr[mid] < arr[left]) return left; else return mid; } else { if (arr[mid] > arr[left]) return left; else if (arr[mid] < arr[right]) return right; else return mid; } }
3.2 减少递归
问题:递归过多会造成栈满
解决思路:叶子很多,假如到最后几层我们是不是就可以直接使用 插入排序,而且最后的元素也刚好基本趋于稳定
解决方式 :加一个if语句判断是继续递归还是直接插入排序,下面以挖坑法为例:
3.3 非递归实现快排
方法: 使用栈,方法基本参考非递归解决二叉树的问题
速度: 不用很多优化,但代码相对不好想。
优化??:可以尝试加入三数取中,插入排序的优化,参考3.1 3.2
代码:
public void quicklySortNoRecursion(int[] arr){ if (arr.length<=1) return; Stack<Integer> stack = new Stack<>(); int left = 0,right = arr.length-1; stack.push(left); stack.push(right); while(!stack.isEmpty()){ right = stack.pop();//记得一定要先弹出right 因为right是后放进去的 left = stack.pop(); int root = quicklyDig(arr,left,right); if (root>left+1){//这个代表,左边至少有俩元素 stack.push(left);//注意这里也要放做再放右 stack.push(root-1); } if (root<right-1){//这个代表右边至少有两个元素 stack.push(root+1); stack.push(right); } } } }
-
归并排序
- 基本思想:先分解 后合并。
- 常用场景:排序的数据量过大,内部排序的空间无法满足,这个时候就要使用外部排序。而归并排序就是最常用的外部排序。
- 例子: 内存只有1G 但是需要排序的数据有100G
- 先把文件分成200份,每个512M
- 分别对512M排序,内存已经可以放下,任意排序都可以使用
- 进行2路归并,同时对200分有序文件做归并处理,最后结果就有序了
- 代码:
递归实现
public class MergeSort { public void mergeSort(int[] arr) { mergeSort(arr, 0, arr.length - 1); } private void mergeSort(int[] arr, int left, int right) { if (right <= left) return; int mid = (right + left) / 2; mergeSort(arr, left, mid);//分解左边 mergeSort(arr, mid + 1, right);//分解右边 merge(arr, left, right, mid);//合并 } private void merge(int[] arr, int left, int right, int mid) { int left1 = left, right1 = mid; int left2 = mid + 1, right2 = right; int[] arr2 = new int[right - left + 1]; int arr2Index = 0;//arr2的坐标 两个归并段 都有数据,那边的数据小那边先放 while (right1 >= left1 && right2 >= left2) { if (arr[left1] <= arr[left2]) arr2[arr2Index++] = arr[left1++]; else arr2[arr2Index++] = arr[left2++]; } //当走到这里的时候 说明 有个归并段 当中 没有了数据 ,拷贝另一半的全部 到tmpArr数组当中 while (right1 >= left1) arr2[arr2Index++] = arr[left1++]; while (right2 >= left2) arr2[arr2Index++] = arr[left2++]; for (int i = left; i <= right; i++) { arr[i] = arr2[i - left]; } } }
非递归实现
public void mergerNoSort(int[] arr) { if (arr.length <= 1) return; //假设每个元素都是一组数据 int gap = 1;//表示每组元素的个数 while (gap <= arr.length) { for (int i = 0; i < arr.length; i += 2 * gap) { int left = i; int right = i + 2 * gap - 1; if (right > arr.length - 1) { right = arr.length - 1; } int mid = left + gap-1; if (mid>=arr.length) { mid = arr.length-1; } merge(arr, i, right, mid); } gap *= 2; } }
其他排序
1. 计数排序
- 时间复杂度:O( MAX(N,范围) )
- 空间复杂度:O(范围)
- 稳定性:稳定
- 适用场景:适合给定范围的场景
- 代码:
public void countSort(int[] arr) { int min = arr[0]; int max = arr[0]; for (int i =0;i<arr.length;i++){ if (arr[i]>max) max = arr[i]; if (arr[i]<min) min = arr[i]; } int[] arr2 = new int[max-min+1]; for (int i =0;i<arr.length;i++){ arr2[arr[i]-min]++; } for (int i = 0;i< arr2.length;i++){ while(arr2[i]>0){ System.out.print((min+i)+" "); arr2[i]--; } } }
2. 基数排序--很少用
3. 桶排序-----很少用