java 简单 数组 自然合并排序

题目:对所给元素存储于数组中或链表中(选择一种情形),写出自然合并排序算法

结果演示:

 

基本思想:

自然排序是在合并排序的基础上修改而成。

①合并排序

给出一个n个元素无序的整数数组, 将其一分为2,则一个子集为n/2,再将子集划分为2,不断划分直到只有一个元素。

如 9  8  6  7  3  4  5  2 1,划分为{9},{8},{6},{7},{3},{4},{5},{2},{1}

在相邻的两两合并排序,如合并一次为{8,9},{6,7},{3,4},{5,2},{1}

不断重复合并排序直至全部被合并成一个有序数组。

②自然合并排序

(1)与合并排序的主要不同为划分子集方法。自然合并排序根据非按自然排列(一般指递增)的子段划分,只有后面的数比前面小,则划开。

如 9  8  6  7  3  4  5  2 1,划分为{9},{8},{6,7},{3,4,5},{2},{1}

由此划分的子段大小不一,因此需要一个数组a来存储每个子段末尾在原数组中的下标,

如:上述的a为:a={0,1,3,6,7,8}(由于在声明数组s时不确认有多少子段,按最坏情况声明则为n个,又为了搜索方便,后面用divideNum来记录多少个子段)

对子段的访问根据a上记录的索引值访问。

注:在取段时,其开头为前一段的结尾索引+1,即第i段开头索引为a[i-1]+1。当取第1段时,应直接取为0,否则数组a会-1越界。

(2)此时数组a中的每个元素分段为最小分段,在后续合并中会将最小分段合并成大分段,用s表示该合成的最小分段的数目。

如:{9}与{8}合并成{8,9},{6,7}和{3,4,5}合并成{3,4,5,6,7},此时s=1;

{8,9}和{3,4,5,6,7}合并,此时s=2;

不难发现s的增加规律为s+=s;

(3)在合并过程中难免出现有的子段没有更多相同s大小的合并对象的情况:

情况一:如3个小段,合并第一次s=1时,多出了s=1一个子段,此时将其复制到第一次合并的结果后面即可,等待下一次合并

情况二:如7个小段合并到第3次,此时有3个s=2的子段,一个s=1的子段,多的s=2的子段和s=1的子段合并,在编程过程中主要体现为索引取值规律的不同

注:测试时难免疏忽,哪里有可能越界欢迎指出

代码中命名有些混乱,可以结合注释掉的代码段运行理解

package algorism2;
import java.util.Scanner;
public class NaturalMerger {
	static int divideNum=0;
	public static int[] SSplit(String input){//将输入的字符串划分成整数数组
		int i=0;
		String[] inps=input.split(" "); //以空格划分
		int[] a=new int[inps.length];
    	for(String s:inps){  //迭代取出
    		a[i++]=Integer.valueOf(s);  //字符串转整数
    	}
    	return a;
	}
	
	public static int[] divide(int[] a){  //按自然排序划分为段,将a中每段末尾的索引存储到一个数组s中
		int[] s=new int[a.length];
		if(a.length==1){
			s[0]=0;
			return s;
		}
		for(int i=1;i<a.length;i++){ 
			if(a[i]<a[i-1]){//非自然排列划分
				s[divideNum]=i-1;
				divideNum++;
			}
			if(i==a.length-1){//最后一个结尾
				s[divideNum]=i;
				divideNum++;
			}
		}
		/*System.out.print("分割后为:");
		for(int i=0;i<NaturalMerger.divideNum;i++)
			System.out.print(s[i]+" ");
		System.out.println("");*/
		return s;
	}	
	
	public static void merger(int[] input,int[] output,int start,int middle,int end,int s){
		//合并input[start:middle]和input[middle+1:end]到output[0:end-start]
		int i=start,j=middle+1,o=start;
		
		while(i<=middle&&j<=end){  //可以将其中最大整数较小的一段添加完
			if(input[i]<=input[j]){
				output[o++]=input[i++];
			}
			else{
				output[o++]=input[j++];
			}
		}
		if(i>middle){  //将另一段后面全加进去
			for(;j<=end;j++)output[o++]=input[j];
		}
		else{
			for(;i<=middle;i++)output[o++]=input[i];
		}
		System.out.print("s:"+s+"合并后为:");
		for(int in:output)System.out.print(in+" ");
		System.out.println("");
		return ;
	}
	
	public static void DeToMerger(int[] a,int[] input,int[] output,int s){
		//根据divide出最小分段末尾索引的数组,相邻两两合并
		//s表示为第几阶段合并,第n阶段为n个最小段与n个最小段合并
		int i=0;
		while(i<=NaturalMerger.divideNum-2*s){
			//合并大小为s的相邻2段
			int r=(i+2*s-1)>NaturalMerger.divideNum?NaturalMerger.divideNum:(i+2*s-1);
			//System.out.println("i:"+i+" r:"+r);
			if(i==0)
				NaturalMerger.merger(input, output, 0, a[i+s-1], a[r],s);
			else
				NaturalMerger.merger(input, output, a[i-1]+1, a[i+s-1], a[r],s);
			i=i+2*s;
		}
		if(i+s<NaturalMerger.divideNum){
			//System.out.println("i:"+i+"s:"+s);
			if(i==0)
				NaturalMerger.merger(input, output, 0, a[i+s-1], a[NaturalMerger.divideNum-1],s);
			else
				NaturalMerger.merger(input, output, a[i-1]+1, a[i+s-1], a[NaturalMerger.divideNum-1],s);
		}
		else if(i<NaturalMerger.divideNum)
			for(int j=a[i];j<=input.length-1;j++)
				output[j]=input[j];
	}
	
	public static void mergerSort(int[] input,int[] s,int[] output){
		int x=1; //对应最小段数
		//System.out.println(NaturalMerger.divideNum);
		while(x<NaturalMerger.divideNum){
			NaturalMerger.DeToMerger(s, input, output, x);//合并一轮
			x+=x;//对应的最小段加倍
			if(x>NaturalMerger.divideNum){
				System.out.print("output最终结果:");
				for(int in:output)System.out.print(in+" ");
				System.out.println("");
			    return;
			}
			NaturalMerger.DeToMerger(s, output, input, x);//将上次合并的结果作为这次要合并的数组
			x+=x;		
		}
		System.out.println("input最终结果:");
		for(int in:input)System.out.print(in+" ");
	}
	
	public static void main(String args[]){
    	
    	System.out.println("请输入整数串,用空格隔开");
    	Scanner sc=new Scanner(System.in);
    	String input=sc.nextLine();  //获取整数的字符串
    	int[] a=NaturalMerger.SSplit(input);//将字符串划分成整数数组
    	int[] s=NaturalMerger.divide(a);
		int[] output=new int[a.length];
    	NaturalMerger.mergerSort(a,s,output);	
    }
}

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值