算法通关村第10关——归并排序(黄金)

算法通关村第10关——归并排序(黄金)

归并排序(MERGE-SORT)简单来说就是将大的序列先视为若干个比较小的数组,分成几个比较小的结构,然后是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分就是将问题**分(divide)成一些小的问题分别求解,而治(conquer)**则将分的阶段得到的各答案"合"在一起)。

image-20230818120808678

再来看看阶段,我们需要将两个已经有序的子序列合并成一个有序序列,就是下侧的满二叉树。比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],这个操作与合并两个有序数组的完全一样,不同的是这里是将数组的两个部分合并。

在看一下遍历时处理元素的过程:

image-20230818120846491

由图的线就可以看出算法的执行过程

写出如下的代码:

  1. 首先,定义一个mergeSort方法,接收一个数组、起始位置、结束位置和一个临时数组作为参数。
public static void mergeSort(int[] array, int start, int end, int temp[]) {
     
}
  1. 在mergeSort方法中,首先判断起始位置是否小于结束位置。如果不满足,则说明只有一个元素或没有元素需要排序,直接返回。
public static void mergeSort(int[] array, int start, int end, int temp[]) {
     if(left < right){
         return;
     }
}

如果起始位置小于结束位置,则将数组一分为二,分别调用mergeSort方法对左半部分和右半部分进行排序。

具体做法是通过计算起始位置和结束位置的中间值,将数组分为两部分,左半部分的起始位置为start,结束位置为middle,右半部分的起始位置为middle+1,结束位置为end。

递归地调用mergeSort方法对左半部分和右半部分进行排序,直到每个子数组都只剩下一个元素或没有元素。

public static void mergeSort(int[] array, int start, int end, int temp[]) {
     if(start >= end){
         return;
     }
    mergeSort(array, start, (start + end)/2, temp);
    mergeSort(array, (start + end)/2, end, temp);
}

下面用图讲解一下:

初始数组:[6, 3, 2, 1, 4, 5, 8, 7]

第一次分割:
          [6, 3, 2, 1]  [4, 5, 8, 7]
          
第二次分割:
    [6, 3]   [2, 1]     [4, 5]   [8, 7]
    
第三次分割:
  [6]  [3]   [2]  [1]   [4]  [5]   [8]  [7]

接下来,调用merge方法将左右两个已排序的子数组合并起来。merge方法接收一个数组、起始位置、结束位置和一个临时数组作为参数。

public static void mergeSort(int[] array, int start, int end, int temp[]) {
     if(start >= end){
         return;
     }
    mergeSort(array, start, (start + end)/2, temp);
    mergeSort(array, (start + end)/2, end, temp);
    merge(array, start, end, temp);
}

在merge方法中,首先定义三个变量,分别表示左半部分的起始位置left,右半部分的起始位置right,以及临时数组的索引位置index。

static void merge(int[] array, int start, int end, int[] temp) {
    
}

使用while循环比较左右两个子数组的元素大小,将较小的元素依次放入临时数组中。当其中一个子数组的所有元素都放入临时数组后,跳出循环。

static void merge(int[] array, int start, int end, int[] temp) {
	int middle = (start + end) / 2;
 	int left = start;
 	int right = middle + 1;
 	int index = left;
    while (left <= middle && right <= end) {
        if (array[left] < array[right]) {
        	temp[index++] = array[left++];
        } else {
        	temp[index++] = array[right++];
        }
    }
}

如果左半部分还有剩余元素,将其依次放入临时数组中。

如果右半部分还有剩余元素,将其依次放入临时数组中。

while (left <= middle) {
	temp[index++] = array[left++];
}
while (right <= end) {
	temp[index++] = array[right++];
}

最后,将临时数组中的元素复制回原数组中,从起始位置到结束位置,完成排序。

for (int i = start; i <= end; i++) {
	array[i] = temp[i];
}

开始合并过程,将子数组按照顺序合并成有序数组。

第一次合并:
    [3, 6]     [1, 2]     [4, 5]     [7, 8]
    
第二次合并:
 [1, 2, 3, 6]   [4, 5, 7, 8]
 
第三次合并:
[1, 2, 3, 4, 5, 6, 7, 8]

最终,得到了一个有序的数组。

在main方法中,我们可以测试mergeSort方法。创建一个测试数组,然后创建一个与测试数组长度相同的临时数组。调用mergeSort方法对测试数组进行排序,并打印结果。

public class MergeSort {
    public static void mergeSort(int[] array, int start, int end, int temp[]) {
        if (start >= end) {
            return;
        }
        mergeSort(array, start, (start + end) / 2, temp);
        mergeSort(array, (start + end) / 2 + 1, end, temp);
        merge(array, start, end, temp);
    }

    public static void merge(int[] array, int start, int end, int[] temp) {
        int middle = (start + end) / 2;
        int left = start;
        int right = middle + 1;
        int index = left;
        while (left <= middle && right <= end) {
            if (array[left] < array[right]) {
                temp[index++] = array[left++];
            } else {
                temp[index++] = array[right++];
            }
        }
        while (left <= middle) {
            temp[index++] = array[left++];
        }
        while (right <= end) {
            temp[index++] = array[right++];
        }
        for (int i = start; i <= end; i++) {
            array[i] = temp[i];
        }
    }

    public static void main(String[] args) {
        int[] array = {6, 3, 2, 1, 4, 5, 8, 7};
        int[] temp = new int[array.length];
        mergeSort(array, 0, array.length - 1, temp);
        System.out.println(Arrays.toString(array));
    }
}

那我们再来写一下力扣的排序数组题目:
912. 排序数组

class Solution {
    public int[] sortArray(int[] nums) {
        // 创建一个临时数组,用于存储合并过程中的临时结果
        int[] temp = new int[nums.length];
        
        // 调用归并排序函数
        mergeSort(nums, 0, nums.length-1, temp);
        
        return nums;
    }
    
    private void mergeSort(int[] nums, int start, int end, int[] temp) {
        if (start < end) { // 当起始位置小于结束位置时,继续拆分数组
            int mid = (start + end) >> 1; // 计算中间位置
            
            // 分别对左右两个子数组进行归并排序
            mergeSort(nums, start, mid, temp); // 对左半部分进行排序
            mergeSort(nums, mid+1, end, temp); // 对右半部分进行排序
            
            // 合并两个有序的子数组
            merge(nums, start, mid, end, temp);
        }
    }

    private void merge(int[] nums, int start, int mid, int end, int[] temp) {
        int left = start; // 左子数组的起始位置
        int right = mid + 1; // 右子数组的起始位置
        int index = start; // 临时数组的起始位置
        
        // 比较左右子数组的元素,将较小的元素放入临时数组中
        while (left <= mid && right <= end) {
            if (nums[left] < nums[right]) {
                temp[index++] = nums[left++];
            } else {
                temp[index++] = nums[right++];
            }
        }
        
        // 将左子数组中剩余的元素放入临时数组中
        while (left <= mid) {
            temp[index++] = nums[left++];
        }
        
        // 将右子数组中剩余的元素放入临时数组中
        while (right <= end) {
            temp[index++] = nums[right++];
        }
        
        // 将临时数组中的元素复制回原数组中
        for (int i = start; i <= end; i++) {
            nums[i] = temp[i];
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值