腾讯的JS前端面试题

无意中看到这个题目,看完所有解答后,觉得还是不好,所以就写了一下。问题来自:http://www.codefans.net/jscss/code/3460.shtml


题目的意思就是:


有一组数字,从1到n,从中减少了3个数,顺序也被打乱,放在一个n-3的数组里,请找出丢失的数字,最好能有程序,最好算法比较快。假设n=10000。


第一个人是这么写的:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>腾讯的JS前端面试题</title>
<script>
var ary = [1, 5, 7, 6, 4, 8, 10];
var n = ary.length + 3;
var newAry = [];
document.write("假设n=" + n + "<br/>");
ary.sort(function(a, b){
	return a - b;
});
document.write("初始数组:" + ary + "<br/>");
for(var i = 1, j=0; i <= n; i ++,j++){
	var diff = ary[j] - i;
	if(!ary[j]){
	  	newAry.push(i);
	} else if(diff > 0){
		for(var k = 0; k < diff; k ++){
		  newAry.push(i++);	
		}
	}
}
//alert(newAry);
document.writeln("缺少的数:" + newAry);
</script>
</head>
<body>
</body>
</html>




就是排序完成后,线性查找,利用下标差值diff来进行添加。里面嵌套一个for语句是为了连续缺失的现象。但是缺点是所有元素都需要匹配一遍,浪费很多时间。


第二个人是这么写的:



<html>
<head>
<title>腾讯公司JS面试题</title>
</head>
<script type="text/javascript">
		//<!--
		var  n = 10;
		var oldArr = [5,1,6,3,7,8,10];//缺失的源数组997个数;
		var newArr = Array(11);
		var lostArr = [];//要找的数的数组
		for(var i = 0; i < n-3; i++) {
			newArr[oldArr[i]] = 1;
		}
		for(var j = 0; j < newArr.length; j++) {
			if(!newArr[j]) {
				lostArr.push(j);
			}
		}
		lostArr.shift(0);
		alert(lostArr);
		//-->
		</script>
<body>
</body>
</html>
</html>


首先先纠正一下这个人代码中的一句话:
newArr[oldArr[i]] = 1;
其实应该写成
newArr[oldArr[i]] = true;
更让人通俗易懂。
这样我们也就知道他要表达的意思就是将缺失元素数组中的值作为新数组的下标并标记为有这个值,新数组的长度是加上确实元素数组
的长度。所以第二次循环的话,直接全部循环,如果没有被标记上的,自然加入到缺失元素数组lost中。不用多说,你能感觉到两次遍历
让我们忍无可忍,比第一个人还要差。
来看看第三个人怎么写的:



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>腾讯面试题</title>
</head>
<body>
<script>
var delNumber = [1106,9012,789]; //假设
var n = 10000; //总循环条例
var number = []; //数组
for (i=0;i<n;i++) {
	number[i] = (i != delNumber[0] && i != delNumber[1] && i != delNumber[2])?(i):("<h3 style=\"color:red\">"+i+"</h3>");
}
function randomSort(a, b){
	return Math.random() - 0.5; //随机运算
}
document.write("本次的随机数是:" + delNumber + "<br /><br />");
document.write(number.sort(randomSort).join()); //选择在外面处理数组,减缓for的压力。
</script>
</body>
</html>




可以说,最无解的莫过于这个人了。他的思想很简单,也很偷懒,就是整个遍历数组,然后判断每个数组中的元素,看看是否与缺失元素
相等。我想表达的是,首先,如果实现已经知道所缺失的元素,何必又要去查找呢???其次,他已经在第一个for语句结束后查找出来了,
为什么下面还要费劲的写个排序函数?排序完又有什么用?题目貌似没有规定,可能被乱序字眼误导了惯性思维。最可惜的是,排序函数
也写的不对呀!!!Math.random() - 0.5这不是相当于没排序????
看完上述三者的激烈编码后,你可能还会想到用json当做哈希表来存储缺失数组数组,用key
作为数组元素的值,value用标记值。很不错,表面看似乎很好,实则很糟糕。且看下面代码:



var arr = [1,2,3,6,7,8,10,11,12,13,14,15,16,17,18,19,20];
function findDelNumber(arr,max){
var obj = {};
for(var i=0;i<arr.length;i++){
obj[arr[i]+'']=arr[i];
}
var str = '';
for(var i=1;i<=max;i++){
if(!obj[i+''])
str +=i+',';
}
return str;
}
alert(findDelNumber(arr,20));



这上面的key和value都是缺失数组中的值,做好一个哈希表后,就可以循环一边总数组的长度,如果发现哈希表中没有这个元素,则自动添加上。
到这里,我需要指出几点,第一,json这种对象数据存储格式不适合作为算法容器,因为你for...in..的时候,其实它需要去查找你里面
存储的属性,花费的时间可想而知。第二,最后你还是避免不了要遍历整个数组去查询所缺失元素,因为上一步只是做了张标记表而已。所以
还不如第一个人写的一次遍历找到元素。
说来说去这么多废话,通过观察这些人的思想和自己思考了一下,发现,首先整理数组成为有序数组是不可避免的。因为计算机不会比你
聪明,就算让你去找,你也会先排好序,乱序的话,只能整个数组查找一个数就遍历一边数组。然后,我们排好序就可以开发出我们的思维
了,你会观察到数组中的值和下标要么就相等,要么就一大段的都差一个数。其实我们可以恢复一下完整的数组元素,你会发现,所有数组值和
下标是相差一的。当你从数组中抽一个数出来的话,把后面的数一次向前进位时,就会发现,后面所有的数值与下标差二了,而前面的却是
差一的。说到这里就差不多都明白了。我们应当在排好序的缺失数组中,去检查它的差值,如果每个都去检查,有点太笨了,聪明的人就知道
复杂问题一分为二,难度就减半,这样我们检查数组中间的数是否和下标差一,如果差一,证明这个数之前的所有数都没有缺失,也就是
这个数组前部分都没有缺失,转而就可以去查找后部分的数组了。如此一来我们便可以进行一次的查找取得一个缺失值。
但是其中有几个陷阱需要指明:
第一,边界问题需要注意,如果缺失的是最前面几个数,或者缺失的存在数组最末尾,那么无论你怎么查找都查不到缺失的数。
第二,当你判断中间数差值不为一的时候,记得还需要看前面一个是否是没有缺失的,也就是前面一个差值必须为一,因为我们这样查找,
第一次拿到的便是最前面缺失的数。
第三,需要注意当你判断中间的数的时候,如果不是所缺失的,记的下次查找的时候不要以前一次中间的下标最为边界,而应该向前或者向
后移一位,因为你先前已经判断出中间数是没有问题的。不然你会发现你的查找可能陷入死循环,就是最后一个数永远查找不到,因为中间数
比较永远不会移动到最末位,只可能是倒数第二位。
注意完这些,相信你会写出很好的解决方案了,下面给出代码,因为10000太长,不方面依次乱序输入,索性用10个数,缺失其中三个数。
查找一次最坏次数为14,查找三个是14*3=42.如果数组越大,性能越能得到体现。数组小,效率也不比线性查找慢。
代码如下:



<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <title> New Document </title>
  <meta charset="utf-8" />
  <meta name="Generator" content="EditPlus">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
 </head>

 <body>
  <script type="text/javascript">

	//缺少2,3,9的乱序数组
	var arr = [1, 5, 7, 6, 4, 8, 10];

	//排序
	arr.sort(function(a,b){
		return a-b;
	});
	
	//折半查找定义
	var serch = function (low,high){
		var pos = parseInt((low + high)/2);	
		//需要判断前一位是否匹配,这个很重要。
		if(arr[pos]!=(pos+1)){
			if (arr[pos-1]==pos){
				lost.push(pos+1);
				//记得添加进去
				arr.splice(pos,0,[pos+1]);
				//打断函数
				return;
			}
			serch(low,pos-1);
		}else{
			serch(pos+1,high);
		}
	}

	var low = 0;//初始化低位查找位置。
	var high = arr.length - 1;	//初始化高位查找位置。
	
	var lost = [];//所缺元素数组
	var len = arr.length + 3;//所有包含所缺元素总和。
	
	//排除高位缺失现象。
	while (arr[high] != len){
		lost.push(arr[high]+1);
		arr.push(arr[high]+1);
		high++;
	}
	
	//排除低位缺失现象。
	while (arr[0] != 1){
		lost.push(arr[0]-1);
		arr.splice(0,0,[arr[0]-1]);
		high++;
	}
	
	//查找剩余缺失元素
	while(lost.length != 3){
		serch(low,high);
		low = 0;
		high = arr.length - 1;
	}

	//14*3=42 最差时间复杂度为42次查找,平均一次查找最差为14次。
	alert(lost);
	

</script>
 </body>
</html>







^.^
----------------------------------------------------Henry


欢迎来群讨论:55732875




























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值