1. 程式人生 > >LeetCode刷題記錄(三)

LeetCode刷題記錄(三)

LeetCode刷題記錄(三)

1、螺旋矩陣

題目:

在這裡插入圖片描述

我的思路:

  1. 我將獲取螺旋矩陣的過程分為四步:先從左往右遍歷矩陣的值,到最右之後再從上往下遍歷,到最下面之後再從右往左遍歷,到最左側之後再從下往上遍歷,這樣依次迴圈,直到遍歷到最後一個值;
  2. 根據這個思路我定義四個變數,分別表示橫向的最小值、最大值和縱向的最小值、最大值,並且可以定義橫向座標和縱向座標,每次遍歷都是橫向或者縱向座標從小到大或從大到小的移動;
  3. 這裡需要注意一點的就是,每遍歷一次到最後的時候,對應最大最小值都需要進行調整,例如在從左到右遍歷到最右邊之後,縱向的最小值就需要加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、楊輝三角

題目:

在這裡插入圖片描述

我的思路:

  1. 這一題思路較為簡潔,根據楊輝三角的定義,每一行中每個數都是左上方的數和右上方的數相加的結果,並且第n行就有n個數,因此在二維陣列中,第n行的第i個數實際上就是n-1行的第i-1和第i個數相加的結果;
  2. 但是這裡有兩點需要注意一下,首先第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();
    }
}

反思:

  1. 這題和《加一》有所不同的就是,《加一》中只有一個數組,並且只針對這一個陣列加一,而本題是兩個二進位制陣列相加,因此就需要考慮陣列長短的問題,如果兩個陣列長短不一,不做控制直接遍歷的話,那麼遍歷到最後很可能就出現短陣列越界的問題,因此需要增加一個判斷,只有短陣列還未遍歷結束時,才會將長短陣列的對應元素相加;
  2. 另外需要注意的一點是,這裡的二進位制字串轉換成字元陣列之後,二進位制的最低位實際上對應的是字元陣列的最高位,最高位實際對應的是字元陣列的最低位,例如“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);
    }
}