归并排序的非递归实现

归并排序的核心思想是分治,也就是将原数组切分成两个子数组分配排序再将排好序的两个子数组进行合并,之前的文章已经讲过递归版本的实现,现在我们看看非递归版本的实现

第一种思路:用步长来切分数组,也就是我们在切分数组的时候,每个子数组里面的数据个数要等于步长,假设给定数组:[3,2,5,4],我们初始给定一个步长step=1,那么第一轮切分出来的子数组分别是[3],[2]合并得到[2,3],[5],[4]合并得到[4,5],这样第一轮步长等于1就完成了,第二轮步长等与原步长乘以2,也就是每一轮都将原步长乘以2,这样得到的子数组分别是[2,3]和[4,5],进行合并,依次类推

    private void mergeSort(int arr[]) {
        int n = arr.length;
        int step = 1;
        while (step < n) {//步长不能超过数组最大下标
            int l = 0;//左子数组的第一个位置
            while (l < n) {
                int m = l + step - 1;//左子数组的最后一个位置
                if (m >= n) {//左子数组都凑不够,就不存在合并的问题,直接扩大步长
                    break;
                }
                int r = m + step;//右子数组的右边界
                r = Math.min(r, n - 1);//右子数组的右边界不能超过数组最大值
                merge(arr, l, m, r);//合并两个子数组
                l = r + 1;//下一对数组继续
            }
            if (step > (n >> 1)) {//防止超过int类型最大值溢出
                break;
            }
            step = step << 1;
        }
    }
    private void merge(int[] arr, int start, int middle, int end) {
        int i = 0;
        int[] help = new int[end - start + 1];
        int index1 = start;
        int index2 = middle + 1;
        while (index1 <= middle && index2 <= end) {
            help[i++] = arr[index1] <= arr[index2] ? arr[index1++] : arr[index2++];
        }
        while (index1 <= middle) {
            help[i++] = arr[index1++];
        }
        while (index2 <= end) {
            help[i++] = arr[index2++];
        }
        int length = help.length;
        for (int i1 = 0; i1 < length; i1++) {
            arr[start + i1] = help[i1];
        }
    }

第二种思路:我们知道归并排序的递归版本其本质上是jvm层面,在栈里面不停地创建方法栈帧,而刚好jdk里面给我们提供了一个栈的实现,也就是stack(其实这里不一定要用stack,队列也可以),原理也是类似的,我们初始的时候把每一个数组元素转换成数组放到栈里面,然后每次从栈中取出两个元素(此时取出来的是数组)进行合并,然后把合并的结果继续入栈,只要栈里面的元素个数大于1,这个过程就持续下去,这样到最后,栈里面只剩一个元素的时候,就是最终归并排序的结果:

 public void mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) return; // 如果数组为空或只有一个元素,直接返回
        Queue queue = new LinkedList<>(); // 使用队列存储子数组
        for (int i = 0; i < arr.length; i++) { // 将每个元素作为一个独立的子数组加入队列
            queue.add(new int[]{arr[i]});
        }
        while (queue.size() > 1) { // 当队列中还剩下两个以上的子数组时,继续合并
            int[] left = queue.poll(); // 取出第一个子数组
            int[] right = queue.poll(); // 取出第二个子数组
            int[] merged = merge(left, right); // 合并这两个子数组
            queue.add(merged); // 将合并后的子数组重新加入队列
        }
        int[] sortedArray = queue.poll(); // 队列中的最后一个子数组就是最终的排序结果
        System.arraycopy(sortedArray, 0, arr, 0, arr.length); // 将排序结果复制回原数组
    }
    private int[] merge(int[] left, int[] right) {
        int[] merged = new int[left.length + right.length]; // 创建一个新的数组来存放合并后的结果
        int i = 0, j = 0, k = 0;

        while (i < left.length && j < right.length) {
            if (left[i] <= right[j]) {
                merged[k++] = left[i++];
            } else {
                merged[k++] = right[j++];
            }
        }
        // 处理剩余的元素
        while (i < left.length) {
            merged[k++] = left[i++];
        }
        while (j < right.length) {
            merged[k++] = right[j++];
        }
        return merged;
    }

你可能感兴趣的:(算法积累,数据结构,算法)