🥳前端算法面试之统计逆序对-每日一练

今天分享的内容是统计数字序列的逆序对数量

什么是逆序对

在数组排序中,逆序对是指数组中的两个元素,如果前一个元素大于后一个元素,则这两个元素构成一个逆序对。例如,在数组 [1, 3, 2, 3, 1] 中,(3, 2)(3, 1) 都是逆序对。

在数组排序中,逆序对的数量也是一个重要的指标,因为它可以帮助我们衡量排序算法的性能。因此,在设计排序算法时,了解和优化逆序对的数量是一个非常重要的问题。

今天分享的内容就是用 JS 代码去统计一个数组的逆序对

暴力算法

一个很简单的统计方法就是暴力遍历,分别统计每个数字组成的"数字对"是否为逆序对。

给数组套两岑循环就可以实现了,下面是代码实现:

javascript 复制代码
/**
 *
 * @param {number[]} data
 */
const bruteForce = (data) => {
	let number = 0;
	for (let i = 0; i < data.length; i++) {
		for (let j = i + 1; j < data.length; j++) {
			if (data[i] > data[j]) {
				number++;
			}
		}
	}
	console.log(number);
};

先从第一个元素开始,往后对比每个元素,如果符合逆序对的要求,number 就加一。统计完了第一个元素,就接着统计第二个元素,直到统计完所有的元素,算法就结束了。

很简单,来看看执行结果:

javascript 复制代码
const data1 = [4,3,6,4,1];
bruteForce(data1); // 6

const data2 = [32, 23, 12, 3, 1, 5, 56, 8, 6, 56, 87];
bruteForce(data2); // 22

测试通过

分治算法

什么是分治算法?分治算法是一种将问题分解成更小的子问题,然后递归地解决这些子问题的算法。分治算法的核心思想是将问题分解成多个更小的子问题,然后通过合并这些子问题的解来解决原始问题。

回到这个问题,我们也可以用分支算法来解决。

解决的过程需要用到归并排序的思想来解决,将数组分成左右两个部分,即先统计左右两个部分逆序对的数量,然后统计两个部分之间的逆序对。

过程很清晰,重点在于,如何统计两个部分之间的逆序对?

还是归并排序的细想,对左右两个部分进行合并排序,合并排序的过程就能知道有多少的逆序对了。

统计两个部分之间的逆序对是一个合并两个有序序列的过程,主要注意,在合并之前,两个部分就已经是有序序列了

javascript 复制代码
let number = 0;
const _countNumberReverse = (data, left, right) => {
	if (left >= right) return 0;
	const mid = Math.floor((left + right) / 2);
	_countNumberReverse(data, left, mid);
	_countNumberReverse(data, mid + 1, right);
	merge(data, left, mid, right);
};

const merge = (data, left, mid, right) => {
	const temp = [];
	let i = left,
		j = mid + 1,
		index = 0;
	while (i <= mid && j <= right) {
		if (data[i] <= data[j]) {
			temp[index++] = data[i++];
		} else {
			number += mid - i + 1;
			temp[index++] = data[j++];
		}
	}
	while (i <= mid) temp[index++] = data[i++];
	while (j <= right) temp[index++] = data[j++];
	data.splice(left, temp.length, ...temp);
};

_countNumberReverse 是很简单的分而治之的递归算法,先统计左半部分,然后统计后半部分,最后用 merge 统计两个部分之间的逆序对

重点来看看 merge 函数,merge 函数的过程是合并排序的过程。因为左边部分经历了左边的左右子部分合并排序,所以左边部分就是有序的,同理右边部分也是有序的。所以合并排序没有问题,逻辑是对的。

在合并的过程,会对左右两部分的元素进行比较,小的先放进 temp 里面。一旦遇到了左边的元素大于右边元素的情况,就说明有逆序对了,那就可以对 number 进行修改了,是加 1 吗?加 1 正确吗?

不对的

举个例子,现在有部分元素,左边元素是 lData,右边元素是 rData

javascript 复制代码
const lData = [1,3,6]
const rData = [2,2,5]

先比较 lData[0]rData[0], rData[0] 更小,所以放进 temp

然后继续比较rData[1]lData[0]lData[0] 更小,发现逆序对。那么,你发现几对了呢?有两对!

因为lData[1] 大于rData[0],那就说明 lData[1] 后面的元素全部都大于rData[0]lData[1] 后面有一个元素,所以一共是两对逆序对


回到代码。在遇到左边元素大于右边元素的时候,对 number 加了 mid - i + 1``mid - i + 1就是左边剩下元素的个数

好了,关键点讲完了,下面执行代码测试一下,并且同暴力算法的执行结果进行对比,检验代码的正确性:

javascript 复制代码
const data = [32, 23, 12, 3, 1, 5, 56, 8, 6, 56, 87];
const countNumberReverse = (data) => {
	_countNumberReverse(data, 0, data.length - 1);
};

countNumberReverse(data);
console.log(number); //22

没有问题,搞定✌️

总结

今天分享的是如何用分治算法的思想来统计数字序列中有多少个逆序对。

首先介绍了什么是逆序对,然后介绍了两种常用的统计逆序对的方法:暴力算法和分治算法。在介绍分治算法时,还介绍了如何实现合并排序,以及如何统计两个部分之间的逆序对数量。合并排序是一种常见的排序算法,通过将数组中的元素两两合并排序,最终得到一个有序序列。而统计两个部分之间的逆序对数量,则是通过合并两个有序序列的过程来实现的。

什么问题可以评论区留言哦。我每天都会分享一篇算法小练习,喜欢就点赞+关注吧

相关推荐
浊酒南街23 分钟前
XGBClassifiler函数介绍
算法·机器学习·xgb
中工钱袋26 分钟前
Vue 中地址栏参数与 HTTP 请求参数的同步问题
前端·vue.js·http
mlxg9999930 分钟前
hom_mat2d_to_affine_par 的c#实现
算法·计算机视觉·c#
zzlyx9930 分钟前
设备管理系统功能与.NET+VUE(IVIEW)技术实现
前端·vue.js·view design
咩咩觉主30 分钟前
C# &Unity 唐老狮 No.6 模拟面试题
开发语言·unity·面试·c#·游戏引擎·唐老师
秋月华星2 小时前
【flutter】TextField输入框工具栏文本为英文解决(不用安装插件版本
前端·javascript·flutter
千里码aicood3 小时前
[含文档+PPT+源码等]精品基于Python实现的校园小助手小程序的设计与实现
开发语言·前端·python
青红光硫化黑3 小时前
React基础之React.memo
前端·javascript·react.js
大麦大麦3 小时前
深入剖析 Sass:从基础到进阶的 CSS 预处理器应用指南
开发语言·前端·css·面试·rust·uni-app·sass
真就死难4 小时前
完全日期(日期枚举问题)--- 数学性质题型
算法·日期枚举