一、 Introduction to Array
- Find Pivot Index
思路:
朴素想法是遍历每一个数,分别计算其左右的和是否相等,这样的时间复杂度是O(n^2)。高级一点的想法是先计算序列的和记为sum,再遍历每一个数p,记这个数左边的序列和为leftSum,只需判断leftSum是否和sum - leftSum - p相等即可。
代码:
public static int pivotIndex(int[] nums) {
int sum = 0, leftsum = 0;
for (int x : nums) sum += x;
for (int i = 0; i < nums.length; ++i) {
if (leftsum == sum - leftsum - nums[i]) return i;
leftsum += nums[i];
}
return -1;
}
- Largest Number At Least Twice of Others
思路:
遍历数组寻找最大的数和次大的数,若当前数大于最大的数,则令最大的数为次大的数,当前数为最大的数;若当前数大于次大的数小于最大的数,则令当前数为次大的数。
代码:
public static int dominantIndex(int[] nums) {
if (nums.length < 2) return 0;
int firstIndex = 0, secondIndex = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > nums[firstIndex]) {
secondIndex = firstIndex;
firstIndex = i;
} else if (nums[i] >= nums[secondIndex]) {
secondIndex = i;
}
}
if (nums[firstIndex] - nums[secondIndex] * 2 >= 0) return firstIndex;
return -1;
}
- Plus One
思路:
比较简单,用数组模拟加法的运算过程,注意处理最大位大于9的情况即可。
代码:
public static int[] plusOne(int[] digits) {
digits[digits.length - 1]++;
for (int i = digits.length - 1; i > 0; i--) {
if (digits[i] > 9) {
digits[i - 1]++;
digits[i] -= 10;
}
}
if (digits[0] > 9) {
int[] newArray = new int[digits.length + 1];
newArray[0] = 1;
digits[0] -= 10;
for (int i = 0; i < digits.length - 1; i++ ) {
newArray[i + 1] = digits[i];
}
return newArray;
}
return digits;
}
二、 Introduction to 2D Array
- Diagonal Traverse
思路:
一般的二维数组花式遍历题目,基本解决方式都是精确控制遍历方向和边界,但这种方式很容易出错,一旦出错就要花费大量时间调试。这道题目比较特殊,有一种简便方法。因为遍历是按照斜线方向,而在每一条线上横纵坐标的和是一个定值。因此,对于一个m*n的矩阵,可以用一个大小为m+n的ArrayList数组存储每一条斜边上的序列。在输出结果的时候,偶数列逆向奇数列正向输出即可。这种方式可以一口气写完代码并直接AC,不必费心去精心微调边界。
代码:
public static int[] findDiagonalOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
int rowLen = matrix.length;
int colLen = matrix[0].length;
ArrayList[] data = new ArrayList[rowLen + colLen];
for (int i = 0; i < data.length; i++) {
data[i] = new ArrayList<>();
}
for (int i = 0; i < rowLen; i++) {
for (int j = 0; j < colLen; j++) {
data[i+j].add(matrix[i][j]);
}
}
int[] result = new int[rowLen * colLen];
int count = 0;
for (int i = 0; i < data.length; i++) {
if (i % 2 == 0) {
for (int j = data[i].size() - 1; j >= 0; j--) {
result[count++] = data[i].get(j);
}
} else {
for (int j = 0; j < data[i].size(); j++) {
result[count++] = data[i].get(j);
}
}
}
return result;
}
- Spiral Matrix
思路:
对于这种螺旋遍历的方法,重要的是要确定上下左右四条边的位置,那么初始化的时候,上边up就是0,下边down就是m-1,左边left是0,右边right是n-1。然后我们进行while循环,先遍历上边,将所有元素加入结果result,然后上边下移一位,如果此时上边大于下边,说明此时已经遍历完成了,直接break。同理对于下边,左边,右边,依次进行相对应的操作,这样就会使得坐标很有规律,并且不易出错
代码:
public static List spiralOrder(int[][] matrix) {
List result = new ArrayList();
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return result;
}
int boundLeft = 0;
int boundRight = matrix[0].length - 1;
int boundTop = matrix.length - 1;
int boundBottom = 0;
while (true) {
for ( int i = boundLeft; i <= boundRight; i++) {
result.add(matrix[boundBottom][i]);
}
if (++boundBottom > boundTop) break;
for (int i = boundBottom; i <= boundTop; i++) {
result.add(matrix[i][boundRight]);
}
if (--boundRight < boundLeft) break;
for (int i = boundRight; i >= boundLeft; i--) {
result.add(matrix[boundTop][i]);
}
if (--boundTop < boundBottom) break;
for (int i = boundTop; i >= boundBottom; i--) {
result.add(matrix[i][boundLeft]);
}
if (++boundLeft > boundRight) break;
}
return result;
}
- Pascal's Triangle
思路:
这道题比较简单,不赘述。
代码:
public List> generate(int numRows) {
List> result = new ArrayList<>(numRows);
for (int i = 0; i < numRows; i++) {
result.add(new ArrayList<>(i + 1));
for (int j = 0; j < i + 1; j++) {
if (j == 0 || j == i) {
result.get(i).add(j, 1);
} else {
result.get(i).add(j, result.get(i - 1).get(j) + result.get(i - 1).get(j - 1));
}
}
}
return result;
}
三、Introduction to String
- Add Binary
思路:
用数组模拟加法运算过程的典型例题,考虑到两个串长度不一定相等和结果的长度可能比两个串长度都大即可。
代码:
public String addBinary(String a, String b) {
StringBuilder result = new StringBuilder();
int i = a.length() - 1, j = b.length() -1, carry = 0, sum = 0;
while (i >= 0 || j >= 0) {
sum = carry;
if (j >= 0) sum += b.charAt(j--) - '0';
if (i >= 0) sum += a.charAt(i--) - '0';
result.append(sum % 2);
carry = sum / 2;
}
if (carry != 0) result.append(carry);
return result.reverse().toString();
}
- Implement strStr()
思路:
这道题比较简单,不赘述。
代码:
public int strStr(String haystack, String needle) {
if (needle.length() == 0) return 0;
if (needle.length() > haystack.length()) return -1;
for (int i = 0; i < haystack.length() - needle.length() + 1; i++) {
for (int j = 0; j < needle.length(); j++) {
if (haystack.charAt(i + j) != needle.charAt(j)) {
break;
}
if (j == needle.length() - 1) {
return i;
}
}
}
return -1;
}
- Longest Common Prefix
思路:
可以先将原字符串数组排序,然后取第一个和最后一个进行比较即可。注意,字符串排序不是按长短而是字母序。
代码:
public String longestCommonPrefix(String[] strs) {
StringBuilder result = new StringBuilder();
if (strs != null && strs.length > 0) {
Arrays.sort(strs);
char[] a = strs[0].toCharArray();
char[] b = strs[strs.length - 1].toCharArray();
for (int i = 0; i < a.length; i++) {
if (b.length > i && b[i] == a[i]) {
result.append(b[i]);
} else {
return result.toString();
}
}
}
return result.toString();
}
四、Two-Pointer Technique
这是很实用的一个小技巧,有两个应用场景:一是Iterate the array from two ends to the middle,二是One slow-runner and one fast-runner at the same time.
- Reverse String
思路:
双指针一前一后,依次swap即可。
代码:
public String reverseString(String s) {
if (s.length() == 0) return s;
char[] a = s.toCharArray();
int i = 0, j = a.length - 1;
char tmp;
while (i < j) {
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
}
return new String(a);
}
- Array Partition I
思路:使原数组变得有序,然后取下标为偶数的数的和即可。注意,普通排序是O(nlgn)时间,可以使用桶排序,空间换时间。
代码:
public static int arrayPairSum(int[] nums) {
int[] map = new int[20001];
for (int i = 0; i < nums.length; i++) {
map[nums[i] + 10000]++;
}
int sum = 0;
boolean tag = true;
for (int i = 0; i < map.length; i++) {
while (map[i] > 0) {
if (tag) {
sum += (i - 10000);
}
tag = !tag;
map[i]--;
}
}
return sum;
}
- Two Sum II - Input array is sorted
思路:
用双指针,从两边往中间逼近即可,比较简单,不赘述。
代码:
public int[] twoSum(int[] numbers, int target) {
if (numbers == null || numbers.length < 1) return null;
int i = 0, j = numbers.length - 1;
while (i < j) {
int x = numbers[i] + numbers[j];
if (x < target) {
++i;
} else if (x > target) {
--j;
} else {
return new int[]{i + 1, j + 1};
}
}
return null;
}
- Remove Element
思路:
比较简单,不赘述。
代码:
public int removeElement(int[] nums, int val) {
int k = 0;
for (int i = 0; i < nums.length; ++i) {
if (nums[i] != val) {
nums[k] = nums[i];
k++;
}
}
return k;
}
- Max Consecutive Ones
思路:
比较简单,不赘述。
代码:
public int findMaxConsecutiveOnes(int[] nums) {
int result = 0;
int count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 1) {
count++;
result = Math.max(count, result);
}
else count = 0;
}
return result;
}
- Minimum Size Subarray Sum
思路:
首先注意到所给的序列都是正数,这是O(n)解法的基础。解法的基本思路是,使用双指针i和j,初始都设置为0,然后从i=0开始累加,当sum大于等于s时,说明当前i-j+1这一段序列和是大于等于s的,然后开始从这段序列里不断减去开头的数字(即j指向的数字)直到sum小于2为止,在这个过程中记录最小的i-j+1即可。
代码:
public static int minSubArrayLen(int s, int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int count = Integer.MAX_VALUE;
int sum = 0;
int i = 0, j = 0;
while (i < nums.length) {
sum += nums[i];
while (sum >= s) {
count = Math.min(count, i - j + 1);
sum -= nums[j++];
}
i++;
}
return count == Integer.MAX_VALUE ? 0 : count;
}
五、Conclusion
- Rotate Array
思路:
将序列反转三次即可,第一次反转[0,n],第二次反转[0,k],第三次反转[k+1,n],注意k可能大于n,故k要取值为k=k%n。
代码:
public void rotate(int[] nums, int k) {
if(nums == null || nums.length < 2){
return;
}
k = k % nums.length;
reverse(nums, 0, nums.length - k - 1);
reverse(nums, nums.length - k, nums.length - 1);
reverse(nums, 0, nums.length - 1);
}
private void reverse(int[] nums, int i, int j){
int tmp = 0;
while(i < j){
tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
i++;
j--;
}
}
- Pascal's Triangle II
思路:
比较简单,不赘述。
代码:
public List getRow(int rowIndex) {
List> result = new ArrayList<>(rowIndex + 1);
for (int i = 0; i < rowIndex + 1; i++) {
result.add(new ArrayList<>(i + 1));
for (int j = 0; j < i + 1; j++) {
if (j == 0 || j == i) {
result.get(i).add(j, 1);
} else {
result.get(i).add(j, result.get(i - 1).get(j) + result.get(i - 1).get(j - 1));
}
}
}
return result.get(rowIndex);
}
- Reverse Words in a String
思路:
学会充分利用java中各种API,你就能三行代码秒解这道题。
代码:
public String reverseWords(String s) {
String[] words = s.trim().split(" +");
Collections.reverse(Arrays.asList(words));
return String.join(" ", words);
}
- Reverse Words in a String III
思路:
学会充分利用java中各种API,你就能三行代码秒解这道题。
代码:
public static String reverseWords(String s) {
String[] str = s.split(" ");
for (int i = 0; i < str.length; i++) str[i] = new StringBuilder(str[i]).reverse().toString();
StringBuilder result = new StringBuilder();
for (String st : str) result.append(st + " ");
return result.toString().trim();
}
- Remove Duplicates from Sorted Array
思路:
比较简单,不赘述。
代码:
public int removeDuplicates(int[] nums) {
if (0 == nums.length) {
return 0;
}
int i = 0, j = 0;
while (i < nums.length) {
if (nums[i] != nums[j]) {
nums[++j] = nums[i];
}
i++;
}
return j + 1;
}
- Move Zeroes
思路:
比较简单,不赘述。
代码:
public void moveZeroes(int[] nums) {
int i = 0, j = 0;
while (i < nums.length) {
if (nums[i] != 0) {
nums[j++] = nums[i];
}
i++;
}
while (j < nums.length) {
nums[j++] = 0;
}
}