数据结构 --- 超全的排序总结--八大排序,动态图,代码

 

目录

💕💕插入排序💕💕每次插入一个数据💕💕

        1. 直接插入排序😘😘直接插😘😘

        2. 希尔排序😘😘​​​​​利用堆插​​😘😘

💕💕选择排序💕💕每次选个最小值放前面💕💕

        1. 直接选择排序👻👻直接找最小值👻👻

        2. 堆排序👻👻利用堆找👻👻

💕💕交换排序💕💕每次交换💕💕

        1. 冒泡排序👻👻超级简单👻👻

        2. 快速排序👻👻面试多考👻👻

                        2.1 Hoare版👍👍​​​​​​Hoare这个人研究的👍👍

                        2.2 挖坑法👍👍挖个坑每次填坑​​​​​​​👍👍

                        2.3 前后快慢指针法👍👍​​​​​​​用前后俩指针👍👍

        3. 快速排序升级版👻👻记住哦👻👻

                        3.1 中位数做基

                        3.2 减少递归

        3.3 非递归实现快排👻👻​​​​​​​嘎嘎6👻👻

💕💕归并排序💕💕​​​​​​​大文件分开排序💕💕

递归实现

非递归实现

💕💕其他排序💕💕很少用​​​​​​​💕💕

        1. 计数排序



🐖🐖🐖🐖如果喜欢!!🐂🐂🐂🐂

🐖🐖🐖🐖欢迎关注!!🐂🐂🐂🐂

🐖🐖🐖🐖持续回访!  !🐂🐂🐂🐂

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. 桶排序-----很少用

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shn!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值