題目
14. 最長公共前綴
編寫一個函數來查找字符串數組中的最長公共前綴。
如果不存在公共前綴,返回空字符串
""
。示例 1:
輸入: ["flower","flow","flight"] 輸出: "fl"示例 2:
輸入: ["dog","racecar","car"] 輸出: "" 解釋: 輸入不存在公共前綴。說明:
所有輸入只包含小寫字母
a-z
。
解答
解答一:兩層for循環
誤區1:剛開始考慮了先數組元素遍歷,然后再元素(字符串)從頭到尾比較,但實際上要先以第一個元素strs[0]為基准。
漏洞1:沒有考慮strs元素為空字符串的情況,如[""],未考慮連續元素都相等的情況如["c","c"],結果這些在提交時都是要判斷的。
個人思路:
1、若傳入為空數組[]或空字符串[""]則直接返回空字符串;
2、取傳入數組的第一個元素為基准字符串,並聲明common數組,用於存放公共前綴;
3、第一層i循環(可以想像指針指向strs[0],且從strs[0][0]往str[0][1、2、3…]移動),第二層j循環(可以想像指針指向strs[1],且從strs[1]往str[2、3…]移動),即題中"flower"中的"f",與"flow"中的"f"相比較;
4、相同則j自增,即"flower"中的"f"繼續與"flight"中的"f"比較;
5、若j循環未徹底完成,即說明當前的str[i]已經不是公共前綴了,就可以返回common了;若此次j循環徹底完成,則將當前的str[i],push進common;
6、外層的i循環結束后,若未觸發過j循環內的return的話,類似["a","a","a"]["ab","abc","abcd"]等等情況的,直接返回strs[0],即str即可。
代碼如下:(已在leetcode提交通過,執行用時104ms)
var longestCommonPrefix = function(strs) { if (strs.length===0 ||strs[0].length===0){return "";} var str=strs[0], common=[]; for(let i=0,len1=str.length;i<len1;i++){ for(let j=1,len2=strs.length;j<len2;j++){ if(str[i]!==strs[j][i]){ return common.join(""); } } common.push(str[i]); }return str; };
兩層循環示意圖如下:
(GIF)
解答二:水平掃描法
參考平台提供題解,提供的第一種方法是“水平掃描法”:
仍然是先把數組中第一個元素str取出來,然后以這個元素為基准,
使用stringObject.indexOf(searchvalue,fromindex)方法查找,
查一下str是否在strs[i]索引為0的位置,
如果不在索引為0的位置,使用stringObject.substring(start,stop)刪減字符串長度,
把str長度減1,再繼續查,直到str長度減到0為止。
代碼如下:(已在leetcode提交通過,執行用時100ms)
var longestCommonPrefix = function(strs) { if (strs.length===0 ||strs[0].length===0){return "";} var str=strs[0]; for (let i=1,len=strs.length;i<len;i++){ while(strs[i].indexOf(str)!==0) { if (str.length === 0) {return "";} str = str.substring(0, str.length - 1); } }return str; }

public String longestCommonPrefix(String[] strs) { if (strs.length == 0) return ""; String prefix = strs[0]; for (int i = 1; i < strs.length; i++) while (strs[i].indexOf(prefix) != 0) { prefix = prefix.substring(0, prefix.length() - 1); if (prefix.isEmpty()) return ""; } return prefix; }
平台提供示意圖如下:
解答三:水平掃描法(改良)
參考平台提供題解,水平掃描法有改良版,
不是取數組第一個元素,還是直接取該元素中的單獨字符,類似本頁中第一種方法,兩個for循環,代碼稍有區別。
首先第一層for循環(枚舉首個字符串的所有字符),使用stringObject.charAt(index)方法獲取數組第一個元素的第i個字符,賦值給c,
第二層for循環,從索引1開始遍歷數組中所有剩余元素的對應i位置的字符,並與c比較,
如果該字符不等於c,或者壓根沒有該字符,直接使用stringObject.substring(start,stop)方法返回首個元素從索引0到i的拷貝,
若兩層循環都正常運行過,未觸發返回,則是類似["ab","abc","abcd"]["aa",aaa"","aaaaa"]這些情況,即第一個元素最短,且整體都符合條件,此時返回這個元素即可。
代碼如下:(已在leetcode提交通過,執行用時92ms)
var longestCommonPrefix = function(strs) { if (strs.length===0 ||strs[0].length===0){return "";} for (let i=0,len1=strs[0].length;i<len1;i++){ let c=strs[0].charAt(i); for (let j=1,len2=strs.length;j<len2;j++){ if(i===strs[j].length||strs[j].charAt(i)!==c){ return strs[0].substring(0,i); } } }return strs[0]; }
解答四:分治
/**2019.03.11 01:40更新
*/
繼續參考平台提供題解,算法三:分治,
大致意思是把數組拆分成左右兩部分,循環拆(遞歸?),直至最后一層的左右兩部分都只有一個元素,
然后將這最后一層的兩個元素進行比較,看是否有公共前綴,結果返回上一層,繼續比較,最后到頂層;
仿照題解,我定義了三個函數,第一個是該題解要運行的函數longestCommonPrefix(),第二個是拆分函數divide(),第三個是比較函數compare();
longestCommonPrefix()返回divide()函數,並傳入strs數組和該數組的索引起始、結束位置;
longestCommonPrefix()代碼如下:
var longestCommonPrefix = function(strs) { if (strs.length===0 ||strs[0].length===0){return "";} return divide(strs,0,strs.length-1); }
divide(strs,l,r)接收三個參數,strs:傳入的數組;l(left):要拆分部分在strs中索引的開始位置;r(right):要拆分部分在strs中索引的結束位置;
在divide()中,首先判斷傳入參數的l和r,如果相等,即是開始位置與結束位置相同,該函數返回這個元素;
如果l不等於r,即數組還沒拆到最后一層,把l和r求和后平分,向下取整,作為l和r的中點索引位置mid,然后中點前后兩部分繼續調用divide(),直至這兩部分都返回一個元素;
divide()代碼如下:
function divide(strs,l,r){ if(l===r){ return strs[l]; }else{ let mid=Math.floor((l+r)/2); let leftPre=divide(strs,l,mid), rightPre=divide(strs,mid+1,r); return compare(leftPre,rightPre); } }
接着調用compare(),這個函數用來比較並找出這兩個元素(字符串)的公共前綴,並將這個前綴返回至上一層和上層的另一部分返回的前綴繼續比較;
依此類推,直到頂層,得出所有元素的最長公共前綴;
這里使用的方法和上個解答一致,兩個, stringObject.substring(start,stop)、 stringObject.charAt(index)。
compare()代碼如下:
function compare(left,right){ let min=Math.min(left.length,right.length); for(let i=0;i<min;i++){ if(left.charAt(i) !== right.charAt(i)){ return left.substring(0,i); } }return left.substring(0,min); }
完整代碼如下:(已在leetcode提交通過,執行用時108ms)

var longestCommonPrefix = function(strs) { if (strs.length===0 ||strs[0].length===0){return "";} return divide(strs,0,strs.length-1); } function divide(strs,l,r){ if(l===r){ return strs[l]; }else{ let mid=Math.floor((l+r)/2); let leftPre=divide(strs,l,mid), rightPre=divide(strs,mid+1,r); return compare(leftPre,rightPre); } } function compare(left,right){ let min=Math.min(left.length,right.length); for(let i=0;i<min;i++){ if(left.charAt(i) !== right.charAt(i)){ return left.substring(0,i); } }return left.substring(0,min); }
平台提示意圖如下:
說下自己碰到的坑,題解里面,這個"l"搞得像個阿拉伯數字"1",在這里浪費了不少時間,🤦♂️,還是太嫩,希望你們別踩坑。
插
插
插
插