LeetCode刷题记录(三)

LeetCode刷题记录(三)

1、螺旋矩阵

题目:

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、杨辉三角

题目:

LeetCode刷题记录(三)_第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、二进制求和

题目:

LeetCode刷题记录(三)_第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()

题目:

LeetCode刷题记录(三)_第4张图片

我的思路:

看到这一题第一个想法就是直接调用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、最长公共前缀

题目:

LeetCode刷题记录(三)_第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、反转字符串

题目:

LeetCode刷题记录(三)_第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);
    }
}

你可能感兴趣的:(数据结构与算法,LeetCode)