
壹 ❀ 引
在學習算法基礎的同時,我還是繼續撿起leetcode的算法題了,珍惜時間,算法每天進步一點點。不得不說,在了解了一些算法概念后,至少有些答案是能看懂了......(慚愧)雖然我很菜,但是多寫多練應該還是會有提升。那么這篇文章就從兩數之和開始。
原題如下:
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那 兩個整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
請實現如下方法能達到上述效果:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function(nums, target) {
};
貳 ❀ 聽風是風不太聰明的實現
在解釋優質答案前,我先說說自己的思路,畢竟不是很好的做法,大家也可以跳過不看。
我想出了兩種做法,先說第一種,由於只有一個數組,但是我們得讓數組中的元素兩兩相加並與目標值判斷是否相等,所以首先想到就是利用for循環嵌套,遍歷同一個數組。

由於不能重復使用同一個元素,所以外層從0開始遍歷時,內層循環的索引起點肯定得是1,當外層是1時,內層索引從2開始。這樣做的目的除了避免同一元素多次使用外,還減少了重復比較次數。我們來大致腦補下,比如外層是0時,會與內層1相加,而如果內層不在外層基礎上加1,就還會出現外層是1時,與內層是0相加比較的情況,這樣就重復了。

所以知道了這些就好做了,直接貼我的實現:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
// 用來裝目標索引的空數組
var result = [],
len = nums.length;
for (var i = 0; i < len; i++) {
// 注意,這里從i+1開始遍歷
for (var j = i + 1; j < len; j++) {
if (nums[i] + nums[j] === target) {
// 注意,concat返回一個新數組
result = result.concat(i, j);
return result;
};
};
}
};
我想到的第二種解決思路其實是在學插入排序時得到的靈感,上面的實現簡單粗暴,無非就是把一個數組當兩個用,第二種思路就是對同一個數組正序倒序同時遍歷比較,當然也得使用循環嵌套。
比如外層循環從0開始,那么內層循環從length-1處倒序遍歷,一直遍歷到0前面一位,期間的元素分別與0的數字相加並與target比較:


直接貼我的第二種思路,由於還是使用了循環嵌套,所以耗時相比第一種也只是提升了一點點,但內存使用卻大了不少,所以也不算很好的實現。
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
var result = [],
len = nums.length;
for (var i = 0; i < len; i++) {
var len_ = len - 1;
// 倒序遍歷,一直遍歷到i的前一位
while (len_ > i) {
if (nums[i] + nums[len_] === target) {
result = result.concat(i, len_);
return result;
};
len_--;
};
};
};
叄 ❀ 優質解答
那么在說完我的弱智算法,來看看官方的優質解答,直接貼代碼,我們再分析實現:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
var temp = [],
len = nums.length;
for (var i = 0; i < len; i++) {
var dif = target - nums[i];
if (temp[dif] != undefined) {
return [temp[dif], i];
};
temp[nums[i]] = i;
};
};
這個思路其實就是借助了哈希表,犧牲少量空間大大減少運行用時,畢竟只用了一次遍歷。如果大家對於哈希表很陌生,還是很推薦大家先理解常用數據結構,我在從零開始的算法入門科普(二)一文中有通過圖解簡單科普哈希表的概念,只是在上述代碼在哈希表存值時並未借助哈希函數。
通常來說哈希表常常用來存儲key-value的數據,由於我們最終是要獲取目標元素索引,所以代碼實現中直接使用數組元素作為位置映射,用於存儲當前元素索引。其次,巧妙通過var dif = target - nums[i]
分別獲取目標值與當前遍歷元素的差值,如果這個差值作為哈希表的索引能訪問到有效元素,那么說明當前遍歷的i與temp[dif]
(拿到的是元素在nums中的索引)就是要找的索引。
不看答案我是真的想不上去,表示驚呆了,那么關於兩數之和就說到這了,收獲很大!!!
本文結束!
2020.5.24復習,與哈希表思路相同,不過這次換成了字典,執行時間會略微提升,代碼如下:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var twoSum = function (nums, target) {
var i = 0,
len = nums.length,
dict = {};
for (; i < len; i++) {
var dif = target - nums[i];
if (dif in dict) {
return [dict[dif], i];
};
dict[nums[i]] = i;
};
};