1.最長公共前綴
題目:
編寫一個函數來查找字符串數組中的最長公共前綴。
如果不存在公共前綴,返回空字符串 ""
。
思路:以第一個字符串爲基準,如果,從第一個字符開始依次和其他成員比較,如果通過則拼接,不通過直接返回當前的結果
var longestCommonPrefix = function(strs) {
const s1 = strs[0];
if (!s1) return "";
const l1 = s1.length,
l2 = strs.length;
if (l2 < 2) return s1;
let res = "";
for (let i = 0; i < l1; i++) {
for (let j = l2 - 1; j; j--) {
if (s1[i] !== strs[j][i]) {
return res;
}
}
res += s1[i];
}
return res
};
問題:每次拼接沒有必要,
優化思路:只需要記錄下標即可,最後直接用下標截取
var s1 = strs[0],
l = strs.length;
if (!s1) return "";
if (l < 2) return s1;
var l1 = s1.length,
i = 0,
j,m;
while (i < l1) {
j = l - 1;
m=s1[i]
while (j) {
if (m !== strs[j][i]) {
return s1.substr(0, i);
}
j--;
}
i++;
}
return s1;
2.有效的括號
題目:
給定一個只包括 '(',')','{','}','[',']' 的字符串,判斷字符串是否有效。
有效字符串需滿足:
左括號必須用相同類型的右括號閉合。
左括號必須以正確的順序閉合。
注意空字符串可被認爲是有效字符串。
思路:遍歷字符串,區分(、[、{三種情況,找到對應的右邊字符的位置,注意嵌套的情況,所以用個計數器來記錄相同字符的次數。然後切割當前成對字符中間的字符以及剩餘字符,做一個遞歸處理。如果先遇到右邊的字符直接返回false
var isValid = function(s) {
var l = s.length;
if (!l) return true;
if (l % 2) return false;
var m,
i = 0;
var f = (q, v) => {
var c = 0;
for (let j = 1; j < l; j++) {
if (s[j] === v && !c) return j;
if (s[j] === q) c++;
if (s[j] === v) c--;
}
return false;
};
switch (s[0]) {
case "(":
m = f("(", ")");
if (!m || !(m % 2)) return false;
return isValid(s.slice(1, m)) && isValid(s.slice(m + 1));
case "{":
m = f("{", "}");
if (!m || !(m % 2)) return false;
return isValid(s.slice(1, m)) && isValid(s.slice(m + 1));
case "[":
m = f("[", "]");
if (!m || !(m % 2)) return false;
return isValid(s.slice(1, m)) && isValid(s.slice(m + 1));
default:
return false;
}
};
問題:回溯太多,遞歸次數太多,每個字符都在遍歷,並且可能會遍歷好幾次
優化方向:匹配過程就是相鄰匹配,且內部優先。只有知道右邊的字符,才能匹配相鄰的字符,所以先把左邊的字符放進棧裏,然後遇到右邊的字符,就取最近的一個字符,如果匹配成功則繼續往下遍歷,否則直接返回false
var isValid = function(s) {
var l = s.length;
if (l % 2) return false;
var a = [],
item,
v;
for (let i = 0; i < l; i++) {
item = s[i];
if (item === "(" || item === "[" || item === "{") {
a.push(item);
} else {
if (!a.length) return false;
v = a.pop();
if (
(item === ")" && v !== "(") ||
(item === "]" && v !== "[") ||
(item === "}" && v !== "{")
) {
return false;
}
}
}
return !a.length;
};
3.合併兩個有序鏈表
題目:將兩個升序鏈表合併爲一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
思路:這個和歸併排序的最後一步很像,可以分遞歸和循環兩種方式處理
遞歸:
遞歸的思路,就是每次都找到當前的節點,然後將next指向調用剩下兩個鏈表的結果,最終遞歸處理得到最終的結果
var mergeTwoLists = function(l1, l2) {
if (!l1 || !l2) {
return l1 || l2;
}
if (l1.val > l2.val) {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
} else {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
}
};
循環:
循環:
循環的思路,是找到每次比較的當前節點,然後用一個變量記錄最終的結果,一個變量記錄上一次比較的結果,這樣在下次得到結果之後把上一次的結果的next指向當前的結果。最後將剩下的節點加入到最後
var mergeTwoLists = function(l1, l2) {
if (!l1 || !l2) {
return l1 || l2;
}
var res, s;
while (l1 && l2) {
if (l1.val > l2.val) {
s = s || res;
if (s) {
s.next = l2;
s = s.next;
}
res = res || l2;
l2 = l2.next;
} else {
s = s || res;
if (s) {
s.next = l1;
s = s.next;
}
res = res || l1;
l1 = l1.next;
}
}
if (s) {
s.next = l1 || l2;
} else {
res.next = l1 || l2;
}
return res;
};
4.刪除排序數組中的重複項
題目:
給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。 不要使用額外的數組空間,你必須在 原地 修改輸入數組 並在使用 O(1) 額外空間的條件下完成。
思路:用雙指針的方式,第一個指針就是當前不重複元素的下標,第二個指針是此輪遍歷元素的下標,單次遍歷,從第二項開始,兩個指針上的值比較,如果相等,就進入下一輪循環,如果不相等,就把當前項的值賦值給第一個指針的下一個元素,同時第一個指針加一,進入下一輪循環。
var removeDuplicates = function(nums) {
var l = nums.length;
if (l < 2) return l;
var temp,
res = 0;
for (var i = 1; i < l; i++) {
if (nums[res] !== nums[i]) {
temp = nums[i];
nums[i] = nums[res + 1];
nums[res + 1] = temp;
res++;
}
}
return res + 1;
};