求数组中为一个不同的数字
先看看这个题的内容:
有给定一个数组有一些数字。只有一个数字和其他的数字不一样,需要把它找出来
数组至少包含3个元素。
例子:
findUniq([ 1, 1, 1, 2, 1, 1 ]) === 2
findUniq([ 0, 0, 0.55, 0, 0 ]) === 0.55
分析:
先看看有哪些条件:
1. 一个数组
2. 每一个元素的数据类型是 number
3. 只有一个唯一的一个元素与其他的元素不一样
4. 本题的关键就是如何表示数组唯一元素的唯一性
解答
当时写了两个解决方案:
第一种方法:求最大值和最小值
不管这个数组中有多少个元素,其实只有两种数字:唯一的一个元素与其他相同的元素
let arr = [1, 1, 1, 3, 1, 1];
arr 只有两种数字,1 和 3。
在深入一步:可以得到一个数组中的最大值和最小值,并且那个唯一的数字就隐藏在这两者之中,
如何得到它呢,就要使用一个数组的查找元素的方法,indexOf 和 lastIndexOf。我们将会使用到这两个方法的共同作用之一:返回查到到的第一个元素的索引。
good!答案就有;上代码:
function findUniq(arr) {
let max = Math.max(...arr);
let min = Math.min(...arr);
return arr.indexOf(max) ===
arr.lastIndexOf(max)? max: min;
}
- 首先是获取数组中的最大值和最小值
- 拿到最大值在数组的索引,并且是从左右两方向获取,之后判断这两个索引是否相等
- 前面已经分析了在最大和最小值之中会有一个唯一的数组,所以只要获取到的索引相同则就是该最大值,否则就是最小值;
这个方法比较简单,但第一种方法并不是我一开始想到的,一开始我想到的方法是这个:
第二种方法:
function findUniq(arr) {
let max = Math.max(...arr);
let min = Math.min(...arr);
return filterUnip(arr, max).length <= 1? max: min;
}
function filterUnip(arr,nums) {
return arr.filter((item) => {
return nums === item;
});
};
还是求最大值和最小值,再利用唯一的数字的数量只用一个的特性,并找出该数字。
只不过处理的手法不一样,我使用来数组中的 filter() 方法求得每一种数字组成的数组,用长度来验证元素的唯一性
但是事件还没有完,上面的两种方法其实只能处理数组长度比较小的,一旦数组的长度超过一定的长度,代码的执行效率就很低。大家可以去把代码去原题测试一下,测试可以过,正真的数据过来就不行,因为有部分数组的元素的基数特别大,以及数组的长度是指数级别。
从性能的角度出发:函数的调用性能比一般的代码要弱。
并且遍历的次数过多也会降低代码的执行效率
Math.max , Math.min, indexOf() , lastIndexOf() ,filter() 其实就是在遍历数组中的每一个元素并且进行对比判断。前面的两种方法 其实都遍历 3 次数组。若一个数组是指数级别的,测试的时间自然很长。所以如何减少遍历数组的次数成为本题的关键
第三种方法:
把数组转化为字典
为什么要选择字典呢?
字典:就是键值对形式的数据结构。这种数据结构有一个很大的特点:键是唯一的,一个字典中的键是不会出现相同的,只会后者的值覆盖前者的值。所以为了得到唯一的数字,最多遍历一遍的情况下是可以做到。 只需要把每一种数字的变成键,用值来计算对应键的数量就可以得到想要的结果。
第三种方法:
function findUniq(arr){
let obj={};
for(let i = 0; i < arr.length; i++){
if(!obj[arr[i]]){
obj[arr[i]] = 1;
//当数组中唯一一个不一样的数字出现在数组的最后一位的时候
if(i === (arr.length - 1)){
return arr[i];
}
}else{
obj[arr[i]] += 1;
//出现多次的数字,次数大于2的时候
if(obj[arr[i]] >= 2){
for(let key in obj){
if(obj[key] === 1){
return key;
}
}
}
}
}
}
代码有点长,不过结构还是比较清晰,假设唯一的值为 a ,其相同的值为 b.
首先函数内所有的代码都是在一个 for 循环之内,我们的目的是只在遍历数组一次的情况下找出唯一的数值。
然后在 for 循环之内 只包含了一个 if 语句, 把数组的元素转化为对象中的键,对象其实就是一种字典,所以每一个键在一个对象中都是唯一存在,由此可知该对象中只有两个键,a 和 b ,这个 if 做的事情就是把数组中的元素抽离出来形成对象中唯一的键,然后利用值来计数。
- 当该键不再对象中存在的时候,会新键一个键,并且值初始化为 1。
当该键存在的时候,其实是已经执行了上一步,所以就是累加,还是利用了题目给的一个条件:唯一的元素。他的计数只用一种情况: 1。 这个方法的优势就在于,可能只用找出第 3 个元素就可以得到想要的结果
- 当 b 的值大于等于 2 的时候就要枚举对象中的键,并判断每一个键对应的值的计数是否为 1
还有特殊的情况存在的,当唯一的值的位置出现在数组最后一个的时候,就要单独判断,就在给键初始化的时候判断是不是在数组的最后一位。
其他的解法:
这一道题的解法其实有很多:
比如:异或的方法(这个方法我也试过,没有成功)