1.環形鏈表
題目:
給定一個鏈表,判斷鏈表中是否有環。
爲了表示給定鏈表中的環,我們使用整數 pos
來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos
是 -1
,則在該鏈表中沒有環。
思路:先說最簡單的,用map記錄每個節點,然後依次比較即可
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function (head) {
var map = new Map();
while (head) {
if (map.has(head)) return true;
map.set(head, 111);
head = head.next;
}
return false;
};
問題:沒什麼大問題,不過需要額外的空間,可以優化
優化方向:追及問題,又或者叫快慢指針。兩個指針,一個跑得快一個跑得慢,如果有環形,那麼肯定會相交的。
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function (head) {
if (!head || !head.next) {
return false
}
let fast = head
let slow = head
while (slow) {
if (!fast || !fast.next) {
return false
}
fast = fast.next.next
slow = slow.next
if (Object.is(fast, slow)) {
return true
}
}
return false
};
2.最小棧
題目:
設計一個支持 push ,pop ,top 操作,並能在常數時間內檢索到最小元素的棧。
push(x) —— 將元素 x 推入棧中。
pop() —— 刪除棧頂的元素。
top() —— 獲取棧頂元素。
getMin() —— 檢索棧中的最小元素。
思路:沒啥說的,其實比較簡單,這裏就放基於數組的實現
var MinStack = function () {
this.data = [];
};
/**
* @param {number} x
* @return {void}
*/
MinStack.prototype.push = function (x) {
this.data.push(x);
};
/**
* @return {void}
*/
MinStack.prototype.pop = function () {
if (this.data.length) this.data.pop();
};
/**
* @return {number}
*/
MinStack.prototype.top = function () {
if (this.data.length) {
return this.data[this.data.length - 1];
} else {
return null;
}
};
/**
* @return {number}
*/
MinStack.prototype.getMin = function () {
if (this.data.length) {
return Math.min(...this.data);
} else {
return null;
}
};
基於鏈表也可以,大同小異
3.相交鏈表
題目:編寫一個程序,找到兩個單鏈表相交的起始節點。
注意:
如果兩個鏈表沒有交點,返回 null.
在返回結果後,兩個鏈表仍須保持原有的結構。
可假定整個鏈表結構中沒有循環。
程序儘量滿足 O(n) 時間複雜度,且僅用 O(1) 內存。
思路:先還是說最簡單的,用map來比較。先把第一個鏈表的節點存儲,然後遍歷第二個鏈表的節點。
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let map = new Map();
while (headA) {
map.set(headA);
headA = headA.next;
}
while (headB) {
if (map.has(headB)) {
return headB;
}
headB = headB.next;
}
return null;
};
問題:需要額外的空間
優化方向:雙指針法。可以用兩個指針同時跑,如果遍歷一次之後交換,如果有相交,那麼在第二次遍歷的時候一定會在同一個節點結束
。如果不相交,那麼第二次遍歷時最後一個節點是null
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let pa = headA;
let pb = headB;
while(pa !==pb){
pa = pa? pa.next : headB;
pb = pb? pb.next : headA;
}
return pa;
};
4.兩數之和 II
題目:
給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。
函數應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。
說明:
返回的下標值(index1 和 index2)不是從零開始的。
你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。
思路:還是用之前的,不過可以優化的是,因爲是有序數組,可以根據最大值和最小值優先排除一些元素,這樣可以節省空間
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(numbers, target) {
let l = numbers.length;
let min = target - numbers[l - 1];
let map = {};
for (let i = 1; i <= l; i++) {
if (numbers[i - 1] < min) continue;
if (map[target - numbers[i - 1]]) return [map[target - numbers[i - 1]], i];
map[numbers[i - 1]] = i;
}
};
優化方向:對撞指針。兩個指針一頭一尾,相加,如果大於目標值,則尾部向前+1,如果小於目標值,則頭部向前+1,直到得到結果
/**
* @param {number[]} numbers
* @param {number} target
* @return {number[]}
*/
var twoSum = function(numbers, target) {
let front=0;end=numbers.length-1;
while(front<end){
const frontNums=numbers[front];
const endNums=numbers[end];
if(frontNums+endNums>target){
end--;
}
if(frontNums+endNums<target){
front++;
}
if(frontNums+endNums===target){
// 答案是從1開始數,程序求出來起始點是0,所以 +1
return [front+1,end+1]
}
}
};
5.Excell表列名稱
題目:
給定一個正整數,返回它在 Excel 表中相對應的列名稱。
例如,
1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -> AB ...
示例 1:
輸入: 1 輸出: "A"
示例 2:
輸入: 28 輸出: "AB"
示例 3:
輸入: 701 輸出: "ZY"
思路:建立字母和數字的映射關係,A=>0,B=>1這樣,然後循環操作數字,每次取26的餘數作爲當前,然後除以26的值作爲下一輪的值
/**
* @param {number} n
* @return {string}
*/
let map=[ 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z']
var convertToTitle = function (n) {
let result = ''
while (n > 0) {
n--
result = map[(n%26)] + result;
n = Math.floor(n / 26);
}
return result;
};