[技巧] 逆序对问题 的 分治解法

分治核心思想:

  • 分解(Divide):将原问题分解成一系列子问题。这些子问题应该是原问题的较小版本。
  • 解决(Conquer):递归地解决这些子问题。如果子问题的规模足够小,则直接求解。
  • 合并(Combine):将子问题的解合并起来,形成原问题的解。
    点击查看图片来源

逆序对问题中:

  • 分解:只要数组长度超过1,就不断均分数组
  • 解决:当数组长度为1或0时,数组有序 且 没有逆序对 ✅
  • 合并:当前数组分成左右两部分,总逆序对数量 = i,j都在左边的逆序对数量 + i,j都在右边的逆序对数量 + i在左边,j在右边的逆序对数量

故解法如下:

func mSort(arr []int) ([]int, int) { // 分治思想
	if len(arr) <= 1 { // 只要长度超过1就继续分
		return arr, 0
	}
	mid := len(arr) / 2
	left, right := arr[:mid], arr[mid:]
	arr1, cnt1 := mSort(left)  // i,j都在左边的逆序对数量
	arr2, cnt2 := mSort(right) // i,j都在右边的逆序对数量

	newArr, cnt3 := merge(arr1, arr2) // i在左边,j在右边的逆序对数量
	return newArr, cnt1 + cnt2 + cnt3
}

func merge(left, right []int) (newArr []int, count int) {
	m, n := len(left), len(right)
	newArr = make([]int, m+n)
	var i, j int
	for i < m && j < n {
		if left[i] <= right[j] { // right[j] 没有 对应的 逆序对
			newArr[i+j] = left[i]
			i++
		} else {
			count += m - i // right[j]与每一个left[i] 都形成一个 逆序对
			newArr[i+j] = right[j]
			j++
		}
	}
	for i < m { // 剩余元素加到末尾
		newArr[i+j] = left[i]
		i++
	}
	for j < n {
		newArr[i+j] = right[j]
		j++
	}
	return newArr, count
}

func reversePairs(record []int) int {
	_, cnt := mSort(record)
	return cnt
}

更多分治问题:

1> 构造类型,奇偶分治,位分治

题目说明实现
932. 漂亮数组题意即两数之和不能为偶数,故数组分为偶数部分和奇数部分,因为 偶数 + 奇数 != 偶数我的提交
1238. 循环码排列先不考虑start,按最高位为0、1将数据分为左右部分构造,后面再循环移动至start到0位置处我的提交

2> 分治构造

题目说明实现
889. 根据前序和后序遍历构造二叉树通过前序找到根节点,从而划分后序遍历为左右子树我的提交
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值