前端面试题(两个升序数组合并为一个升序数组)
给定两个数组a和b,值为别为[1,3,5,7]和[2,4,6,8],怎么才能将它们变成c,值为[1,2,3,4,5,6,7,8]?
前提不能把a和b合并之后再排序,并且采用最优算法让循环执行的次数最少。
这道题是近来比较火的算法题之一,我在58以及区块链的一家公司都遇到过,而且不仅前端,也可以作为py,java面试题。
在算法当中,优化就是尽量避免无效的计算。
举个例子,我们经常使用循环来创建一系列的元素,并添加到页面中,这样的案例数不胜数。
// 第一种方式
function createGoodsList(num) {
var i = 0, array = [];
for( ; i<num; i++) {
array.push('<li>' + (i+1) + '</li>')
}
return array
}
document.getElementById('goods_wraper').innerHTML = createGoodsList(10).join('')
// 第二种方式
function createGoodsList(num){
var i = 0, str = '';
for( ; i<num; i++){
str += '<li>' + (i+1) + '</li>'
}
return str
}
document.getElementById('goods_wraper').innerHTML = createGoodsList(10)
// 第三种方式
function createGoodsList(parent,num){
var i = 0
for( ; i<num; i++){
var liElement = document.createElement('li')
liElement.innerText = i + 1
parent.appendChild(liElement)
}
}
createGoodsList(document.getElementById('goods_wraper'),10)
上面的方法,第一种优于第二种,第二种优于第三种。第二种减少了频繁的DOM操作,第一种减少了中间变量的产生,避免了一些无效的计算。
这是因为早期浏览器中没有对于 ‘+’ 运算符的优化,由于String类型是不可变的,所以要创建中间值来存储 ‘+’ 连接的结果,频繁地创建和销毁字符串导制程序运行性能异常的差。
回到我们的题目中。
我们看a和b,都是已经给定的升序数组,既然不能合并a和b,要达到最优的算法过程,必然要利用给定的升序规则,
即针对a和b进行循环比对相同索引的值的大小,将值小者放到c中,然后值大者与值小者下一位进行比对,
假设a与b的长度是不等的,我们要设置两个下标来对应a和b各自的索引,
并且在比对以后,要针对长度较大的数组额外写一个循环,将没有参加比对的值放到c中
看代码吧
var a = [1,3,5,7,9]
var b = [2,4,6,8]
var i = j = 0, c = [], a_length = a.length, b_length = b.length
while(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哪个数组长度长一点,才会多写一个循环,那么我们有没有办法把三个循环的写法(真正执行的只有两个)简化一下呢?
当然是可以的,我们只需要判断一下在读取数组中的值得时候,这个值是否存在
var a = [1,3,5,7,9]
var b = [2,4,6,8]
var i = j = 0, c = [], a_length = a.length, b_length = b.length
while(i<a_length || j<b_length){
if(b[j] > a[i] && a[i] || !b[j]){
c.push(a[i])
i++
}else if(b[j] <= a[i] && b[j] || !a[i]){
c.push(b[j])
j++
}
}