首页>>前端>>JavaScript->JavaScript将两个升序数组合并为一个升序数组(经典面试题)

JavaScript将两个升序数组合并为一个升序数组(经典面试题)

时间:2023-11-29 本站 点击:0

现在有这样一道题目:

给定两个数组 a 和 b ,它们的值为别为[1,3,5,7][2,4,6,8],请你用最简单的方法将 a 与 b 合并为一个数组,合并后得到的数组的值应为[1,2,3,4,5,6,7,8]

这道题是在这几年的面试中比较容易出现的算法题之一,我在58同城以及区块链的一家公司的面试过程中都遇到过,而且不仅仅局限于 javascript,也可以作为 python ,java 等的面试题。

可能有的同学想,这有什么难的,不就是考察数组的合并和排序嘛~

于是落笔成文:

varc=a.concat(b).sort((a,b)=>a-b)

写完美滋滋,你看我,一行代码能搞定的,绝对不写成两行

然后面试官微微一笑,让你回去等通知

为什么呢?因为你没仔细审题


题目中说“请你用最简单的方法”,最简单,并不等于写的代码最少。

所以这里其实有个隐藏的前提,那就是不能把 a 和 b 合并之后再排序,这里最简单的解法应该是让循环执行的次数最少。

因为在算法当中,优化意味着要尽量避免无效的计算和多次的计算。

举个例子,我们经常使用循环来创建一系列的元素,并追加到页面中。

第一种方式,利用数组存储创建的元素,最后利用数组方法转成字符串进行追加

functioncreateGoodsList(num){vari=0,array=[];for(;i<num;i++){array.push('<li>'+(i+1)+'</li>')}returnarray}document.getElementById('goods_wraper').innerHTML=createGoodsList(10).join('')

第二种方法,利用字符串拼接的方法来存储创建的元素,然后追加到页面里

functioncreateGoodsList(num){vari=0,str='';for(;i<num;i++){str+='<li>'+(i+1)+'</li>'}returnstr}document.getElementById('goods_wraper').innerHTML=createGoodsList(10)

第三种方式,直接在循环里追加,每循环一次,则往页面中追加一次

functioncreateGoodsList(parent,num){vari=0for(;i<num;i++){varliElement=document.createElement('li')liElement.innerText=i+1parent.appendChild(liElement)}}createGoodsList(document.getElementById('goods_wraper'),10)

在上面的三种方法

第一种优于第二种,第二种优于第三种。

相比于第三种循环追加的方法,第二种减少了频繁的 DOM 操作,而第一种减少了中间变量的产生,避免了一些多余的运算。

这是因为早期浏览器中没有对于 '+' 运算符的优化,由于 String 类型是不可变的,所以要通过创建中间值来存储 '+' 连接的结果,频繁地创建和销毁字符串导制浏览器在运行程序时性能异常的差。

咳咳,跑题了


回到我们的题目中。

即然 a 和 b ,都是已经给定的升序数组,那最简单的方式,肯定要利用上 a 和 b 自身的升序排列,来减少在排序阶段将要进行的比较。

所以,我们只需要将 a 与 b 中的元素两两比较,而 a 中的元素需要互相比较大小吗?b 中的元素需要互相比较大小吗?

不需要。

于是就有了这样的思路:

首先,针对 a 和 b 中具有相同索引的值进行大小比对

然后将其中较小者放进 c 中

较大者则进入下一次比较过程

当某个给定数组中的值比较完毕,只需要把另一个数组的值全部放入 c 中

拿到正确的结果

第一次实现:

虽然题目中给定的 a 和 b 的长度是相等的,但我们这里假设 a 与 b 的长度不相等,我们来设置两个下标来对应 a 和 b 各自的索引

vara=[1,3,5,7,9]varb=[2,4,6,8]vari=j=0,c=[],a_length=a.length,b_length=b.lengthwhile(i<a_length&&j<b_length){if(b[j]>a[i]){c.push(a[i])i++}else{c.push(b[j])j++}}while(i<a_length){c.push(a[i])i++}while(j<b_length){c.push(b[j])j++}console.log(c)

现在已经完成了题目,但是仔细考虑一下上面的思路,因为我们并不知道 a 和 b 哪个数组长度长一点,才会多写一个循环(真正执行的只有两个),那么我们有没有办法把三个循环的写法简化一下呢?

其实这里我们只需要判断一下在读取数组中的值得时候,这个值是否存在

优化:

vara=[1,3,5,7,9]varb=[2,4,6,8]vari=j=0,c=[],a_length=a.length,b_length=b.lengthwhile(i<a_length||j<b_length){if((b[j]>a[i]&&a[i])||!b[j]){c.push(a[i])i++}elseif((b[j]<=a[i]&&b[j])||!a[i]){c.push(b[j])j++}}console.log(c)

到这里,这道题就完成了。


bug 引发的思考:

经过一次偶尔的测试,我发现上面的代码其实有个不起眼的 bug,这个 bug 会导致我们的代码功亏一篑

比如:

vara=[0,2,3,5,9,10,12]varb=[7,11,13]...console.log(c)//[7,11,13,0,2,3,5,9,10,12]

这是因为我们在代码里判断了 a[i] 为真,而当a[i]值为 0 的时候,判断是不能成立的,所以出现了 bug。

既然这样,我们直接使用另一种思路来替代之前的解题思路,

首先,我们的循环条件要修改一下,只按照一个数组进行循环,这样就保证循环中每一次的a[i]都存在,这是什么意思呢?

假如我有如下的代码:

vara=[0,1,2,3,4,5],i=0while(i<a.legnth){i++}

这里的判断条件能保证a[i]肯定是存在的。

那我们说一下整体的思路:

首先,通过变更循环的判断条件将代码中针对值是否存在的判断去掉

其次,我们先将一个数组放进 c 中 var c = [...a]

接下来按照数组 b 进行循环

为了优化逻辑,我们就倒序循环,以省略多余的变量ij

在循转中,我们直接使用赋值的方式填充数组,不再使用push方法

varc=[...a]varb_length=b.length-1vara_length=a.length-1varc_length=b_length+a_length+1while(b_length>=0){if(a_length<0){c[c_length--]=b[b_length--]continue}c[c_length--]=a[a_length]>=b[b_length]?a[a_length--]:b[b_length--]}console.log(c)//[0,2,3,5,7,9,10,11,12,13]

作者:晴天同学


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/JavaScript/682.html