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


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


    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

  • 相关阅读:
    一枚渣硕的2019校招记录
    PLT hook笔记
    从排序数组中删除重复项
    golang刷Leetcode系列 --- 实现strStr()
    Docker镜像浅谈
    golang刷Leetcode系列 --- 加1
    ubuntu包管理机制
    LeetCode 234——回文链表
    LeetCode 19——删除链表的倒数第N个节点(JAVA)
    LeetCode 160——相交链表(JAVA)
  • 原文地址:https://www.cnblogs.com/musecho/p/14513247.html
Copyright © 2020-2023  润新知