LeetCode刷題記錄(三)
LeetCode刷題記錄(三)
1、螺旋矩陣
題目:
我的思路:
- 我將獲取螺旋矩陣的過程分為四步:先從左往右遍歷矩陣的值,到最右之後再從上往下遍歷,到最下面之後再從右往左遍歷,到最左側之後再從下往上遍歷,這樣依次迴圈,直到遍歷到最後一個值;
- 根據這個思路我定義四個變數,分別表示橫向的最小值、最大值和縱向的最小值、最大值,並且可以定義橫向座標和縱向座標,每次遍歷都是橫向或者縱向座標從小到大或從大到小的移動;
- 這裡需要注意一點的就是,每遍歷一次到最後的時候,對應最大最小值都需要進行調整,例如在從左到右遍歷到最右邊之後,縱向的最小值就需要加1,依次類推,在從上往下遍歷到最後的時候,橫向最大值要減1,從右往左遍歷到最後的時候橫向最大值要減1,從下往上遍歷到最後的時候,橫向最小值要加1,這樣做是為了避免出現迴圈遍歷,也就是永遠只遍歷最外面一圈的情況。
按照這個思路實現的程式碼如下:
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
if(matrix == null) {
return new ArrayList<Integer>();
}
if(matrix.length == 0) {
return new ArrayList<Integer>();
}
List<Integer> rslt = new ArrayList<Integer>();
//1、定義4個變數分別表示橫向最大、最小和縱向最大最小
int colFirst = 0, rowFirst = 0;
int colLast = matrix[0].length - 1, rowLast = matrix.length - 1;
int num = matrix[0].length * matrix.length;
//2、遍歷的索引
int i = 0, j = 0;
//3、四個布林值變數用來判斷進入哪一個if判斷
boolean colToBig = true, rowToBig = false, colToSmall = false, rowToSmall = false;
for(int a = 0; a < num; a++) {
if(i <= colLast && colToBig) {
rslt.add(matrix[j][i]);
if(i < colLast) {
//4、j保持不變,i加1,這樣從左往右遍歷
i++;
} else {
//5、遍歷到最後一個值的時候,縱向最小值加1,j加1,i保持不變,用於下次遍歷
rowFirst++;
j++;
//6、重置布林值,用於下次判斷
colToBig = false;
rowToBig = true;
continue;
}
}
if(j <= rowLast && rowToBig) {
rslt.add(matrix[j][i]);
if(j < rowLast) {
j++;
} else {
colLast--;
i--;
rowToBig = false;
colToSmall = true;
continue;
}
}
if(i >= colFirst && colToSmall) {
rslt.add(matrix[j][i]);
if(i > colFirst) {
i--;
} else {
rowLast--;
j--;
colToSmall = false;
rowToSmall = true;
continue;
}
}
if(j >= rowFirst && rowToSmall) {
rslt.add(matrix[j][i]);
if(j > rowFirst && rowToSmall) {
j--;
} else {
colFirst++;
i++;
rowToSmall = false;
colToBig = true;
}
}
}
return rslt;
}
}
反思:
在程式碼中我還定義了四個布林值變數:
boolean colToBig = true, rowToBig = false, colToSmall = false, rowToSmall = false;
這是用來控制是否進入判斷邏輯的,因為在剛開始我沒有定義這四個變數的時候,在for迴圈中,i和j很可能滿足多個if判斷邏輯,但是實際上我們只希望每次遍歷的時候只走進其中一個if判斷,因此定義布林值變數,並在每次遍歷到最後的時候進行重置,這樣就能保證每次只進入一個if判斷了。
2、楊輝三角
題目:
我的思路:
- 這一題思路較為簡潔,根據楊輝三角的定義,每一行中每個數都是左上方的數和右上方的數相加的結果,並且第n行就有n個數,因此在二維陣列中,第n行的第i個數實際上就是n-1行的第i-1和第i個數相加的結果;
- 但是這裡有兩點需要注意一下,首先第1行的數,因為第1行的數無法通過上一行的數相加得到,所以可以直接定義,第二點對於每一行最左邊的數或者最右邊的數,可能無法獲取左上方的數或者右上方的數,這樣可以預設左上方的數或者右上方的數為0。
按照這個思路實現的程式碼如下:
class Solution {
public List<List<Integer>> generate(int numRows) {
if(numRows == 0) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> rslt = new ArrayList<List<Integer>>();
List<Integer> row;
for(int i = 0; i < numRows; i++) {
row = new ArrayList<Integer>();
if(i == 0) {
//1、第1行直接賦值
row.add(1);
rslt.add(row);
} else {
for(int j = 0; j <= i; j++) {
//2、第i行就有i個數
if(j - 1 < 0) {
//3、最左邊的數
row.add(0 + rslt.get(i - 1).get(j));
} else if(j >= rslt.get(i - 1).size()) {
//4、最右邊的數
row.add(rslt.get(i - 1).get(j - 1) + 0);
} else {
//5、其餘的數只需要左上方的數和右上方的數相加即可
row.add(rslt.get(i - 1).get(j - 1) + rslt.get(i - 1).get(j));
}
}
rslt.add(row);
}
}
return rslt;
}
}
3、二進位制求和
題目:
我的思路:
這一題我的思路和第一篇中介紹的題目《加一》類似,將兩個字串分別轉換為字元陣列,然後將每一位分別相加,得到結果,如果遇到進位的情況,需要再原有結果上再加1,我們首先來看實現。
按照這個思路實現的程式碼如下:
class Solution {
public String addBinary(String a, String b) {
//1、將字串轉換為字元陣列,並比較得到長度長的陣列和短的陣列
char[] aChars = a.toCharArray();
char[] bChars = b.toCharArray();
char[] longChars;
char[] minChars;
int aLen = aChars.length, bLen = bChars.length, minLen, maxLen;
if(aLen <= bLen) {
minLen = aLen;
maxLen = bLen;
minChars = aChars;
longChars = bChars;
} else {
minLen = bLen;
maxLen = aLen;
minChars = bChars;
longChars = aChars;
}
StringBuffer sb = new StringBuffer();
//2、定義一個布林值用於控制是否進位
boolean fwdPlus = false;
int i = minLen - 1, j = maxLen - 1;
for(int k = 0; k < maxLen; k++) {
//3、以長度較長的陣列為基準,分別遍歷兩個陣列,如果長度短的陣列還未遍歷結束,
//則將長陣列和短陣列對應位置的數相加
if(j >= maxLen - minLen) {
if(minChars[i] == '0' && longChars[j] == '0') {
if(fwdPlus) {
sb.insert(0, 1);
fwdPlus = false;
} else {
sb.insert(0, 0);
fwdPlus = false;
}
} else if(minChars[i] == '0' && longChars[j] == '1') {
if(fwdPlus) {
sb.insert(0, 0);
fwdPlus = true;
} else {
sb.insert(0, 1);
fwdPlus = false;
}
} else if(minChars[i] == '1' && longChars[j] == '0') {
if(fwdPlus) {
sb.insert(0, 0);
fwdPlus = true;
} else {
sb.insert(0, 1);
fwdPlus = false;
}
} else if(minChars[i] == '1' && longChars[j] == '1') {
if(fwdPlus) {
sb.insert(0, 1);
fwdPlus = true;
} else {
sb.insert(0, 0);
fwdPlus = true;
}
}
j--;
i--;
} else {
//4、短陣列遍歷結束,則直接遍歷長陣列剩餘的位數,並根據是否進位來獲得結果
if(longChars[j] == '0') {
if(fwdPlus) {
sb.insert(0, 1);
fwdPlus = false;
} else {
sb.insert(0, 0);
fwdPlus = false;
}
} else if(longChars[j] == '1') {
if(fwdPlus) {
sb.insert(0, 0);
fwdPlus = true;
} else {
sb.insert(0, 1);
fwdPlus = false;
}
}
j--;
}
}
//5、長短陣列全部遍歷結束,如果仍需進位,則在最前面加1
if(fwdPlus) {
sb.insert(0, 1);
}
return sb.toString();
}
}
反思:
- 這題和《加一》有所不同的就是,《加一》中只有一個數組,並且只針對這一個陣列加一,而本題是兩個二進位制陣列相加,因此就需要考慮陣列長短的問題,如果兩個陣列長短不一,不做控制直接遍歷的話,那麼遍歷到最後很可能就出現短陣列越界的問題,因此需要增加一個判斷,只有短陣列還未遍歷結束時,才會將長短陣列的對應元素相加;
- 另外需要注意的一點是,這裡的二進位制字串轉換成字元陣列之後,二進位制的最低位實際上對應的是字元陣列的最高位,最高位實際對應的是字元陣列的最低位,例如“1010”字串轉換成陣列的形式為[1, 0, 1, 0],這個陣列的最低位1實際上是二進位制的最高位,最高位0實際上是二進位制的最低位,因此遍歷字元陣列的時候需要從陣列高位向低位遍歷,這樣才能夠保證計算的準確性,同時每一位計算得到的結果都要寫在字串的第一位,而不是依次向後新增。
4、實現strStr()
題目:
我的思路:
看到這一題第一個想法就是直接呼叫Java中String類的indexOf()方法,一句話就可以完成,也符合題目的要求:
class Solution {
public int strStr(String haystack, String needle) {
return haystack.indexOf(needle);
}
}
但是如果不使用Java原生的方法的話,那該怎麼做呢?我認為可以從第0位開始遍歷字串,每次從haystack中獲取needle長度的字串,判斷這個字串是否和needle長度相同,如果不同則遍歷的索引向後移動一位,如果相同則得到結果。
按照這個思路實現的程式碼如下:
class Solution {
public int strStr(String haystack, String needle) {
if(haystack == null || "".equals(needle)) {
return 0;
}
int len = needle.length(), index = -1;
//1、i只需要遍歷到haystack.length()-neddle.length()即可,防止出現數組越界
for(int i = 0; i + len <= haystack.length(); i++) {
if(needle.equals(haystack.substring(i, i + len))) {
index = i;
break;
}
}
return index;
}
}
5、最長公共字首
題目:
我的思路:
這一題我的思路比較直接,我以字串陣列的第一個字串為基準,從第1位開始依次擷取字串獲得字首,每擷取一次則將字首與其餘的字串進行比較,如果其餘的字串均是以此為字首,則繼續擷取更大的字首,否則得到結果。
按照這個思路實現的程式碼如下:
class Solution {
public String longestCommonPrefix(String[] strs) {
String rslt = "", prefix = "";
if(strs.length > 0) {
for(int i = 1; i <= strs[0].length(); i++) {
//1、獲得第一個字串的字首
prefix = strs[0].substring(0, i);
if(strs.length > 1) {
for(int j = 1; j < strs.length; j++) {
if(!strs[j].startsWith(prefix)) {
//2、如果某個字串不是以此為字首,則得到最長公共字首
break;
} else {
//3、字串以此為字首,並且已經遍歷到字串陣列的最後,
//則可以繼續判斷,獲取更大的公共字首
if(j == strs.length - 1) {
rslt = prefix;
}
}
}
} else {
rslt = prefix;
}
}
}
return rslt;
}
}
6、反轉字串
題目:
我的思路:
這一題比較簡單,有很多種方法可以解決,我當時做的時候採用的是一個比較耗時的方法,就是從最後依次向前遍歷整個字串,從而得到結果。
按照這個思路實現的程式碼如下:
class Solution {
public String reverseString(String s) {
if(s == null) {
return null;
}
if("".equals(s)) {
return "";
}
char[] c = s.toCharArray();
StringBuffer sb = new StringBuffer();
for(int i = c.length - 1; i >= 0; i--) {
sb.append(c[i]);
}
return sb.toString();
}
}
反思:
這道題實際上還有一個比較高效的解法,就是使用雙指標,可以定義兩個指標指向字串的最小索引和最大索引,然後交換兩個指標指向的值,再依次後移最小索引、前移最大索引,直到兩個指標相遇。基於此想法我又修改了我的程式碼:
class Solution {
public String reverseString(String s) {
if(s == null || "".equals(s)) {
return s;
}
char[] sChars = s.toCharArray();
for(int i = 0, j = sChars.length - 1; i < j; i++, j--) {
char tmp = sChars[i];
sChars[i] = sChars[j];
sChars[j] = tmp;
}
return new String(sChars);
}
}