首頁目錄 點擊查看
第一題
- 難度:中等
- 題目:3. 無重復字符的最長子串
給定一個字符串,請你找出其中不含有重復字符的 最長子串 的長度。
示例
輸入: "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
輸入: "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b",所以其長度為 1。
解題思路
-
這道理我想了好久啊,都覺得沒有很好地方式可以處理,看了一個大神的解題思路,我豁然開朗,貼出來
使用一個數組來維護滑動窗口
遍歷字符串,判斷字符是否在滑動窗口數組里不在則 push 進數組
在則刪除滑動窗口數組里相同字符及相同字符前的字符,然后將當前字符 push 進數組
然后將 max 更新為當前最長子串的長度
遍歷完,返回 max 即可
image.png
我的答案
根據以上思路,答案就很容易寫出來了
var lengthOfLongestSubstring = function (s) {
let max = 0,
array = [];
for (let i = 0; i <= s.length - 1; i++) {
let index = array.indexOf(s[i])
if (index !== -1) {
array.splice(0, index + 1)
}
array.push(s[i])
max = Math.max(array.length, max)
}
return max
};
- 時間復雜度:O(N)
- 空間復雜度: O(N)
高分答案
除了以上的維護數組的方法,還有另外兩種解題思路,維護下標以及優化map
//維護下標
var lengthOfLongestSubstring = function(s) {
let index = 0, max = 0
for(let i = 0, j = 0; j < s.length; j++) {
index = s.substring(i, j).indexOf(s[j])
if(index !== -1) {
i = i + index + 1
}
max = Math.max(max, j - i + 1)
}
return max
};
//優化Map
var lengthOfLongestSubstring = function(s) {
let map = new Map(), max = 0
for(let i = 0, j = 0; j < s.length; j++) {
if(map.has(s[j])) {
i = Math.max(map.get(s[j]) + 1, i)
}
max = Math.max(max, j - i + 1)
map.set(s[j], j)
}
return max
};
這兩個的性能都差不多,稍微比我的第一種要好一點,在內存消耗上優勢更加明顯。
第二題
- 難度:中等
- 題目:11. 盛最多水的容器
給你 n 個非負整數 a1,a2,...,an,每個數代表坐標中的一個點 (i, ai) 。在坐標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0)。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
說明:你不能傾斜容器,且 n 的值至少為 2。
image
圖中垂直線代表輸入數組 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍色部分)的最大值為 49。
示例:
輸入:[1,8,6,2,5,4,8,3,7]
輸出:49
解題思路
- 簡單粗暴雙循環,遍歷數組,挨個去比較
- 雙指針 效率更高,定義兩個角標i,j 根據數值的大小動態改變i,j的值,直到把數組數值都比較一遍。
我的答案
- 簡單粗暴雙循環
let area = 0,
carea = 0;
for (let i = 0; i <= height.length - 1; i++) {
for (let j = i + 1; j <= height.length - 1; j++) {
carea = Math.min(height[i], height[j]) * (j - i)
if (area <= carea) {
area = carea
}
}
}
return area
時間復雜度:O(N2)
-
空間復雜度: O(N)
image.png 雙指針
let area = 0,
carea = 0,
i = 0,
j = height.length - 1;
while (i < j) {
carea = Math.min(height[i], height[j]) * (j - i)
if (area <= carea) {
area = carea
}
if (height[i] < height[j]) {
i++
} else {
j--
}
}
return area
- 時間復雜度:O(N)
-
空間復雜度: O(N)
image.png
這兩者的性能差距實在是太大了,所以還是推薦雙指針吧。
第三題
- 難度:中等
- 題目: 15. 三數之和
給你一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有滿足條件且不重復的三元組。
注意:答案中不可以包含重復的三元組。
示例
給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合為:
[
[-1, 0, 1],
[-1, -1, 2]
]
解題思路
雙指針的做法,首先對數組進行排序,如果第一個數大于零則直接返回空,遍歷整個數組,如果當前的nums[i]
===nums[i + 1]
則直接continue
,沒必要浪費時間,設置left=i+1
、right = nums.length-1
當left
<right
,判斷nums[i] + nums[left] + nums[right]
,如果和大于零,則減小right
反之增大left
,如果相等,保存數值,也要判斷left
和right
的上一位和下一位是否相等,如果相等則直接進行下一個。
我的答案
var threeSum = function (nums) {
let array = []
nums.sort((a, b) => {
return a - b
})
if (nums[0] > 0) {
return array
}
for (let i = 0; i <= nums.length - 1&& nums[i] <=0; i++) {
let left = i + 1;
let right = nums.length - 1
if (nums[i] == nums[i - 1]) continue; // 去重
while (left < right) {
let sum = nums[i] + nums[left] + nums[right]
if (sum < 0) {
left++
}
if (sum > 0) {
right--
}
if (sum === 0) {
array.push([nums[left], nums[right], nums[i]])
while (left < right && nums[left] == nums[left + 1]) left++; // 去重
while (left < right && nums[right] == nums[right - 1]) right--; // 去重
left++;
right--
}
}
}
return array
};
第四題
- 難度:中等
- 題目: 16. 最接近的三數之和
給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在唯一答案。
示例
輸入:nums = [-1,2,1,-4], target = 1
輸出:2
解釋:與 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
解題思路
- 這道題的解題思路和之前的的三數之和很類似,相當于之前的相加等于0,現在改為任意數字,然后加一個判斷是否和是和目標最接近的。
我的答案
var threeSumClosest = function (nums, target) {
let dis = null,
result = 0;
nums.sort((a, b) => {
return a - b
})
for (let i = 0; i <= nums.length - 1; i++) {
let left = i + 1;
let right = nums.length - 1
if (nums[i] == nums[i - 1]) continue; // 去重
while (left < right) {
let sum = nums[i] + nums[left] + nums[right]
let current = Math.abs(target - sum)
if (dis > current) {
dis = current
result = sum
}
if (dis === null) {
dis = current
result = sum
}
if (sum > target) {
right--
}
if (sum < target) {
left++
}
if (sum - target === 0) {
return sum
}
}
}
return result
};
第五題
- 難度:簡單
- 題目:26. 刪除排序數組中的重復項
給定一個排序數組,你需要在 原地 刪除重復出現的元素,使得每個元素只出現一次,返回移除后數組的新長度。
不要使用額外的數組空間,你必須在 原地 修改輸入數組 并在使用 O(1) 額外空間的條件下完成。
示例:
給定數組 nums = [1,1,2],
函數應該返回新的長度 2, 并且原數組 nums 的前兩個元素被修改為 1, 2。
你不需要考慮數組中超出新長度后面的元素。
給定 nums = [0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 并且原數組 nums 的前五個元素被修改為 0, 1, 2, 3, 4。
你不需要考慮數組中超出新長度后面的元素。
說明:
為什么返回數值是整數,但輸出的答案是數組呢?
請注意,輸入數組是以「引用」方式傳遞的,這意味著在函數里修改輸入數組對于調用者是可見的。
你可以想象內部操作如下:
// nums 是以“引用”方式傳遞的。也就是說,不對實參做任何拷貝
int len = removeDuplicates(nums);
// 在函數里修改輸入數組對于調用者是可見的。
// 根據你的函數返回的長度, 它會打印出數組中該長度范圍內的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
解題思路
- 裁剪數組
這道題可以倒著遍歷數組,創建一個last
用于存放數組最后一個數,用當前遍歷到的數和last
對比,如果不等于last
則把當前數賦值給last
,否則說明兩數相等,刪除當前數值。 - 雙指針
這里雙指針的方法,和之前不太一樣,right
不是從最后一個數開始,而是從left
下一位開始,比較兩數大小,如果兩者不相等,則將right
的值賦值給left
的下一位。
我的答案
- 裁剪數組
var removeDuplicates = function (nums) {
let last = nums[nums.length] - 1;
for (let i = nums.length - 1; i >= 0; i--) {
if (nums[i] !== last) {
last = nums[i]
} else {
nums.splice(i, 1)
}
}
return nums.length
};
console.log(removeDuplicates([1, 1, 2, 2, 3, 3, 4, 5, 6]))
- 雙指針
var removeDuplicates = function (nums) {
let i = 0;
let j = 1;
while (j < nums.length) {
if (nums[i] !== nums[j]) {
nums[++i] = nums[j]
}
j++
}
return i + 1
};
console.log(removeDuplicates([1, 1, 2, 2, 3, 3, 4, 5, 6]))
這兩種方法比較極端啊,一個內存消耗低,一個執行時間短,各有所長,不過我更覺得雙指針更好
第六題
- 難度:中等
- 題目:75. 顏色分類
給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,使得相同顏色的元素相鄰,并按照紅色、白色、藍色順序排列。
此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。
注意:
不能使用代碼庫中的排序函數來解決這道題。
示例
輸入: [2,0,2,1,1,0]
輸出: [0,0,1,1,2,2]
進階
一個直觀的解決方案是使用計數排序的兩趟掃描算法。
首先,迭代計算出0、1 和 2 元素的個數,然后按照0、1、2的排序,重寫當前數組。
你能想出一個僅使用常數空間的一趟掃描算法嗎?
解題思路
- 這道題因為本來就屬于雙指針的題目,所以理所當然就使用雙指針,只是這里,單純的雙指針明顯是不夠用的,所以這里需要添加一個current,表示當前的數值index。
- 另外一個方法是看了題解,遇到0就刪除該元素,放到最前面,遇到2就刪除該元素,放到最后面,遇到1不處理
我的答案
- 操作刪除元素
var sortColors = function (nums) {
let index = 0;
let count = 0;
while (count < nums.length) {
if (nums[index] === 0) {
nums.splice(index, 1)
nums.unshift(0)
index++
} else if (nums[index] === 2) {
nums.splice(index, 1)
nums.push(2)
} else {
index++
}
count++
}
return nums
};
- 雙指針
var sortColors = function (nums) {
let left = 0;
let right = nums.length - 1;
let current = 0
while (current <= right) {
if (nums[current] === 0) {
[nums[current], nums[left]] = [nums[left], nums[current]];
left++
current++
continue;
} else if (nums[current] === 2) {
[nums[current], nums[right]] = [nums[right], nums[current]];
right--
continue;
} else {
current++
continue;
}
}
return nums
};
雙指針的性能和直接操作元素的差距還是比較明顯的。
第七題
- 難度:中等
- 題目:209. 長度最小的子數組
給定一個含有 n 個正整數的數組和一個正整數 s ,找出該數組中滿足其和 ≥ s 的長度最小的 連續 子數組,并返回其長度。如果不存在符合條件的子數組,返回 0。
示例
輸入:s = 7, nums = [2,3,1,2,4,3]
輸出:2
解釋:子數組 [4,3] 是該條件下的長度最小的子數組。
解題思路
- 這道題的解題思路就是滑動窗口,先擴大搜索范圍,找到合適的區域,然后再縮小區域,找出最合適的解。
我的答案
var minSubArrayLen = function (s, nums) {
let minLen = Infinity;
let left = 0;
let right = 0;
let sum = 0;
while (right < nums.length ) {
sum += nums[right]
while (sum >= s) {
minLen = Math.min(minLen, right - left + 1);
sum -= nums[left]
left++
}
right++
}
return minLen === Infinity ? 0 : minLen
};
第八題
- 難度:中等
- 題目:80. 刪除排序數組中的重復項 II
給定一個排序數組,你需要在原地刪除重復出現的元素,使得每個元素最多出現兩次,返回移除后數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組并在使用 O(1) 額外空間的條件下完成。
示例
給定 nums = [1,1,1,2,2,3],
函數應返回新長度 length = 5, 并且原數組的前五個元素被修改為 1, 1, 2, 2, 3 。
你不需要考慮數組中超出新長度后面的元素。
給定 nums = [0,0,1,1,1,1,2,3,3],
函數應返回新長度 length = 7, 并且原數組的前五個元素被修改為 0, 0, 1, 1, 2, 3, 3 。
你不需要考慮數組中超出新長度后面的元素。
為什么返回數值是整數,但輸出的答案是數組呢?
請注意,輸入數組是以“引用”方式傳遞的,這意味著在函數里修改輸入數組對于調用者是可見的。
你可以想象內部操作如下:
// nums 是以“引用”方式傳遞的。也就是說,不對實參做任何拷貝
int len = removeDuplicates(nums);
// 在函數里修改輸入數組對于調用者是可見的。
// 根據你的函數返回的長度, 它會打印出數組中該長度范圍內的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
解題思路
- 這道題和 26. 刪除排序數組中的重復項非常類似,只需要把+1改為+2就是答案,但是這個答案的效率看了一下并不是很高,所以看了一下題解,看到一個大哥的答案,覺得解題思路很厲害,貼出來
我的答案
- 26題改
var removeDuplicates = function (nums) {
let index = 0;
let pass = 0;
while (index + pass <= nums.length - 1) {
let current = nums[index + pass + 2];
if (current === nums[index]) {
pass++
} else {
index++;
[nums[index], nums[index + pass]] = [nums[index + pass], nums[index]]
}
}
return index
};
- 空間O(1)
var removeDuplicates = function (nums) {
let index = 0;
for (let i = 0; i <= nums.length - 1; i++) {
if (index < 2 || nums[i] > nums[index - 2]) {
nums[index++] = nums[i];
}
}
return index
};