由一道题说开去 Find the odd int

日常在做codewars的题目,有这么一道题...

# Find the odd int

题目是这样的

Given an array of integers, find the one that appears an odd number of times. There will always be only one integer that appears an odd number of times.

给定一个整数数组,找到出现奇数次的整数。提示:始终只有一个整数出现奇数次。

题目不麻烦,我们循环一次,记下每个数字出现的次数;因为始终只有一个整数出现奇数次,最终再遍历find一次即可。

export const findOdd = (xs: number[]): number => {
    let map: { [key: string]: number } = {}
    xs.forEach(l => {
        if (map[l] !== undefined) {
            map[l] += 1
        } else {
            map[l] = 1
        }
    })
    return Number(Object.keys(map).find(k=>!!map[k]&&(map[k] % 2===1||map[k] % 2===-1)))
}

三下五除二,测试-提交-通过。OK,看看评论有什么,于是看到了这么一行神仙代码,一时竟没反应过来

export const findOdd = (xs: number[]): number => {
  return xs.reduce( (a,b)=> a^b);
};

就是如此简洁,短短一行。有两个关键,一个函数一个运算符,我们来分析分析。

# reduce

reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

求和累加

const sum = (arr)=>arr.reduce((accumulator, currentValue) => accumulator + currentValue)

sum([1,2,3,4]) // 10

累计乘积

const multip = (arr)=>arr.reduce((accumulator, currentValue) => accumulator * currentValue)

multip([1,2,3,4]) // 24

转化整数

[1,2,4,6,8,9].reduce((accumulator, currentValue) => accumulator *10 + currentValue)
// 124689

转对象

const obj = [{name: '技术', id: 1}, {name: '设计', id: 2}].reduce((accumulator, cur) => {
    accumulator[cur.id] = cur;
    return accumulator;
}, {});
console.log(obj) // {"1":{"name":"技术","id":1},"2":{"name":"设计","id":2}}

reduce还有很多神奇用法,这里我们就不一一举例了。

# 异或运算

a ^ b 在a,b的位表示中,每一个对应的位,两个不相同则返回1,相同则返回0. 异或是一种基于二进制的位运算,用符号XOR或者 ^ 表示,其运算法则是对运算符两侧数的每一个二进制位,同值取0,异值取1。它与布尔运算的区别在于,当运算符两侧均为1时,布尔运算的结果为1,异或运算的结果为0。

他有四条性质

  1. 交换律
  2. 结合律(即(a^b)^c == a^(b^c))
  3. 对于任何数x,都有x^x=0,x^0=x
  4. 自反性 A XOR B XOR B = A xor 0 = A

# 回到题目

根据第三条性质,现在就很好理解了,相同两个数字异或等于0,0异或任何数等于原数,那么出现偶数次数字两两抵消掉,再配合reduce累积异或(第二条性质),剩下的就只有目标值了。不得不说非常巧妙了。

// 题目中 
doTest([1,1,2,-2,5,2,4,4,-1,-2,5], -1);
// 就相当于 
1^1^2^-2^5^2^4^4^-1^-2^5 = -1

codewars题目-Find the odd int (opens new window)

# 最后

需要说明的是,这个写法非常简洁,但并不优雅,因为牺牲了可读性,抽象化的太高反而影响了理解。当然,做题为目的的话,帮助我们熟悉语言和计算世界的精妙也无可厚非。

最近更新
01
echarts扇形模拟镜头焦距与可视化角度示意图
03-10
02
vite插件钩子
03-02
03
vite的依赖预构建
02-13
更多文章>