• 数据结构(3)归并排序


    归并排序

    • 归并排序原理

      归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或1的有序子序列,再两两归并,直至得到一个长度为n的有序序列为止,这种排序方法成为2路归并排序。

    • 先看代码  
    package Sort;
    
    class MergeSort {
    
    	public static void main(String[] args) {
    		
    		int[] list = {80,40,50,30,60,70,10,90,20};;
    		MergeSort ms = new MergeSort(); 
    		ms.MSort(list);
    		System.out.println("");
    		System.out.println("最终结果:");
    		ms.printArrry(list);
    	}
    	
    
    	private void printArrry(int[] a){
    		for(int k = 0; k <= a.length-1; k++){
    			System.out.print(a[k]);
    		}
    	}
    	private void MSort(int[] list) {
    		mergeSort(list, 0, list.length-1);
    	}
    	
    	private void mergeSort(int [] list,int a, int b){
    
    		int mid;
    		if (a == b){
    			//System.out.println("执行到最底层:");
    			//printArrry(list);
    			return; 
    		}
    		else {
    			mid = (a+b)/2;
    			mergeSort(list, a, mid);
    			mergeSort(list, mid+1, b);
    			Merge(list, a, mid, b);
    			System.out.println("
    "+"left:"+a+" mid:"+mid+" right:"+b);
    			System.out.println("此时数组为:");
    			printArrry(list);
    		}
    	}
    	
    	private void Merge(int [] list, int left, int mid, int right){
    		int a,b,c,d;
    		a = left;
    		b = mid+1;
    		c = left;
    		d = left;
    		int[] tempaddr = new int[list.length];
    		while (a<=mid && b<=right) {
    			if (list[a] <= list[b]) {
    				tempaddr[c++] = list[a++];
    			}
    			else {
    				tempaddr[c++] = list[b++];
    			}
    		}
    		while (a<=mid) {
    			tempaddr[c++] = list[a++];
    		}
    		while (b<=right) {
    			tempaddr[c++] = list[b++];
    		}
    		while (d <= right){
    			list[d] = tempaddr[d++];
    		}
    	}
    }
    
    • mergeSort()分析
    	private void mergeSort(int [] list,int a, int b){
    		int mid;
    		/*当拆分至最小单元时,则跳出*/
    		if (a == b){
    			return; 
    		}
    		else {
    			mid = (a+b)/2;
    			/*将上一次拆分后的数组list的前半部分再进行拆分*/
    			mergeSort(list, a, mid);
    			/*将上一次拆分后数组list的后半部分再进行拆分*/
    			mergeSort(list, mid+1, b);
    			/*将拆分至list最小单元的前半部分和后半部分进行归并*/
    			Merge(list, a, mid, b);
    			System.out.println("
    "+"left:"+a+" mid:"+mid+" right:"+b);
    			System.out.println("此时数组为:");
    			printArrry(list);
    		}
    	}
    
    • Merge()分析  
    	private void Merge(int [] list, int left, int mid, int right){
    		int a,b,c,d;
    		a = left;
    		b = mid+1;
    		c = left;
    		d = left;
    		int[] tempaddr = new int[list.length];
    		/*将两个有序的数组,从头开始比较,较小值存入tempaddr[]*/
    		while (a<=mid && b<=right) {
    			if (list[a] <= list[b]) {
    				tempaddr[c++] = list[a++];
    			}
    			else {
    				tempaddr[c++] = list[b++];
    			}
    		}
    		/*将前半部分还有剩余的数组取出,存入tempaddr[]*/
    		while (a<=mid) {
    			tempaddr[c++] = list[a++];
    		}
    		/*将后半部分还有剩余的数组取出,存入tempaddr[]*/
    		while (b<=right) {
    			tempaddr[c++] = list[b++];
    		}
    		/*将已经归并好的数组,传给list,使得list原有的对应于list[left..right]的部分有序*/
    		while (d <= right){
    			list[d] = tempaddr[d++];
    		}
    	}
    
    • 输出结果  
    left:0 mid:0 right:1
    此时数组为:
    408050306070109020
    left:0 mid:1 right:2
    此时数组为:
    405080306070109020
    left:3 mid:3 right:4
    此时数组为:
    405080306070109020
    left:0 mid:2 right:4
    此时数组为:
    304050608070109020
    left:5 mid:5 right:6
    此时数组为:
    304050608010709020
    left:7 mid:7 right:8
    此时数组为:
    304050608010702090
    left:5 mid:6 right:8
    此时数组为:
    304050608010207090
    left:0 mid:4 right:8
    此时数组为:
    102030405060708090
    最终结果:
    102030405060708090
    
    • 原理图示

      红色部分显示的是,此时操作的数组的区间,黑色部分,则是未被操作的区间。

      通过这张图可以看出,因为mid=(a+b)/2,mergeSort(list, a, mid),所以list数组一路向下拆分,从0-8,到0-4,到0-2,到0-1,最后到0-0,此时得到return,返回递归上一层,此时mid为0,mergeSort(list, mid+1, b),也可以得到return,然后就执行Merge(list, a, mid, b)。这句话就是把此时(a,mid,b)=(0,0,1)进行了归并,使得list[0..1]有顺序。

      以此类推,接下来依次是(a,mid,b)=(0,1,2),(a,mid,b)=(0,2,4),....,(a,mid,b)=(5,6,8),(a,mid,b)=(0,4,8)

    • 时间复杂度分析

    将list[1..n]的相邻长度为h的有序序列进行两两归并,并将结果放回list[1..n],需要耗时O(N)的时间,由完全二叉树的深度克制,整个排序需要进行log2N次,所以总的时间复杂度为O(nlogn)。这比上一篇讲的冒泡排序,直选排序都要快速,所以应用范围也更为广泛。

  • 相关阅读:
    CF263E Rhombus
    AtCoder Grand Contest 034
    Docker C/S 架构逻辑图
    使用filledeat modules配置
    filebeat分别收集多个类型日志
    ELK部署收集日志
    ES界面
    Django下的post请求访问出现403错误
    Django配置(urls.py)
    Day-1 python
  • 原文地址:https://www.cnblogs.com/danbing/p/5147917.html
Copyright © 2020-2023  润新知