数据结构----排序算法

1.插入排序

插入排序的基本思想就是将待排序的数据,根据这个待排数据的大小插入一个已经排序好的序列中,直到将数组中的所有数据都插入完为止,得到一个新的有序序列

1.1直接插入排序

直接插入排序就是一种简单的插入排序。

直接插入排序就是将第一个数据当成已经是有序序列了,从第二个数据开始遍历,假设每次开始遍历的数据的下标为i,然后创建一个tmp变量来存储arr[i],然后从i-1位置开始往前遍历,用变量j来记录从i-1位置开始往前遍历时遇到的下标。

记住一个要点:i位置前面一定是一个有序序列

此时会有两种情况:

第一种情况:如果arr[j]>tmp,此时让arr[j+1]=arr[j]即可,然后让j--

第二种情况:如果arr[j]<=tmp,此时让arr[j+1]=tmp,此时跳出循环即可

此时还需要考虑到一种j越界的情况,如下图

代码实现:

java 复制代码
public class Sort {

    /**
     * 时间复杂度:O(N^2)
     * 空间复杂度:O(1)
     * 稳定性:稳定
     * @param arr
     */
    public static void insertSort(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[j+1]=tmp;
                    break;
                }
            }
            arr[j+1]=tmp;
        }
    }
}

测试:

直接插入排序有一个特点:数组越有序,排序效率就越高

1.2希尔排序

希尔排序也叫缩小增量排序,希尔排序就是在排序前对数组进行分组,有一个gap(两个数据之间的距离)的概念,将gap相同的数据分为一组,然后对每一组数据进行排序即可。

当gap==1时,在统一将数据进行排序即可

此时通过上图可以发现,一组中的数据如果越少,就越无序,但此时因为数据少,反而又会快了一点。如果数据越多,就越接近有序,但是又因为数据多,反而会慢了一点点

如当gap==1时,此时所有数据就为一组了,此时反而是更有序的

如何对一组数据进行排序呢?其实就是我们对每一组数据进行直接插入排序即可

其实希尔排序可以看做是对直接插入排序的优化,因为希尔排序会让数组越来越有序,越有序,直接插入排序就越快,

代码实现:

java 复制代码
public class Sort {
    public static void shellSort(int[] arr) {
        int gap = arr.length;
        while (gap>1){
            gap/=2;
            shell(arr,gap);
        }
    }

    private static void shell(int[] arr, int gap) {
        for (int i = gap; i < arr.length; i++) {
            int tmp = arr[i];
            int j=i-gap;
            for(;j>=0;j-=gap){
                if (arr[j]>tmp){
                    arr[j+gap]=arr[j];
                } else {
                    arr[j+gap]=tmp;
                    break;
                }
            }
            arr[j+gap]=tmp;
        }
    }
}

测试:

2.选择排序

2.1直接选择排序

直接选择排序就是每次排序一个数字时,假设这个数字的下标为i,创建一个变量minIndex来记录这次排序时最小数字的下标,先将minIndex初始化为i,创建变量j,然后j从i+1的位置完后遍历,如果遇到比arr[minIndex]小的数字,则更新minIdex,当j遍历完一次数组,此时minIndex就记录这次排序时的最小数字的下标,然后交换arr[i]和arr[minIndex]的值即可

代码实现:

java 复制代码
public class Sort {

    public static void selectSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j=i+1;j< arr.length;j++){
                if (arr[j]<arr[minIndex]){
                    minIndex=j;
                }
            }
            swap(arr,i,minIndex);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

}

测试:

2.2直接选择排序优化

此时可以定义一个变量left=0和一个变量right=arr.length-1,此时创建两个变量,minIndex和manIndex,minIndex用来记录这次排序时最小数字的下标,maxIndex用来记录这次排序时的最大数字的下标。

此时还是从left+1的位置开始遍历,如果遇到arr[j]<arr[minIndex],此时更新minIndex,如果遇到arr[i]>arr[maxIndex],此时更新maxIndex。

当一次排序完之后,就交换arr[left]和arr[minIndex],交换arr[right]和arr[maxIndex]

交换完之后,让left++,right--,知道left和right相遇,此时就排序完成了。

代码实现:

java 复制代码
public class Sort {

    public static void selectSort2(int[] arr){
        int left=0;
        int right=arr.length-1;
        while(left<right){
            int minIndex = left;
            int maxIndex = left;
            for (int i = left+1; i <= right ; i++) {
                if (arr[i]<arr[minIndex]){
                    minIndex=i;
                }
                if (arr[i]>arr[maxIndex]){
                    maxIndex=i;
                }
            }
            swap(arr,left,minIndex);
            swap(arr,right,maxIndex);
            left++;
            right--;
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }
}

此时运行代码时,排序会出错,如下图

因此此时有一个特殊情况,此时可能会出现一个maxIndex==left的情况,什么意思呢?

代码中是先交换arr[left],arr[minIndex],如下图,此时这次下次交换是没有问题的

此时在交换arr[right]和arr[maxIndex]就会出现问题了,因为一开始arr[maxIndex]是等于12的,而在上次交换时,就将arr[maxIndex]变为2,本来是想将12与arr[right]交换的,此时却变成了2与12交换,所以此时就出错了

此时只需要对left==maxIndex这种特殊情况进行处理即可

java 复制代码
public class Sort {

    public static void selectSort2(int[] arr){
        int left=0;
        int right=arr.length-1;
        while(left<right){
            int minIndex = left;
            int maxIndex = left;
            for (int i = left+1; i <= right ; i++) {
                if (arr[i]<arr[minIndex]){
                    minIndex=i;
                }
                if (arr[i]>arr[maxIndex]){
                    maxIndex=i;
                }
            }
            swap(arr,left,minIndex);
            if(left==maxIndex){
                maxIndex=minIndex;
            }
            swap(arr,right,maxIndex);
            left++;
            right--;
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }
}

2.3堆排序

堆排序,如果想进行升序排序的话,先创建一个大根堆,创建一个堆,首先就要找到最后一棵子树,从最后一棵树开始调整为大根堆,每调整一棵树时,都要向下调整,前面的一篇文章中讲过这个问题

堆排序的话,如果是升序,首先找出最后一个节点的下标,假设为end,然后交换arr[0]和arr[end],然后从将第一棵树调整为大根堆,重复此操作,知道end<=0

代码实现:

java 复制代码
public class Sort {
    
    public static void heapSort(int[] arr){
        createHeap(arr);
        int end = arr.length-1;
        while (end>0){
            swap(arr,0,end);
            shiftDown(arr,0,end);
            end--;
        }
    }

    private static void createHeap(int[] arr) {
        int last = arr.length-1;
        for (int parent = (last-1)/2;parent>=0;parent--){
            shiftDown(arr,parent,arr.length);
        }
    }

    private static void shiftDown(int[] arr, int parent, int length) {
        int child = 2*parent+1;
        while (child<length){
            if (child+1<length&&arr[child+1]>arr[child]){
                child=child+1;
            }
            if (arr[child]>arr[parent]){
                swap(arr,parent,child);
                parent=child;
                child=parent*2+1;
            } else {
                break;
            }
        }
    }
}

3.交换排序

3.1冒泡排序

冒泡排序记住两个要点:第一个循环是比较的趟数,第二个循环是每一趟比较的次数。

代码实现:

java 复制代码
    public static void bubbleSort(int[] arr){
        for (int i=0;i< arr.length-1;i++){
            boolean flag = true;
            for (int j=0;j< arr.length-1-i;j++){
                if (arr[j+1]<arr[j]){
                    swap(arr,j,j+1);
                    flag=false;
                }
            }
            if (flag){
                break;
            }
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

3.2快速排序

1.Hoare版快速排序

我们以升序排序为例,首先要找到一个基准值,定义两个指针left和right,然后将arr[left]作为基准值,然后我们就先从right指针开始往前找比基准值的小的,每次right指针找到比基准值小的,right指针就停下来,然后再从left指针开始往后走找比基准值大的,当left指针走到比基准值大,此时交换left和right指向的值,然后重复此操作,知道left和right相遇,最终在将left或者right指向的值与基准值交换,如下图

此时一次排序后数组会有一个特点,我们会用一个变量mid来记录left和right相遇的位置,mid左边的数字全是比arr[mid]小的,mid右边的数字全是比arr[mid]大的数字,如下图

然后此时就可以对mid的左边在进行同样的排序,左边进行完同样的排序后,在对mid右边的数字进行同样的排序,如下图,我只画了左边那部分,右边那部分同理

所以此时就可以通过递归左右子数的形式去处理左右部分的排序。

递归的时候要注意返回的条件,除了left==right这个条件外,还有left>right这个条件,如下图

代码实现:

java 复制代码
public class Sort {
   private static void quick(int[] arr, int start, int end) {
        if (start>=end){
            return;
        }
        int mid = findMid(arr,start,end);
        quick(arr,start,mid-1);
        quick(arr,mid+1,end);
    }

    private static int findMid(int[] arr, int left, int right) {
        int tmp = arr[left];
        int tmpLeft=left;
        while (left<right){
            while (left<right&&arr[right]>=tmp){
                right--;
            }
            while (left<right&&arr[left]<=tmp){
                left++;
            }
            swap(arr, left, right);
        }
        swap(arr,left,tmpLeft);
        return left;
    }
    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

}
1.为什么不先从从left开始往后找比基准值大的呢?

在我的理解中,快速排序的核心就是首先要让整体有序,什么意思呢?就以上面的例子为例,mid的前面的数字都是比arr[mid]小的数字,而mid后面的数字都是比arr[mid]大的数字,整体有序就是这种意思。

则此时如果我们先让left往后找比基准值大的,再从right开始往前开始找比基准值小的,此时就会有一个问题,假设之前arr[left]和arr[right]发生了一次交换,也就是说,此时arr[right]会是一个比基准值大的数字了,此时如果先让left先走,此时left可能会直接跑到right的位置,此时直接与right相遇,left和right相遇,此时就要将arr[right]和基准值交换,此时是不敢交换的,因为快速排序要讲究一个整体有序,此时如果arr[right]和基准值发生了交换,那么此时mid的前面部分就会存在一个比arr[mid]大的数字,这个就不符合整体有序的这部分。

如下图

2.移动的条件中能将等号去掉吗?

因为此时如果没有等号,遇到下面的这种情况,会导致死循环,如下图

此时的基准值为6,此时如果arr[left]和arr[right]都等于6,如果没有等号,left和right不会移动,此时arr[left]和arr[right]发生交换,此时交不交换都一样,没有等号,即使交换了,left和right是一定不会动的,所以此时就会导致死循环。

2.挖坑法实现快速排序

挖坑法就是抽象点来说就是先将基准值存储到一个变量中,然后还是从right开始,往前找比基准值小的,此时如果遇到比基准值小的,此时直接将arr[left]=right,接着再从left开始往后找比基准值大的,此时如果遇到比基准值大的,此时直接让arr[right]=arr[left],最终left和right相遇,此时让arr[left]=tmp即可

如何理解呢?

我们只需要记住一点,将right遇到比基准值小的值放到前面,将left遇到的比基准值大的值放到后面

代码实现:

java 复制代码
public class Sort {
   private static void quick(int[] arr, int start, int end) {
        if (start>=end){
            return;
        }
        int mid = findMid(arr,start,end);
        quick(arr,start,mid-1);
        quick(arr,mid+1,end);
    }

    public static int partition(int[] arr,int left,int right){
        int tmp = arr[left];
        while (left<right){
            while (left<right&&arr[right]>=tmp){
                right--;
            }
            arr[left]=arr[right];
            while (left<right&&arr[left]<=tmp){
                left++;
            }
            arr[right]=arr[left];
        }
        arr[left]=tmp;
        return left;
    }
    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

}

3.前后指针法快速排序

定义一个指针prev=left,cur=left+1,当遇到(arr[cur]<tmp&&arr[++prev]!=arr[cur])时,才交换arr[cur]和arr[prev],否则让cur++,最终cur>right跳出循环时,交换arr[left]和arr[prev]

代码实现:

java 复制代码
public class Sort {
   private static void quick(int[] arr, int start, int end) {
        if (start>=end){
            return;
        }
        int mid = findMid(arr,start,end);
        quick(arr,start,mid-1);
        quick(arr,mid+1,end);
    }

    public static int partition(int[] arr,int left,int right){
        int tmp=arr[left];
        int prev=left;
        int cur=left+1;
        while (cur<=right){
            if (arr[cur]<tmp&&arr[++prev]!=arr[cur]){
                swap(arr,prev,cur);
            }
            cur++;
        }
        swap(arr,left,prev);
        return prev;
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

}

4.一个小总结

不管是哪一个版本的快速排序,最关键的核心就是求mid那块,不管是什么版本的快速排序,partition方法的主要功能就是让mid前面的数字都小于arr[mid],让mid后面的数字大于arr[mid]。

5.快速排序优化

快速排序有一个缺点,就是在数组有序或者逆序的情况下,递归的情况会退化成一棵单边树,这就会导致快速排序的时间复杂度达到O(N^2)

5.1优化方案一:三数取中法

为什么在有序和逆序的情况下,快速排序的时间复杂度会达到O(N^2),因为这两种情况left和right第一次相遇的位置要么是在0下标或者最后一个位置,此时就会导致递归退化成单边树

三数取中法的核心思想就是打破那种有序性,从当前区间的左端,中间和右端三个元素中选取中间值作为pivot,尽可能让left和right不在最左边和最右边相遇,通过交换tmp和arr[mid]来破坏有序性,让left的后面有一个比tmp小的,能够让right在寻找比tmp小的停下来,此时pivot两边都会有数据,避免形成单边树。

快速排序的性能依赖于pivot的选择。当数组中的数据为有序或者无序时,此时每次递归都会将第一个或者最后一个位置作为pivot,那么分区后会出现一边没有数据,另一边数据有n-1个数据,这就会导致递归树退化成单边树,递归深度为O(N),时间复杂度会达到O(N^2)

代码实现:

java 复制代码
package sort2;

public class Sort {

    public static void quickSort(int[] arr){
        quick(arr,0,arr.length-1);
    }

    private static void quick(int[] arr, int start, int end) {
        if (start>=end){
            return;
        }
        int mid=findMid(arr,start,end);
        swap(arr,start,mid);
        System.out.println("start: "+start+" end: "+end);
        int pivot = partition(arr,start,end);
        quick(arr,start,pivot-1);
        quick(arr,pivot+1,end);
    }

    /**
     * 三数取中
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private static int findMid(int[] arr, int left, int right) {
        int mid=(left+right)/2;
        if (arr[left]<arr[right]){
            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;
            }
        }
    }

    public static int partition(int[] arr,int left,int right){
        int tmp=arr[left];
        while (left<right){
            while (left<right&&arr[right]>=tmp){
                right--;
            }
            arr[left]=arr[right];
            while (left<right&&arr[left]<=tmp){
                left++;
            }
            arr[right]=arr[left];
        }
        arr[left]=tmp;
        return left;
    }

    private static int partitionHoare(int[] arr, int left, int right) {
        int tmp=arr[left];
        int tmpLeft=left;
        while (left<right){
            while (left<right&&arr[right]>=tmp){
                right--;
            }
            while (left<right&&arr[left]<=tmp){
                left++;
            }
            swap(arr,left,right);
        }
        swap(arr,left,tmpLeft);
        return left;
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp=arr[i];
        arr[i]=arr[j];
        arr[j]=tmp;
    }

}
5.2优化方案二:减少递归的次数

首先要知道一个点,快排的特点就像一颗二叉树,越往下的节点就越多,节点越多就意味着快排的递归的次数就越多,递归的次数变得多就有可能导致栈溢出。

但是在快排中,还有一个特点,就是每次向下递归,排序的区间就会越小,越小就意味着这段区间的数字越有序,此时就可以通过插入排序来优化,因为插入排序的特点是数组越有序,排序的速度就越快

代码实现:

java 复制代码
    public static void quickSort(int[] arr){
        quick(arr,0,arr.length-1);
    }

    private static void quick(int[] arr, int start, int end) {
        if (start>=end){
            return;
        }
        if (start-end+1<3){
            insertSortRange(arr,start,end);
        }
        int mid=findMid(arr,start,end);
        swap(arr,start,mid);
//        System.out.println("start: "+start+" end: "+end);
        int pivot = partition(arr,start,end);
        quick(arr,start,pivot-1);
        quick(arr,pivot+1,end);
    }

    private static void insertSortRange(int[] arr, int start, int end) {
        for (int i = start; i <= end; i++) {
            int tmp=arr[i];
            int j=i-1;
            for (;j>=start;j--){
                if (arr[j]>tmp){
                    arr[j+1]=arr[j];
                }else {
                    arr[j+1]=tmp;
                    break;
                }
            }
            arr[j+1]=tmp;
        }
    }

    /**
     * 三数取中
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private static int findMid(int[] arr, int left, int right) {
        int mid=(left+right)/2;
        if (arr[left]<arr[right]){
            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;
            }
        }
    }

    public static int partition(int[] arr,int left,int right){
        int tmp=arr[left];
        while (left<right){
            while (left<right&&arr[right]>=tmp){
                right--;
            }
            arr[left]=arr[right];
            while (left<right&&arr[left]<=tmp){
                left++;
            }
            arr[right]=arr[left];
        }
        arr[left]=tmp;
        return left;
    }

6.快速排序的非递归实现

快速排序的非递归实现可以通过栈或者队列实现的,下面,我将以栈为例子来讲解

其实无论是递归还是非递归来实现快速排序,核心就是能够正确找到待排序区间的left和right,此时就可以利用栈来记录每次排序区间的left和right。

此时有一个小细节:因为我们是通过pivot来寻找left和right,但是如果pivot的某一边只有一个元素的话,此时因为这段待排序区间只有一个元素,此时就不用记录这段待排序区间的left和right了,因为一个元素的区间就是有序的

代码实现:

java 复制代码
    public static void quickSort(int[] arr){
        quickNor(arr,0,arr.length-1);
    }

    private static void quickNor(int[] arr, int start, int end){
        int pivot=partition(arr,start,end);
        Stack<Integer> stack=new Stack<>();
        if (pivot>start+1){
            stack.push(start);
            stack.push(pivot-1);
        }
        if (pivot<end-1){
            stack.push(pivot+1);
            stack.push(end);
        }
        while (!stack.isEmpty()){
            end=stack.pop();
            start=stack.pop();
            pivot=partition(arr,start,end);
            if (pivot>start+1){
                stack.push(start);
                stack.push(pivot-1);
            }
            if (pivot<end-1){
                stack.push(pivot+1);
                stack.push(end);
            }
        }
    }

4.归并排序

归并排序的核心思想:

首先递归得把数组不断地从中间分成两半(分区),知道每个子数组只有一个元素,此时只有一个元素的子数组是天然有序的,然后,再把这些有序的子数组,两两合并成一个更大的有序数组,知道最终合并成一个完整的,有序的数组,合并的过程是归并排序的关键,保证了两个有序数组合并后依然有序.

4.1递归实现

归并排序的操作分为两步:分解和合并,如下图

我的理解:虽说是有分解的过程,但是并不是真正的将数组拆了,而是一种抽象的拆,这种拆就是通过下标划分区域,其实这里的"拆"是指划分区域,比如[left,mid]区间就是一个区域,[mid+1,right]区间又是一个区域,这两个区域看起来是分开了,但是这两部分还是在同一个数组里面且这两个区域是相连的,但是其实这两个区域是在同一个数组中连续的两块区域,如下图

此时在写合并的代码,有几个细节需要注意:

第一个细节:在每次合并时,我们会创建一个中间数组,先存储好排序的结果,这个中间数组的大小时right-left-1

第二个细节:当中间数组存储好排序的结果后,此时要将中间数组的排序结果复制到原来的数组里面,此时复制的时候要写成arr[i+left]=tmp[i],不能写成arr[i]=tmp[i],因为归并是一段区间一段区间来进行排序的,这段排序区间的起点不一定是0,但区间的的起点一定是left,由于tmp是从0开始的,所以此时要arr[i+left]可以对应到tmp[i]

第三个细节:下面这种写法也是错的,因为tmp是要从0开始遍历的,此时left不一定是0

完整代码实现:

java 复制代码
public class Sort {
    public static void mergeSort(int[] arr){
        mergeTmp(arr,0,arr.length-1);
    }

    private static void mergeTmp(int[] arr, int left, int right) {
        if (left==right){
            return;
        }
        //分解
        int mid=(left+right)/2;
        mergeTmp(arr,left,mid);
        mergeTmp(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 k=0;
        int[] tmp=new int[right-left+1];
        while (s1<=e1 && s2<=e2){
            if (arr[s1]<=arr[s2]){
                tmp[k++]=arr[s1++];
            }else{
                tmp[k++]=arr[s2++];
            }
        }
        while (s1<=e1){
            tmp[k++]=arr[s1++];
        }
        while (s2<=e2){
            tmp[k++]=arr[s2++];
        }
        for (int i = left; i < k; i++) {
            arr[i]=tmp[i];
        }
    }
}

4.2 非递归实现

这里的非递归实现,本质是用迭代的方式替代了递归方式中的分区(拆分过程).

在递归版本的归并排序中,通过不断得递归,将数组划分为更下的数组,知道每个子数组只包含一个元素(此时的数组是天然有序的),然后在依次合并这些有序的子数组,最终使整个数组有序

而在非递归的实现中,直接从最小的,长度为1的有序子数组开始,此时就可以直接开始合并了,每一个依次合并,就将子数组的长度扩大(假设原来的子数组的长度为len,扩大后的长度为len=len*2),并继续按照新的长度来划分数组和合并,知道子数组的长度超过或者等于原数组的长度,此时整个数组已经变为一个完全有序的数组

总之就是按照我们上面分析的子数组的长度的变换(gap=1,2,4,8......&&len<arr.length),每次都已len来将数组分成多个子数组,然后找出每个子数组对应的left,midright边界,将相邻的两个有序子数合并成一个更大的有序子数组(红色这步跟递归的合并方式一样,直接调用即可)

代码实现:

java 复制代码
public class Sort {
    public static void mergeSort(int[] arr){
        mergeTmp(arr,0,arr.length-1);
    }

    public static void mergeNor(int[] arr){
        int gap=1;
        while (gap<arr.length){
            for (int i = 0; i < arr.length; i++) {
                int left=i;
                int mid=left+gap-1;
                //处理mid越界的情况
                if (mid>=arr.length){
                    mid=arr.length-1;
                }
                int right=mid+gap;
                //除了right越界的情况
                if (right>=arr.length){
                    right=arr.length-1;
                }
                merge(arr,left,mid,right);
            }
            gap*=2;
        }
    }

    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 k=0;
        int[] tmp=new int[right-left+1];
        while (s1<=e1 && s2<=e2){
            if (arr[s1]<=arr[s2]){
                tmp[k++]=arr[s1++];
            }else{
                tmp[k++]=arr[s2++];
            }
        }
        while (s1<=e1){
            tmp[k++]=arr[s1++];
        }
        while (s2<=e2){
            tmp[k++]=arr[s2++];
        }
        for (int i = left; i < k; i++) {
            arr[i]=tmp[i];
        }
    }
}
相关推荐
hansang_IR1 小时前
【记录】四道双指针
c++·算法·贪心·双指针
_OP_CHEN1 小时前
算法基础篇:(十二)基础算法之倍增思想:从快速幂到大数据运算优化
大数据·c++·算法·acm·算法竞赛·倍增思想
CoovallyAIHub1 小时前
分割万事万物的AI,再进化!Meta SAM 3 来了,支持中文提示词!
深度学习·算法·计算机视觉
九年义务漏网鲨鱼1 小时前
蓝桥杯算法——记忆化搜索
算法·职场和发展·蓝桥杯
xiaohua10092 小时前
ZGC实践
java·jvm
jieyu11192 小时前
安全工程师常见的基础面试题
面试题·安全工程师
蒂法就是我2 小时前
策略模式在spring哪里用到了?
java·spring·策略模式
04aaaze2 小时前
C++(C转C++)
c语言·c++·算法
青衫码上行2 小时前
【Java Web学习 | 第14篇】JavaScript(8) -正则表达式
java·前端·javascript·学习·正则表达式