我的飞书:https://rvg7rs2jk1g.feishu.cn/docx/AWfNdtY6toyOjSxWT84cTmwGnhg
概念+分类
概念
排序:将一串记录,按照其中某个或某些关键字的大小,进行递增或者递减的排列的操作,是笔试和面试中重点考的考试题型之一
同时,在提到排序的时候就不得不提到稳定性了
稳定性: 当有两个相同的元素处于待排序的数据中,同时他们分别处于前后位置,在进行排序之后,如果他们进行了位置的调换,则说明是不稳定的;如果没有发生调换,则说明是稳定的 --> 两个相同数据的相对位置是否发生改变
内部排序:将数据元素全部放在内存中的排序
外部元素:不能在内外村之间移动数据的排序
排序的代码一定要会写!!不能只会原理
常见排序算法(七种)
所有的排序都是在原数组进行排序,不需要另外创建一个空数组
-
插入排序
-
直接插入排序
-
希尔排序
-
-
选择排序
-
选择排序
-
堆排序
-
-
交换排序
-
冒泡排序
-
快速排序
-
-
归并排序
算法实现(暂且实现递增)
生成数组:采用随机生成数据的形式来进行排序
public static void main(String[] args) {
Random random = new Random();
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(0,9);
System.out.print(arr[i]+" ");
}
System.out.println();
//排序调用
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
插入排序
将待排序的数据,逐个按照大小插入已经排好序的序列中,知道所有数据都全部插入序列,就像斗地主整理手牌一样
直接插入排序
定义两个索引值(i j), i 代表当前需要进行排序的位置,j 为 i 之前已经排好序的末尾元素位置,再将i位置的元素与j位置进行比较,并将j不断向前移,知道找到一个位置的元素大小比 i 位置的元素小,此时这个元素的后一个位置就是 i 元素的真正位置,同时之前进行对比的元素都要进行后移;如果没有找到比 i 小的元素(都比 i 大)那么此时 j 已经遍历完排好序的序列,此时 j 为-1,i元素就要放入 j+1 的位置;同时如果原本 j 位置上的元素就已经比 i
小了,那 i 对应的元素就还是在原位
步骤
需要另外生成一个地址用来存储数据,进行对比
-
i 从第二个数据开始进行比较,同时 i 之前的数据都是有序的
-
j=i-1; 用来代表前面已经有序的元素
-
j<0 则一次循环停止,代表原本处于i位置上的元素找到了位置
-
i++;
代码
public static void sort1(int[] arr){
for (int i = 1; i < arr.length; i++) {
int tmp = arr[i];
int j = i-1;//代表已经排序的最后一个元素的位置
for (; j >=0 ; j--) {
if(arr[j]>tmp){
arr[j+1] = arr[j];
}else {
//这里代表已经找到了arr[i]的位置
//此时的位置就应该是 j+1;
break;
}
}
//当已排序的序列完全遍历之后,就要将arr[j+1]进行赋值
//不管是break退出循环,还是遍历完全退出,都要给j+1进行赋值
//break: 找到位置,为 j+1
//全部遍历退出: 找到位置为序列的开头,也就是arr[0],此时 j==-1,所以也是在 j+1 的位置
//因为只要是循环结束就会执行语句,所以不能直接将arr[0]设定为tmp
//有两种循环退出方式
arr[j+1] = tmp;
}
}
结果

结果分析:
-
时间复杂度:
-
最好情况:数据一开始就已经是有序的 --> O(N)
-
最坏情况:数据一开始就是逆序的 --> O(N^2)
-
-
空间复杂度: O(1)
-
稳定性: 稳定
-
特点: 数据越有序,直接插入越快(排序优化)
希尔排序(缩小增量排序)
将待排序的数组分成若干个子数组,对每个子数组进行插入排序,然后逐渐减少子数组的数量,使得整个数组变得更有序,最后再进行一次简单的插入排序
分组:
图中是第一次分组
将相同颜色的部分分为一组,也就是说,希尔排序的分组不是将相邻数据进行分组,而是选定一个特定的标记数,每隔一个标记数的数据就分为一组,图中的标记数是5,之后再不断减少标记数
跳跃式分组:尽可能将最小的元素往前拿,最大的元素往后(第一次的标记数就是数组长度的一半) --> 提升效率
只有当标记数为1的时候,才是排序,在此之前都是预排序
代码
public static void shellSort(int[] arr){
int gap = arr.length;
while (gap>1){
gap = gap/2;
shell(arr,gap);
}
}
public static void shell(int[] arr,int gap){
int tmp;
for (int i = gap; i <arr.length ; i++) {
tmp = arr[i];
int j = i-gap;
for (; j >=0 ; j+=gap) {
if(arr[j]>tmp){
arr[j+gap] = arr[j];
}else {
break;
}
}
arr[j+gap] = tmp;
}
}
结果

结果分析:
-
时间复杂度: O(N^1.25) ~ O(N^1.5)
-
空间复杂度: O(1)
-
稳定性: 不稳定
选择排序
每一次从待排序的数据元素中选出最小(最大)的元素,存放到序列的其实位置,直到全部数据排序完毕
直接选择排序
代码
第一种
public static void selectSort(int[] arr){
for (int i = 0; i < arr.length ; i++) {
int flag = i;//记录下标
for (int j = i+1; j < arr.length ; j++) {
if (arr[j]<arr[flag]){
flag=j;//让下标始终为当前找到的最小值的下标
}
}
int tmp = arr[i];
arr[i] = arr[flag];
arr[flag] = tmp;
}
}
双指针:
public static void selectSort2(int[] arr){
int left = 0;
int right = arr.length-1;
while (left<right){
int i = left+1;
int minIndex = left;
int maxIndex = left;
while (i<=right){
if(arr[i]<arr[minIndex]){
minIndex = i;
}
if(arr[i]>arr[maxIndex]){
maxIndex = i;
}
i++;
}
int tmp = arr[right];
arr[right] = arr[maxIndex];
arr[maxIndex] = tmp;
tmp = arr[left];
arr[left] = arr[minIndex];
arr[minIndex] = tmp;
left++;
right--;
}
}
结果

结果分析
-
时间复杂度: O(N^2)
-
空间复杂度: O(1)
-
稳定性: 不稳定
堆排序
代码
public static void heapSort(int[] arr){
//创建大根堆
createHeap(arr);
int end = arr.length-1;
while (end>0){
int tmp = arr[0];
arr[0] = arr[end];
arr[end] = tmp;
siftDown(arr,0,end);
end--;
}
}
private static void createHeap(int[] arr){
//从最后一个子树的父亲节点开始
for (int parent = (arr.length-1-1)/2; parent >=0 ; parent--) {
siftDown(arr,parent,arr.length);
}
}
private static void siftDown(int[] arr,int parent,int len){
//向下调整
int child = (2*parent)+1;
while (child<len){
if(child+1<len&&arr[child]<arr[child+1]){
child = child+1;
}
if (arr[child]>arr[parent]){
int tmp = arr[child];
arr[child] = arr[parent];
arr[parent] = tmp;
parent = child;
child = 2*parent+1;
}else{
break;
}
}
}
结果

结果分析:
-
时间复杂度: O(N*logN)
-
空间复杂度: O(1)
-
稳定性: 不稳定
交换排序
冒泡排序
代码
public static void bubbleSort(int[] array){
//i用来表示趟数
for (int i = 0; i < array.length-1; i++) {
boolean flag = false;
for (int j = 0; j < array.length-1; j++) {
if(array[j]>array[j+1]){
swap(array,j,j+1);
flag = true;
}
}
//优化
if(flag==false){
//有序
break;
}
}
}
private static void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
结果

结果分析:
-
时间复杂度:
-
优化前: 最好最坏都是 O(N^2)
-
优化后: 最好是 O(N) 最坏是 O(N^2)
-
-
空间复杂度: O(1)
-
稳定性: 稳定
快速排序
是一种二叉树结构的排序算法,找一个基准值,将数据分为两部分,使基准值左边都比他小,右边都比他大,同时使用的是双指针,左右两指针相遇的位置就是基准值按照排序应该在的位置
代码
1.Hoare
public static void fastSort(int[] arr){
sort3(arr,0,arr.length-1);
}
private static void sort3(int[] arr,int start,int end){
if(start>=end){
return;
}
int flag = partitionHoare(arr,start,end);
sort3(arr,start,flag-1);
sort3(arr,flag+1,end);
}
private static int partitionHoare(int[] arr,int left,int right){
int temp = arr[left];
int flag = left;
while (left<right){
while (left<right&&arr[right]>=temp)
right--;
while (left<right&&arr[left]<=temp)
left++;
swap(arr,left,right);
}
swap(arr,left,flag);
return left;
}
private static void swap(int[] arr,int i,int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
2.挖坑法
Hoare的一种优化
public static void fastSort(int[] arr){
sort3(arr,0,arr.length-1);
}
private static void sort3(int[] arr,int start,int end){
if(start>=end){
return;
}
int flag = partition(arr,start,end);
sort3(arr,start,flag-1);
sort3(arr,flag+1,end);
}
private static int partition(int[] arr,int left,int right){
int temp = arr[left];
int flag = left;
while (left<right){
while (left<right&&arr[right]>=temp)
right--;
while (left<right&&arr[left]<=temp)
left++;
if (left>=right){
break;
}
arr[flag] = arr[right];
arr[right] = arr[left];
}
arr[left] = temp;
return left;
}
结果

结果分析:
-
时间复杂度:
-
最好情况: O(N*logN)
-
最坏情况: O(N^2)
-
-
空间复杂度:
-
最好情况: O(logN)
-
最坏情况: O(N)
-
-
稳定性: 不稳定
归并排序
分治法: 将已经有序的子序列进行合并(二路归并)
代码
public static void mergeSort(int[] arr){
mergeFunc(arr,0,arr.length-1);
}
private static void mergeFunc(int[] arr,int left,int right){
if (left>=right){
return;
}
int mid = left+((right-left)>>1);
//分组
mergeFunc(arr,left,mid);
mergeFunc(arr,mid+1,right);
merge(arr,left,mid,right);
}
private static void merge(int[] arr,int left,int mid,int right){
int s1 = left;
int e1 = mid;
int s2 = mid+1;
int e2 = right;
int[] tmp = new int[right-left+1];
int flag = 0;
//1.保证两个表都有数据
while (s1<=e1&&s2<=e2){
if(arr[s1]<=arr[s2]){
tmp[flag] = arr[s1];
s1++;
}else {
tmp[flag] = arr[s2];
s2++;
}
flag++;
}
//剩余数据
while (s1<=e1){
tmp[flag] = arr[s1];
flag++;
s1++;
}
while (s2<=e2){
tmp[flag] = arr[s2];
flag++;
s2++;
}
//返回到原数组
for (int i = 0; i <flag ; i++) {
arr[i+left] = tmp[i];
}
}
结果

结果分析:
-
时间复杂度: O(N*logN)
-
空间复杂度: O(N)
-
稳定性: 稳定
总结
| 名称 | 时间复杂度 | 空间复杂度 | 稳定性 |
| 直接插入排序 | 最好情况: O(N) 最坏情况: O(N^2) 平均:O(N^2) | O(1) | 稳定 |
| 希尔排序 | O(N^1.25) ~ O(N^1.5) 平均:O(N^1.3) | O(1) | 不稳定 |
| 选择排序 | O(N^2) | O(1) | 不稳定 |
| 堆排序 | O(N*logN) | O(1) | 不稳定 |
| 冒泡排序 | 优化前: 最好最坏都是 O(N^2) 优化后: 最好是 O(N) 最坏是 O(N^2) 平均:O(N^2) | O(1) | 稳定 |
| 快速排序 | 最好情况: O(N*logN) 最坏情况: O(N^2) | 最好情况: O(logN) 最坏情况: O(N) | 不稳定 |
| 归并排序 | O(N*logN) | O(N) | 稳定 |

1671

被折叠的 条评论
为什么被折叠?



