分治归并排序算法——Java实现

1、分治法

    许多有用的算法在结构上是递归的:为了解决一个给定的问题,算法一次或多次递归地调用其自身以解决紧密相关的若干子问题。这些算法典型地遵循分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

    分治模式在每层递归时都有三个步骤:

   (1)分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。

   (2)解决这些子问题,递归地求解各子问题。然而,若子问题的规模足够小,则直接求解。

   (3)合并这些子问题的解成原问题的解。

2、归并排序算法

    归并排序算法完全遵循分治模式。直观上其操作如下:

   (1)分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。

   (2)解决:使用归并排序递归地排序两个子序列。

   (3)合并:合并两个已排序的子序列以产生已排序的答案。

    当待排序的序列长度为1时,递归“开始回升”,在这种情况下不要做任何工作,因为长度为1的每个序列都已排好序。归并排序算法的关键操作是“合并”步骤中两个已排序序列的合并。我们通过调用一个辅助过程Merge(A,p,q,r)来完成合并,其中A是一个数组,p、q和r是数组下标,满足p<=q<r。该过程假设子数组A[p,q]和A[q+1,r]都已排好序。它合并这两个子数组形成单一的已排好序的子数组并代替当前的子数组A[p,r]。

    过程Merge按以下方式工作。回到我们玩扑克牌的例子,假设桌上有两堆牌面朝上的牌,每堆都已排序,最小的牌在顶上。我们希望把这两堆牌合并成单一的排好序的输出堆,牌面朝下地放在桌上。我们的基本步骤包括在牌面朝上的两堆牌的顶上两张牌中选取较小的一张,将该牌从其堆中移开(该堆的顶上将显露一张新牌)并牌面朝下地将该牌放置到输出堆。

    下面是Merge的伪代码:

Merge(A,p,q,r):
tmp[1,..,r-p+1]
i = p
j = q+1
while i <= q && j <= r			
   if A[i] <= A[j]				
	tmp[k++] = A[i++];			
   else			
	tmp[k++] = A[j++];							
   while i <= q			
	tmp[k++] = A[i++];				
   while j <= r			
	tmp[k++] = A[j++];				
   for k2 = 0 to tmp.length			
	A[k2+p] = tmp[k2]; 



    现在我们可以把过程Merge作为归并排序算法中的一个子程序来用。下面的过程Merge_sort(A,p,r)排序子数组A[p,r]中的元素。若p>=r,则该子数组最多有一个元素,所以已经排好序。否则,分解步骤简单地计算一个下标q,将A[p,r]分成两个子数组A[p,q]和A[q+1,r],前者包含[n/2]个元素,后者包含[n/2]个元素。

Merge_sort(A,p,r):
if p < r
     q = (p+r)/2
     Merge_sort(A,p,q)
     Merge_sort(A,q+1,r)
     Merge(A,p,q,r)
    为了排序整个序列A=(A[0],A[1],...,A[n]),我们执行初始调用Merge_sort(A,0,A.length),这里再次有A.length = n。图2-4自底向上地说明了当n为2的幂时该过程的操作。算法由以下操作组成:合并只含1项的序列对形成长度为2的排好序的序列,合并长度为2的序列对形成长度为4的排好序的序列,依此下去,直到长度为n/2的两个序列被合并最终形成长度为n的排好序的序列。


3、Java代码实现

public class Merge_sort_test {
	
	public static void Merge(int[] A,int p,int q,int r){
		
		int[] tmp = new int[r-p+1];//声明一个临时数组,长度为要归并数组的长度
		int i = p;   //记住左边数组第一个元素的下标
		int j = q+1; //记住右边数组第一个元素的下标
		int k = 0;		
		while(i <= q && j <= r){
			//左边数组元素和右边数组元素比较,把小的元素赋给临时数组
			if(A[i] <= A[j]){				
				tmp[k++] = A[i++];
			}
			else{				
				tmp[k++] = A[j++];
			}
		}
		//把左边剩余的数组元素赋给临时数组
		while(i <= q){			
			tmp[k++] = A[i++];
		}
		//把右边剩余的数组元素赋给临时数组
		while(j <= r){			
			tmp[k++] = A[j++];
		}
		//用临时数组元素覆盖原数组元素
		for(int k2 = 0;k2 < tmp.length;k2++){			
			A[k2+p] = tmp[k2]; 
		}
	}
	public static void/*int[]*/ Merge_sort(int[] A,int p,int r){
		
		int q = (p+r)/2;
		if(p < r){
			//递归调用			
			Merge_sort(A,p,q);
			Merge_sort(A,q + 1,r);
			//归并排序数据元素
			Merge(A,p,q,r);
		}
		//return A;
	}
	public static void main(String[] args) {
		// 
		int[] A = {5,2,4,7,1,3,2,6};
		System.out.println("原始数据: ");
		for(int i = 0;i < A.length;i++){
			
			System.out.print(A[i] + " ");
		}
		System.out.println();
		Merge_sort(A,0,A.length -1);
		System.out.println("输出结果: ");
		for(int i = 0;i < A.length;i++){
			
			System.out.print(A[i] + " ");
		}
	}

}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
分治法是一种常用的算法设计方法,它将一个大问题分解成若干个相同或类似的子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。归并排序就是一种使用分治法的排序算法归并排序的思想如下: 1. 将待排序的数组不断地二分,直到每个子数组只有一个元素。 2. 将相邻的两个子数组进行合并,合并时按照从小到大的顺序将元素放入一个临时数组中。 3. 重复步骤2,直到所有的子数组都合并成一个有序的数组。 以下是C++实现归并排序算法的代码示例: ```cpp // 合并两个有序数组 void merge(vector<int>& nums, int left, int mid, int right) { vector<int> temp(right - left + 1); // 临时数组用于存放合并结果 int i = left; // 左子数组的起始位置 int j = mid + 1; // 右子数组的起始位置 int k = 0; // 临时数组的索引 // 将两个子数组中较小的元素依次放入临时数组中 while (i <= mid && j <= right) { if (nums[i] <= nums[j]) { temp[k++] = nums[i++]; } else { temp[k++] = nums[j++]; } } // 将剩余的元素放入临时数组中 while (i <= mid) { temp[k++] = nums[i++]; } while (j <= right) { temp[k++] = nums[j++]; } // 将临时数组中的元素复制回原数组 for (int m = 0; m < k; m++) { nums[left + m] = temp[m]; } } // 归并排序 void mergeSort(vector<int>& nums, int left, int right) { if (left >= right) { return; } int mid = left + (right - left) / 2; mergeSort(nums, left, mid); // 对左子数组进行排序 mergeSort(nums, mid + 1, right); // 对右子数组进行排序 merge(nums, left, mid, right); // 合并两个有序子数组 } // 调用归并排序函数 vector<int> nums = {5, 2, 9, 1, 7}; mergeSort(nums, 0, nums.size() - 1); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值