🥳每日一练-删除数组中重复的元素-JS简易版

这篇文章分享一个新的数据结构--散列表,并用散列表解决一个算法问题:删除数组中重复的数字

对于这个问题,如果数组是有序的,那算法的复杂度可以降到 O(n); 如果是无序的,一不小心就会变成 O(nlogn)的复杂度

在 JS 中,无论数组是否有序,都可以使用语言本身支持的 数据结构 API,Set 或者 Map 来解决,像下面这样:

javascript 复制代码
const deleteDupBySet = (array)=>{
  return [...new Set(array)];
}

如果不借助这些 API,你会怎么解决这个问题呢?

有序的数组

在遍历数组的过程中,如果发现相同的元素,就用后面的元素覆盖之前的重复元素。即如果发现了一个,后面的元素就往前移动一个;如果发现了两个,后面的元素都往前移动两格;

javascript 复制代码
/**
 *
 * @param {number[]} array
 */
const deleteDuplicSorted = (array) => {
	let k = 0;
	for (let i = 1; i < array.length; ) {
		if (array[i - k - 1] == array[i]) {
			k++;
		} else {
			array[i - k] = array[i];
			i++;
		}
	}
	array.length = array.length - k;
	return array;
};

上述代码定义了一函数,接收数组作为参数。这个函数会将 array 中的重复元素都删除,最后返回 array;

函数中用 K 记录重复元素的个数。当遇到相同的元素是,k++,然后继续比较下一个元素;当遇到不同的元素的时候,就可以讲这个元素往前挪了,挪动的幅度就是 k。遍历结束之后,需要将 array 的长度减 k。

测试代码

javascript 复制代码
const data = [1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 9, 9];
console.log(deleteDuplicSorted(data));
// [
//   1, 2, 3, 4, 5,
//   6, 7, 8, 9
// ]

测试成功

无序的数组

如果数组是无序的,用上面的方法就行不通了。这其中的关键是,确认元素是否重复。需要拿到一个元素都要遍历整个数组才能确认它是否重复吗?

如果不这么做,可不可以先给无序的数组排个序呢?然后再遍历删除重复的元素。这样做可以是可以,但会增加复杂度,排序最短的复杂度是 O(nlogn), 然后还需要遍历数组,整个复杂度就是 O(n + nlogn)

有没有更高效的办法呢,有的。我们可以借助散列表来降低确认元素的复杂度。

javascript 复制代码
class HashList {
	constructor(number) {
		this.list = Array(number).fill(null);
		this.len = number;
	}

	isHas(value) {
		const hashValue = value % this.len;
		let linkList = this.list[hashValue];
		while (linkList) {
			if (linkList.value == value) return true;
			linkList = linkList.next;
		}

		this._insert(hashValue, value);
		return false;
	}
	_insert(index, value) {
		const linkList = this.list[index];
		const temp = { value, next: null };
		if (linkList == null) {
			this.list[index] = temp;
		} else {
			// 头插法
			temp.next = linkList.next;
			linkList.next = temp;
		}
	}
}

这里定义了一个散列表的数据结构--HashList

这里使用散列表的定位方式除留余数法,即将元素对一个特定的树取余,来决定释放在散列表的哪个位置;冲突崇礼的方式是拉链法,即如果元素定位到了散列表相同的位置,就会用链表的方法串在一起,不影响散列表的其他位置。像下面这样:

此图来自 B站王道数据结构视频课

HashList 还有两个函数,isHas 和 _insert,isHas 是判断元素是否存在于散列表中,可以借助这个方法来判断元素是否重复。如果监测到已经存在于散列表中,就说明元素重复了。如果元素不在散列表中,就是自动调用_insert 方法,将元素插入到散列表。_insert 是供内部调用的一个方法,直接插入到散列表的指定位置

javascript 复制代码
/**
 *
 * @param {number[]} array
 */
const deleteDupByHash = (array) => {
	const hashList = new HashList(array.length);
	let k = 0;
	for (let i = 0; i < array.length; i++) {
		if (hashList.isHas(array[i])) {
			k++;
		} else {
			array[i - k] = array[i];
		}
	}
	array.length = array.length - k;
	return array;
};

这里定义了一个函数 deleteDupByHash,其中借助了 HashList 实现了删除无序数组中的重复元素。遍历数组的逻辑和之前一样,即如果发现了一个重复元素,后面的元素就往前移动一个;如果发现了两个重复元素,后面的元素都往前移动两格;

测试代码:

javascript 复制代码
const data = [1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 9, 9];
console.log(deleteDupByHash(data));
// [
//   1, 2, 3, 4, 5,
//   6, 7, 8, 9
// ]

测试成功

完整代码

javascript 复制代码
/**
 *
 * @param {number[]} array
 */
const deleteDuplicSorted = (array) => {
	let k = 0;
	for (let i = 1; i < array.length; ) {
		if (array[i - k - 1] == array[i]) {
			k++;
		} else {
			array[i - k] = array[i];
			i++;
		}
	}
	array.length = array.length - k;
	return array;
};
class HashList {
	constructor(number) {
		this.list = Array(number).fill(null);
		this.len = number;
	}

	isHas(value) {
		const hashValue = value % this.len;
		let linkList = this.list[hashValue];
		while (linkList) {
			if (linkList.value == value) return true;
			linkList = linkList.next;
		}

		this._insert(hashValue, value);
		return false;
	}
	_insert(index, value) {
		const linkList = this.list[index];
		const temp = { value, next: null };
		if (linkList == null) {
			this.list[index] = temp;
		} else {
			// 头插法
			temp.next = linkList.next;
			linkList.next = temp;
		}
	}
}
/**
 *
 * @param {number[]} array
 */
const deleteDupByHash = (array) => {
	const hashList = new HashList(array.length);
	let k = 0;
	for (let i = 0; i < array.length; i++) {
		if (hashList.isHas(array[i])) {
			k++;
		} else {
			array[i - k] = array[i];
		}
	}
	array.length = array.length - k;
	return array;
};

//测试
const data = [1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 9, 9];

console.log(deleteDuplicSorted([..data]));

console.log(deleteDupByHash([...data]));

总结

这篇文章分享了如何删除数组中的重复元素,并且提到了有序数组和无序数组的处理方法,都是复杂度最低的方案。代码清晰,例子丰富,讲解详细,是个不可多得的好文章

你觉得这篇文章怎么样?我每天都会分享一篇算法小练习,喜欢就点赞+关注吧

相关推荐
姑苏洛言42 分钟前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手1 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言1 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
hackchen2 小时前
Go与JS无缝协作:Goja引擎实战之错误处理最佳实践
开发语言·javascript·golang
YuTaoShao2 小时前
【LeetCode 热题 100】141. 环形链表——快慢指针
java·算法·leetcode·链表
你的人类朋友3 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手3 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3
小小小新人121233 小时前
C语言 ATM (4)
c语言·开发语言·算法
你的冰西瓜4 小时前
C++排序算法全解析(加强版)
c++·算法·排序算法
我命由我123454 小时前
前端开发问题:SyntaxError: “undefined“ is not valid JSON
开发语言·前端·javascript·vue.js·json·ecmascript·js