徒手挖地球六週目
NO.13 羅馬數字轉整數 簡單
思路一:哈希表 1.用一個hash表把所有羅馬數字和阿拉伯數字相互匹配的特殊值作爲鍵值對存儲起來,例如"M,1000"、“CM,900”、“D,500”、“CD,400”。。。2.然後將字符串逐步分割並去hash表進行查詢匹配,因爲兩位長度的羅馬數字優先於一位長度的羅馬數字,所以每步循環都需要先兩位分割匹配再一位分割匹配。3.匹配到hash表的鍵之後,將對應的值加入結果中。
public int romanToInt(String s) {
int ans=0;
// 所有羅馬數字和阿拉伯數字相互匹配的特殊值作爲鍵值對存儲起來
Map<String,Integer> map=new HashMap<>();
map.put("M",1000);
map.put("CM",900);
map.put("D",500);
map.put("CD",400);
map.put("C",100);
map.put("XC",90);
map.put("L",50);
map.put("XL",40);
map.put("X",10);
map.put("IX",9);
map.put("V",5);
map.put("IV",4);
map.put("I",1);
// 然後將字符串逐步分割並去hash表進行查詢匹配
for (int i=0;i<s.length(); ){
// 兩位長度的羅馬數字優先於一位長度的羅馬數字,所以先進行兩位長度羅馬數字的判斷
if (i+1<s.length()&&map.containsKey(s.substring(i,i+2))){
ans+=map.get(s.substring(i,i+2));
i+=2;
}else {
ans+=map.get(s.substring(i,i+1));
i++;
}
}
return ans;
}
時間複雜度:O(n)
NO.14 最長公共前綴 簡單
思路一:依次LCP法假設輸入數組爲[“leetcode”,“leetcodes”,“leetgo”,“letsgo”]:1. 我們先讓0號元素和1號元素求公共前綴 LCP(0號元素,1號元素)求出最長公共前綴prefix=“leetcode”。2. 然後prefix=LCP[prefix,2號元素]求出prefix=leet。3. prefix=LCP[prefix,3號元素]求出prefix=le。求出最終的答案"le"。
public String longestCommonPrefix(String[] strs) {
// 如果數組元素個數爲0,返回""
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;
}
時間複雜度:O(s) s是數組中每個字符串的字符個數之和
空間複雜度:O(1)
思路二:優化依次LCP法 上述方法中,無論數組每個元素的長短我們都需要從第一個數組元素比較到最後一個數組元素,所以有一個明顯的問題:如果數組中最後一個元素非常短,但是我們仍然需要比較s次。
可以將思路一的算法改爲從前往後枚舉字符串的每一列,先比較每個字符串相同列上的字符(即不同字符串相同下標的字符)然後再進行對下一列的比較。
依然假設輸入數組爲[“leetcode”,“leetcodes”,“leetgo”,“letsgo”]:1. 第一次循環i=0先用0號元素的第一個字符"l",去和1號元素的第一個字符比較,相等;繼續去和第2號元素的第一個字符比較,相等;繼續去和第3號元素的第一個字符比較,相等;2. 第二次循環i=1再用0號元素的第二個字符"e",去和1號元素的第二個字符比較,相等;繼續去和第2號元素的第二個字符比較,相等;繼續去和第3號元素的第二個字符比較,相等;3. 第三次循環i=2再用0號元素的第三個字符"e",去和1號元素的第三個字符比較,相等;繼續去和第2號元素的第三個字符比較,相等;繼續去和第3號元素的第三個字符比較,不相等,則返回數組0號元素[0,i)字符,即"le"。
public String longestCommonPrefix(String[] strs) {
if (strs==null||strs.length==0)return "";
// 將數組的0號元素的每個字符逐一取出進行比較
for (int i=0;i<strs[0].length();i++){
// 取出數組0號元素的i號字符
char c = strs[0].charAt(i);
// 將c和數組的每個元素的i號字符進行比較
for (int j=1;j<strs.length;j++){
// 如果某個元素的所有字符都已比較完畢(即最短的元素)或者某個元素的第i個字符和c不相等
if (i==strs[j].length()||strs[j].charAt(i)!=c){
// 則0號元素的[0,i)號字符就是公共前綴
return strs[0].substring(0,i);
}
}
}
return strs[0];
}
時間複雜度:O(s) s是數組中每個字符串的字符個數之和。在最壞情況下,本算法的效率與算法一相同,但是最好的情況下,算法只需要進行 n*minLen 次比較,其中minLen是數組中最短字符串的長度。
空間複雜度:O(1)
思路三:分治法 其實這方法也是上述的依次LCP法的一種優化方法,LCP(s1,s2,…,sn)=LCP(LCP(s1,s2,…,smid),LCP(smid+1,smid+2,…sn))。
先將數組元素分成兩部分,分別求LCP得到lcpLeft和lcpRight,最後求LCP(lcpLeft,lcpRight)得到prefix。
public String longestCommonPrefix(String[] strs) {
if (strs == null || strs.length == 0) return "";
return longestCommonPrefix(strs, 0 , strs.length - 1);
}
private String longestCommonPrefix(String[] strs, int l, int r) {
if (l == r) {
return strs[l];
}
else {
int mid = (l + r)/2;
String lcpLeft = longestCommonPrefix(strs, l , mid);
String lcpRight = longestCommonPrefix(strs, mid + 1,r);
return commonPrefix(lcpLeft, lcpRight);
}
}
String commonPrefix(String left,String right) {
int min = Math.min(left.length(), right.length());
for (int i = 0; i < min; i++) {
if ( left.charAt(i) != right.charAt(i) )
return left.substring(0, i);
}
return left.substring(0, min);
}
時間複雜度:O(S),S是所有字符串中字符數量的總和,S=m∗n。
空間複雜度:O(m*log(n))
NO.15 三數之和 中等
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wSpkRvS0-1576661830367)(https://s2.ax1x.com/2019/12/17/QocMLV.png)]
這道題有一個麻煩的地方,就是需要去重,如果直接簡單的三重循環暴力破解的話,除了時間複雜度問題之外還不便於去重。
思路一:雙指針法 1. 首先對數組進行排序。2. 依次遍歷數組元素,每遍歷一個元素nums[i]時,就用左右指針指向nums[i]後面元素部分的兩端,即指向nums[L]和nums[R],判斷nums[i]、nums[L]和nums[R]之和sum是否等於0,等於0則加入結果集。如果sum>0,則說明需要較小的數字,即"R–"。如果sum<0,則說明需要較大的數字,即"L++"。循環直至左右指針相遇,即後面元素部分已組合完畢,則本次循環結束。3. 如果遍歷到某個元素nums[i]已經大於0,則三數之和必然大於0(充分利用排序後的特點,減少無用的比較),結束循環。
然後是該算法去重的思路:4. 如果nums[i]==nums[i-1],就會導致結果重複,所以應該跳過。5. 如果sum==0的時候,nums[L]==num[L+1]就會導致結果重複,所以應該跳過,L++。6. 如果sum==0的時候,nums[R]=nums[R-1]就會導致結果重複,所以應該跳過,R–。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> ans=new ArrayList<>();
int len = nums.length;
if (nums==null||len<3)return ans;
// 1.排序
Arrays.sort(nums);
// 2. 依次遍歷數組元素
for (int i=0;i<len;i++){
// 如果當前元素已經大於0,那麼之後所有的三數之和一定都大於0。結束循環。
if (nums[i]>0)break;
// 4. 如果nums[i]==nums[i-1],就會導致結果重複,所以應該跳過。
if (i>0&&nums[i]==nums[i-1])continue;
// 用左右指針指向nums[i]後面元素部分的兩端
int L=i+1;
int R=len-1;
// 循環直至左右指針相遇,即後面元素部分已組合完畢,則本次循環結束。
while (L<R){
int sum = nums[i] + nums[L] + nums[R];
if (sum==0){
// 三數之和等於0,等於0則加入結果集。
ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
// 5. 如果sum\=\=0的時候,nums[L]\=\=num[L+1]就會導致結果重複,所以應該跳過,L++。
while (L<R&&nums[L]==nums[L+1])L++;
// 6. 如果sum\=\=0的時候,nums[R]=nums[R-1]就會導致結果重複,所以應該跳過,R--。
while (L<R&&nums[R]==nums[R-1])R--;
L++;
R--;
}else if (sum>0){//如果sum>0,則說明需要較小的數字,即"R--"
R--;
}else if (sum<0){//如果sum<0,則說明需要較大的數字,即"L++"
L++;
}
}
}
return ans;
}
時間複雜度:O(n^2)