騰訊的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




























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章