【算法】归并排序:自底向上、自顶向下(分治思想)的实现

【算法】归并排序:自底向上、自顶向下(分治思想)的实现


1 自底向上

  • 算法

    import java.util.Arrays;
    
    /**
     * 归并排序:自底向上
     */
    public class Sort {
    
    	/**
    	 * @title: mergeSort
    	 * @description: 归并排序 主算法:将两个(或以上)的有序序列组成新的有序序列
    	 *               把一个长度为n的无序序列看作是n个长度为1的有序序列,并做两两合并,得到n/2个长度为2的子序列,
    	 *               如此重复,每次n*2,直到得到一个长度为n的有序序列;
    	 * @date: 2021/3/8
    	 * @param value
    	 */
    	public static void mergeSort(int[] value) {
    		int[] valueSorted = new int[value.length];
    
    		int n = 1;// 初值为1,单个子表的元素数量
    
    		/**
    		 * 注意!最后一次一趟归并,必然是把valueSorted中的子序列归并到value中
    		 * 原因:局部变量valueSorted,在函数mergeSort(int[] value) 调用完毕后会销毁, 所以要把结果放到传递过来的形参value中
    		 */
    		while (n < value.length) {// 每次循环,子序列长度n加倍
    			mergeAll(value, valueSorted, n);// 一趟归并,将value中若干子序列归并到valueSorted
    			n = 2 * n;
    			if (n >= value.length) {
    				//value = valueSorted; 这样赋值无效
    				System.arraycopy(valueSorted, 0, value, 0, value.length);
    			}
    			
    			if (n < value.length) {
    				mergeAll(valueSorted, value, n);// 一趟归并,将valueSorted中若干子序列归并到value
    				n = 2 * n;
    			}
    		}
    	}
    
    	/**
    	 * @title: mergeAll
    	 * @description: 归并排序 中的一趟归并
    	 * @date: 2021/3/8
    	 * @param value       预备归并的序列
    	 * @param valueSorted 得到的归并结果序列
    	 * @param n           归并的元素长度
    	 */
    	public static void mergeAll(int[] value, int[] valueSorted, int n) {
    		for (int i = 0; i < value.length; i += 2 * n) {
    			merge(value, valueSorted, i, i + n, n);
    		}
    	}
    
    	/**
    	 * @title: merge
    	 * @description: 归并排序 中的一次归并
    	 * @date: 2021/3/8
    	 * @param value       预备归并的序列
    	 * @param valueSorted 得到的归并结果序列
    	 * @param begin1      第一块开始位置
    	 * @param begin2      第二块开始位置
    	 * @param n           归并的元素长度
    	 */
    	public static void merge(int[] value, int[] valueSorted, int begin1, int begin2, int n) {
    		/**
    		 * i控制从value的begin1开始遍历, j控制从value的beigin2开始遍历, k控制valueSorted遍历增加元素
    		 */
    		int i = begin1, j = begin2, k = begin1;
    
    		while (i < begin1 + n && j < begin2 + n && j < value.length) { // i肯定小于j,所以只需判断j<value.length的边界情况
    
    			if (value[i] < value[j])// 升序
    				valueSorted[k++] = value[i++];
    			else
    				valueSorted[k++] = value[j++];
    
    		}
    
    		while (i < begin1 + n && i < value.length) {// j遍历完,将i剩下元素放入valueSorted
    			valueSorted[k++] = value[i++];
    		}
    		while (j < begin2 + n && j < value.length) {// i遍历完,将j剩下元素放入valueSorted
    			valueSorted[k++] = value[j++];
    		}
    
    	}
    
    
    	public static void main(String[] args) {
            Sort sort = new Sort();
            int[] a = {8, 100, 10, 1, 9, 5, 2, 99};
            System.out.println("原数组:" + Arrays.toString(a));
    
            sort.MergeSort(a);
            System.out.println("排序后的数组:" + Arrays.toString(a));
        }
    
    }
    

2 自顶向下

  • 分治思想:将一个大问题化为规模小的问题,但解决方法相同;解决了最深层的n个问题后,一层层合并上去,最终解决大问题。

  • 使用分治思想

    import java.util.Arrays;
    
    
    /**
     * @author musecho801
     * @title 归并排序(升序):自顶向下
     * @description 分治思想。
     * 将整体一分为二,对左半部分数组、右半部分数组分别进行递归,再合并
     * @date 2021/3/10 16:57
     */
    public class Sort {
    
        /**
         * @title MergeSort
         * @description 归并排序入口
         * @date 2021/3/10 17:30
         * @Param value: 待排序数组
         * @return: void
         */
        public void MergeSort(int[] value) {
            mergeRecursion(value, 0, value.length - 1);
        }
    
        /**
         * @title mergeRecursion
         * @description 归并排序 递归:将整体一分为二,对左半部分数组、右半部分数组分别进行递归,再调用合并函数
         * @date 2021/3/10 17:31
         * @Param value: 待排序数组
         * @Param low: 左半部分数组的开始下标
         * @Param high: 右半部分数组的开始下标
         * @return: void
         */
        public void mergeRecursion(int[] value, int low, int high) {
            if (low >= high)
                return;
    
            int mid = (low + high) / 2;
            mergeRecursion(value, low, mid);
            mergeRecursion(value, mid + 1, high);
            merge(value, low, mid, mid + 1, high);
        }
    
        /**
         * @title merge
         * @description 合并被一分为二的数组,将两者排序
         * @date 2021/3/10 17:31
         * @Param value: 待排序数组
         * @Param begin1: 左半部分数组的开始下标
         * @Param end1: 左半部分数组的结束下标
         * @Param begin2: 右半部分数组的开始下标
         * @Param end2: 右半部分数组的结束下标
         * @return: void
         */
        public void merge(int[] value, int begin1, int end1, int begin2, int end2) {
            int i = begin1, j = begin2, k = 0;
            //注意:定义的数组长度
            int[] valueSorted = new int[end2 - begin1 + 1];//临时结果数组
    
            //两个都没结束
            while (i <= end1 && j <= end2) {
                if (value[i] <= value[j])
                    valueSorted[k++] = value[i++];
                else
                    valueSorted[k++] = value[j++];
            }
    
            //前者没完
            while (i <= end1) {
                valueSorted[k++] = value[i++];
            }
            //后者没完
            while (j <= end2) {
                valueSorted[k++] = value[j++];
            }
    
            //只更新发生变动的排序位置元素
            i = begin1;
            for (int u = 0; u < end2 - begin1 + 1; u++) {
                value[i++] = valueSorted[u];
            }
        }
    
        public static void main(String[] args) {
            Sort sort = new Sort();
            int[] a = {8, 100, 10, 1, 9, 5, 2, 99};
            System.out.println("原数组:" + Arrays.toString(a));
    
            sort.MergeSort(a);
            System.out.println("排序后的数组:" + Arrays.toString(a));
        }
    
    }
    

参考

自顶向下代码参考:https://www.jb51.net/article/110410.htm

原文地址:https://www.cnblogs.com/musecho/p/14513247.html