算法导论第2章(4) 分治法的应用 找逆序对 (习题2-4)

本文介绍了一种利用归并排序算法高效计算数组中逆序对数量的方法。通过分而治之策略,将数组分成两部分分别计算逆序对,并在合并过程中继续查找逆序对。该算法不仅实现了数组的排序,还准确地统计了逆序对的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二章习题2-4

逆序对的概念:数组s[0...N]中如果i<j ,s[i]>j就表示有一个逆序对

问题:对任意一个数组求其逆序对。

可以使用分治法:对于一个数组S将其分为2个部分S1和S2,求S1和S2的逆序对个数,

再求S1和S2合并后逆序对的个数:这个过程与merge排序的过程是一样的,可以使用merge排序来求得:

例如下面是java实现:

package com.junjun.algorithm.charpter2;

import com.junjun.algorithm.Sort;

/**
 * 第二章 习题2-4
 * 数组s[0...N]中如果i<j ,s[i]>j就表示有一个逆序对
 * 计算任意数组中的逆序对数
 * @author junjun
 *
 */
public class InversePair extends Sort
{
	//算逆序对的个数
	private static int count = 0;
	
	/**
	 * 使用归并排序求逆序对
	 * @param s
	 */
	public int countInversePair(int [] s)
	{
		count=0;
		this.mergeSort(s,0, s.length-1);
		return count;
	}

	/**
	 * 对数组 s进行归并排序,p<<q<r
	 * 其中p到s[p,q] 和s[q+1,r]都是排好序的,将他们合并成一个排好序的数组并替换s[p,r],在这个过程中找到逆序对的个数
	 * @param s 数组
	 * @param p 
	 * @param q
	 * @param r
	 */
	private void merge(int [] s,int p,int q,int r)
	{
		int n1 = q-p+1; //左边一段s[p,q]的长度
		int n2 = r-q;   //右边一段s[q+1,r]的长度
		int [] L = new int [n1]; 
		int [] R = new int [n2];
		
		for(int i = 0; i < n1;i++)
			L[i] = s[p+i];
		for(int i = 0; i < n2; i++)
			R[i] = s[q+1+i];
	
		//打印当前左边与右边的数组 L和R
		System.out.println("\n----------");
		System.out.print("L:");
		this.print(L);
		System.out.print("\nR:");
		this.print(R);
		System.out.println("");
		
		// 开始归并
		int i =0; 
		int j = 0;
		int k = p;
		while(i < n1 && j < n2)
		{
			// 选择较小的作为插入对象
			if(L[i] <= R[j])
			{
				s[k++] =  L[i++];
			}
			else //如果L[i]>R[j]可能存在逆序对
			{
				/*右边每插入一个数就有n1-i个逆序对存在,因为是右边剩下的每一个元素L[i...n1]都比R[j]更小。
				* 例如L=2,3 R=0,2,7
				* 第一轮:插入R[0]=0, 是L=[2,3] 有2个逆序对 (2,0),(3,0)
				* 第二轮:插入L[0]=2,  不做处理 ,0个逆序对
				* 第三轮:插入R[1]=2,  L=[3],有1个逆序对 (3,2)
				* 第四轮:插入L[1]=3,  不做处理, 0个逆序对
				* 第五轮:插入R[2]=7,  L=[] 0个逆序对 
				* 所以有3个逆序对 
				*/
				count+= (n1-i);		
				//打印逆序对
				for(int l = i;l <n1;l++)
					System.out.print("("+L[l]+","+R[j]+")  ");
				
				s[k++] =  R[j++];
				
			}
		}
		
		//将剩下的插入
		while(i<n1) s[k++] = L[i++];			
		while(j<n2) {s[k++] = R[j++];}	
	}
	
	/**
	 * 递归进行merge排序
	 * @param s
	 * @param p
	 * @param r
	 */
	private void mergeSort(int [] s, int p ,int r)
	{
		if(p>=r) return;
		
		int q = (p+r)/2;
		mergeSort(s,p,q);
		mergeSort(s,q+1,r);
		merge(s,p,q,r);		
	}
	
	
	public static void main(String[] args)
	{
		// 逆序对有5个 (2,1),(3,1),(8,1),(6,1),(8,6)
		int [] s = {2,3,8,6,1};
		
		InversePair ip = new InversePair();
		System.out.println("\n逆序对个数:"+ip.countInversePair(s));;
		System.out.println("排序过后的数组:");
		ip.print(s);
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值