Array

448.找出数组中所有消失的数

要求:整型数组取值为 1 ≤ a[i] ≤ n,n是数组大小,一些元素重复出现,找出[1,n]中没出现的数,实现时时间复杂度为O(n),并不占额外空间

思路1:(discuss)用数组下标标记未出现的数,如出现4就把a[3]的数变成负数,当查找时判断a的正负就能获取下标

tips:注意数组溢出

  1. public List<Integer> findDisappearedNumbers(int[] nums) {
  2. List<Integer> disList = new ArrayList<Integer>();
  3.  
  4. //用数组下标来记录出现过的数
  5. for (int i = 0; i < nums.length; i++) {
  6. int item = Math.abs(nums[i]) - 1;
  7. //判断是否已经出现过
  8. if (nums[item] > 0) {
  9. nums[item] = -nums[item];
  10. }
  11. }
  12.  
  13. //把仍然是正数所对应的下标加入数组列表中
  14. for (int i = 0; i < nums.length; i++) {
  15. if (nums[i] > 0) {
  16. disList.add(i + 1);
  17. }
  18. }
  19. return disList;
  20. }

思路2:(discuss)下标标记数,但把出现过的数用作下标,然后此下标对应的数+n

  1. public List<Integer> findDisappearedNumbers(int[] nums) {
  2. List<Integer> disList = new ArrayList<Integer>();
  3. int n = nums.length;
  4. //用数组下标来记录出现过的数
  5. for (int i = 0; i < nums.length; i++) {
  6. nums[(nums[i] - 1) % n] += n;
  7. }
  8.  
  9. //把a[i]小于n时的i+1加入数组列表中
  10. for (int i = 0; i < nums.length; i++) {
  11. if (nums[i] <= n) {
  12. disList.add(i + 1);
  13. }
  14. }
  15. return disList;
  16. }

思路3:(discuss)整理数组,目的就是把数字与下标对应,整理后下标与数字不对应就证明此数消失,此方法称为数组原地实现(只改变顺序不改变值)

  1. public List<Integer> findDisappearedNumbers(int[] nums) {
  2. for (int i = 0; i < nums.length; i++) {
  3. while (nums[i] != i + 1 && nums[i] != nums[nums[i] - 1]) {
  4. int tmp = nums[i];
  5. nums[i] = nums[tmp - 1];
  6. nums[tmp - 1] = tmp;
  7. }
  8. }
  9. List<Integer> res = new ArrayList<Integer>();
  10. for (int i = 0; i < nums.length; i++) {
  11. if (nums[i] != i + 1) {
  12. res.add(i + 1);
  13. }
  14. }
  15. return res;
  16. }

2016-12-14

442.找出数组重复的数

要求与448类似

思路1:(自写一次AC)还是用下标标记,每出现一次,相应下标的数+n,检索时搜大于2n的就重复两次,还能检测出现次数大于两次的情况

  1. public List<Integer> findDuplicates(int[] nums) {
  2. List<Integer> dupList = new ArrayList<Integer>();
  3. if (nums == null || nums.length == 0) {
  4. return dupList;
  5. }
  6. int n = nums.length;
  7. //出现一次就把相应下标所指整数+n
  8. for (int i = 0; i < n; i++) {
  9. nums[(nums[i] - 1) % n] += n;
  10. }
  11. for (int i = 0; i < n; i++) {
  12. if (nums[i] > 2*n) {
  13. dupList.add(i + 1);
  14. }
  15. }
  16. return dupList;
  17. }

思路2:(discuss)用正负法,巧用if的位置,但只适合出现两次,注意Math.abs用了两次(另:再遍历一次+Math.abs可恢复原数据)

  1. public List<Integer> findDuplicates(int[] nums) {
  2. List<Integer> dupList = new ArrayList<Integer>();
  3. if (nums == null || nums.length == 0) {
  4. return dupList;
  5. }
  6. for (int i = 0; i < nums.length; i++) {
  7. //获得原下标
  8. int item = Math.abs(nums[i]) - 1;
  9. //只有出现过一次并且item一样才会满足
  10. if (nums[item] < 0) {
  11. dupList.add(Math.abs(item + 1));
  12. }
  13. nums[item] = -nums[item];
  14. }
  15. return dupList;
  16. }

2016-12-14

414.第三大的数

要求:给出非空整型数组,返回第三大的数;若不存在,则返回最大数。时间复杂度要为O(n)

思路1: (discuss)尝试多次才成功,定义前三大数,并用continue跳出循环排除重复的情况,用右移插入数,判断好是否到第三大

warn:想好如何定义前三大的数,若定义为Integer.MIN_VALUE,输入为-2147483648时出错,可以投机定义为long类型

  1. public int thirdMax(int[] nums) {
  2. if (nums == null || nums.length == 0) {
  3. return 0;
  4. }
  5. //创建三个变量存储前三大的数
  6. long max, mid, min;
  7. max = mid = min = Long.MIN_VALUE;
  8. //判断第三个数是否赋值
  9. int count = 0;
  10.  
  11. for (int x : nums) {
  12. //防止重复输入,continue跳出本次循环
  13. if (x == max || x == mid) {
  14. continue;
  15. }
  16. //x大于max则三个变量都向右移
  17. if (x > max) {
  18. min = mid;
  19. mid = max;
  20. max = x;
  21. count++;
  22. } else if (x > mid) {
  23. min = mid;
  24. mid = x;
  25. count++;
  26. } else if (x >= min) {
  27. min = x;
  28. count++;
  29. }
  30. }
  31. if (count >= 3) {
  32. return (int)min;
  33. }
  34. return (int)max;
  35. }

思路2: (discuss)使用TreeSet特性,有序且不能重复,当size()大于3时去掉第一位,小于3时输出最大值,时间复杂度为O(nlog3)

  1. public int thirdMax(int[] nums) {
  2. TreeSet<Integer> set = new TreeSet<Integer>();
  3. for (int num : nums) {
  4. set.add(num);
  5. //超过3个则去除
  6. if (set.size() > 3) {
  7. set.remove(set.first());
  8. }
  9. }
  10. //少于3个输出最大值
  11. return set.size() < 3 ? set.last() : set.first();

2016-12-15

283.移动零

要求:把数组中的0都放在末尾,且保持其他数的相对位置

思路1: (自写一次AC)从末尾开始判断是否为0,是0就与旁边交换,直到旁边的不是0为止(24ms)

tips:注意超过数组长度

  1. public void moveZeroes(int[] nums) {
  2. for (int i = nums.length - 1; i >= 0; i--) {
  3. if (nums[i] == 0) {
  4. int j = i;
  5. while (j < nums.length - 1 && nums[j + 1] != 0) {
  6. nums[j] = nums[j + 1];
  7. nums[j + 1] = 0;
  8. j++;
  9. }
  10. }
  11. }
  12. }

思路2: (discuss)把非0数放在头,其余位置补齐0(0ms)

  1. public void moveZeroes(int[] nums) {
  2. int nonzero = 0;
  3. for (int num : nums) {
  4. if (num != 0) {
  5. nums[nonzero] = num;
  6. nonzero++;
  7. }
  8. }
  9. //补全0
  10. while (nonzero < nums.length) {
  11. nums[nonzero] = 0;
  12. nonzero++;
  13. }
  14. }

思路3: (discuss)一指针一直动判断是否为0,另一指针停留在非0头部的下一位处等待非0数的出现,从而进行交换(0ms)

  1. public void moveZeroes(int[] nums) {
  2. //指向非0数的指针
  3. int j = 0;
  4. for (int i = 0; i < nums.length; i++) {
  5. if (nums[i] != 0) {
  6. int temp = nums[j];
  7. nums[j] = nums[i];
  8. nums[i] = temp;
  9. j++;
  10. }
  11. }
  12. }

2016-12-15

268.消失的数

要求:数组包含n个数,从0...n取值,找出缺失的那个值,时间复杂度要线性,空间复杂度恒定

思路1: (九章答案+自己想)把下标和值对应起来,等于是排序,然后判断下标和值不同的就是缺失项(对应448的思路3)

warn:记得用while,直到下标和值能对应,由于没有n这个下标,所以要排除这种情况,前面的都没缺失就返回n

  1. public int missingNumber(int[] nums) {
  2. int n = nums.length;
  3. for (int i = 0; i < n; i++) {
  4. //先排除n的情况,因为超出了长度
  5. while (nums[i] != i && nums[i] < n) {
  6. int t = nums[i];
  7. nums[i] = nums[t];
  8. nums[t] = t;
  9. }
  10. }
  11. for (int i = 0; i < n; i++) {
  12. if (nums[i] != i) {
  13. return i;
  14. }
  15. }
  16. return n;
  17. }

思路2: (discuss)利用数学,比如求和,下标与值求异或(a ^ b ^ b = a,剩下单个的)

求和法:不缺失数组和(n+1项)减去缺失数组和(n项)

  1. public int missingNumber(int[] nums) {
  2. int n = nums.length;
  3. int s = n * (n + 1) / 2;
  4. int sum = 0;
  5. for (int i = 0; i < n; i++) {
  6. sum += nums[i];
  7. }
  8. return s - sum;
  9. }

异或法:先设xor为n,数组有n时与n抵消可以输出缺失那一项,无n时就代表缺少n

  1. public int missingNumber(int[] nums) {
  2. //下标没有n项,所以把xor先设为n
  3. int xor = nums.length;
  4. for (int i = 0; i < nums.length; i++) {
  5. xor = xor ^ i ^ nums[i];
  6. }
  7. //数组的值有n时与原始xor抵消,输出缺失的另一项,无n就输出n
  8. return xor;
  9. }

2016-12-15

238.排除自己后的乘积

要求:output数组上每个位置的值等于由原数组num除该位置外的所有数的乘积,不能使用除法,复杂度为O(n)

思路: (discuss)分成位置的两边相乘

tips:从左往右乘的时候,令output[0]为1,此后就可以把output[i - 1]作为是之前i - 1项的积,可重复利用;而从右往左时,第一个数已经是最终结果,而第二个数只需要乘一次...所以需要新的累积变量right

        (其实也可以两个方向都用累积变量存储前面项的乘积,只不过左往右乘的时候可应用输出数组本身作为累积,更省空间)

  1. public int[] productExceptSelf(int[] nums) {
  2. int n = nums.length;
  3. int[] output = new int[n];
  4. output[0] = 1;
  5.  
  6. //从i=1开始,先算nums[i]左边数的乘积
  7. for (int i = 1; i < n; i++) {
  8. output[i] = output[i - 1] * nums[i - 1];
  9. }
  10. //新的累加值,算右边的乘积
  11. int right = 1;
  12. for (int i = n - 1; i >= 0; i--) {
  13. output[i] *= right;
  14. right *= nums[i];
  15. }
  16. return output;
  17. }

2016-12-15

229.多数元素(众数)II

要求:在大小为n的数组中找出出现次数大于n / 3次的元素,用线性时间和O(1)空间

思路: (经典算法)“摩尔投票法”,这里的含义是先设置两个候选数,相同的时候就计1票,不同的时候就扣1票,当票数为0时,就换另一个候选数,并且计第1票;选好候选数之后,就真正计算各自的票数

tips:背下来思路...1、设候选数、计票器 2、确定候选数 3、真正计票 4、判断是否满足条件

warn:考虑空数组,数组长度为1,两个数相等的情况!!!

  1. public List<Integer> majorityElement(int[] nums) {
  2. List<Integer> res = new ArrayList<Integer>();
  3. int n = nums.length;
  4. if (nums == null || n == 0) {
  5. return res;
  6. }
  7. if (n == 1) {
  8. res.add(nums[0]);
  9. return res;
  10. }
  11.  
  12. //设2个候选数,2个对应的计票器
  13. int number1, number2, count1, count2;
  14. number1 = number2 = nums[0];
  15. count1 = count2 = 0;
  16.  
  17. //挑选候选数
  18. for (int i = 0; i < n; i++) {
  19. if (nums[i] == number1) {
  20. count1++;
  21. } else if (nums[i] == number2) {
  22. count2++;
  23. } else if (count1 == 0) { //count减为0就换候选数
  24. number1 = nums[i];
  25. count1++;
  26. } else if (count2 == 0) {
  27. number2 = nums[i];
  28. count2++;
  29. } else {
  30. count1--;
  31. count2--;
  32. }
  33. }
  34.  
  35. //清空票数,计算两个候选数的真正票数
  36. count1 = count2 = 0;
  37. for (int i = 0; i < n; i++) {
  38. if (nums[i] == number1) {
  39. count1++;
  40. }
  41. if (nums[i] == number2) {
  42. count2++;
  43. }
  44. }
  45.  
  46. //判断票数是否达到n/3以上
  47. if (count1 > n / 3) {
  48. res.add(number1);
  49. }
  50. //考虑两数相等的情况
  51. if (count2 > n / 3 && number2 != number1) {
  52. res.add(number2);
  53. }
  54. return res;
  55. }

另:据说使用Map也行,但是空间复杂度比较大

2016-12-15

228.总结范围数

要求:数组不重复,出现连续的数列就用->表示,如0, 1, 2就变成了0->2,单个数不需改变

思路: (自写修改后AC)判断nums[i+1] == nums[i],若等于则while循环获得范围结尾处,不等于就直接输出当前。

warn:注意数组溢出,注意数组溢出,注意数组溢出!!!考虑数组长度0,1,以及n-1位置上的情况

  1. public List<String> summaryRanges(int[] nums) {
  2. List<String> res = new ArrayList<String>();
  3. int n = nums.length;
  4. if (nums == null || n == 0) {
  5. return res;
  6. }
  7. if (n == 1) {
  8. String s = String.valueOf(nums[0]);
  9. res.add(s);
  10. return res;
  11. }
  12. for (int i = 0; i < n - 1; i++) {
  13. if (nums[i + 1] == nums[i] + 1) {
  14. int j = i;
  15. while (i < n - 1 && nums[i + 1] == nums[i] + 1) {
  16. i++;
  17. }
  18. String s = nums[j] + "->" + nums[i];
  19. res.add(s);
  20. } else {
  21. String s = String.valueOf(nums[i]);
  22. res.add(s);
  23. }
  24. }
  25. if (nums[n - 1] != nums[n - 2] + 1) {
  26. String s = String.valueOf(nums[n - 1]);
  27. res.add(s);
  28. }
  29. return res;
  30. }

2016-12-16

219.包含重复II

要求:给出数组和整数k,求是否在最大间隔j - i = k以内有重复数出现

思路1: (查HashSet用法后自写一次AC)根据HashSet特性,不能有重复数插入,让set长度为k + 1(头尾下标间隔为k,总共包含k + 1个数)

tips:超过长度删除数据时,虽set无序,但可根据输入数组的有序,来确定删除的是哪个数(输入nums[i]对应删除nums[i - k - 1])

  1. public boolean containsNearbyDuplicate(int[] nums, int k) {
  2. if (nums == null || nums.length == 0 || k == 0) {
  3. return false;
  4. }
  5. Set<Integer> set = new HashSet<Integer>();
  6. for (int i = 0; i <nums.length; i++) {
  7. //set长度超过k,则删除当前第1个
  8. if (i > k) {
  9. //set无序,但可利用数组有序
  10. set.remove(nums[i - 1 - k]);
  11. }
  12. //插入不了证明重复
  13. if (!set.add(nums[i])) {
  14. return true;
  15. }
  16. }
  17. return false;
  18. }

思路2: (discuss)根据HashMap特性键不能重复,把数组的值当作map键,数组的下标当作map值,求长度时比较map值(对应数组下标)

tips:先判断是否已经存在再输入!!

  1. public boolean containsNearbyDuplicate(int[] nums, int k) {
  2. if (nums == null || nums.length == 0 || k == 0) {
  3. return false;
  4. }
  5. Map<Integer, Integer> map = new HashMap();
  6. for (int i = 0; i < nums.length; i++) {
  7. if (map.containsKey(nums[i])) {
  8. //get(nums[i])是找之前的
  9. if (i - map.get(nums[i]) <= k) {
  10. return true;
  11. }
  12. }
  13. //调转键与值
  14. map.put(nums[i], i);
  15. }
  16. return false;
  17. }

2016-12-16

217.包含重复

要求:找出数组是否有重复数

思路: (自写map和set的)与219一样,比较简单,只放了map代码

  1. public boolean containsDuplicate(int[] nums) {
  2. if (nums == null || nums.length == 0) {
  3. return false;
  4. }
  5. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  6. for (int i = 0; i < nums.length; i++) {
  7. if (map.containsKey(nums[i])) {
  8. return true;
  9. }
  10. map.put(nums[i], i);
  11. }
  12. return false;
  13. }

2016-12-16

216.组合和III

要求:用k个1-9的数,求所有加起来等于n的集合,每个集合中的元素不重复

思路: (经典算法)“BackTracking回溯算法”,满足条件就添加,不满足就继续递归

warn:背步骤...具体还没理解如何return,如何递归  backTracking:1、判断是否满足条件,是就添加并返回 2、循环:2.1、添加新循环元素 2.2、递归表达式 2.3、弹出最后一项

  1. public List<List<Integer>> combinationSum3(int k, int n) {
  2. List<List<Integer>> ans = new ArrayList<>();
  3. backTracking(ans, new ArrayList<Integer>(), k, 1, n);
  4. return ans;
  5. }
  6.  
  7. private void backTracking(List<List<Integer>> ans, List<Integer> comb, int k, int start, int n) {
  8. //数组中有k个数并相加为n就返回
  9. if (comb.size() == k && n == 0) {
  10. List<Integer> real = new ArrayList<Integer>(comb);
  11. ans.add(real);
  12. return;
  13. }
  14. for (int i = start; i <= 9; i++) {
  15. //添加新数
  16. comb.add(i);
  17. //递归
  18. backTracking(ans, comb, k, i+1, n-i);
  19. //弹出最后一位
  20. comb.remove(comb.size() - 1);
  21. }
  22. }

2016-12-17

209.最小子数组之和

要求:给出含有n个正数的数组,用最短的子数组各项加起来能够大于等于给定的正数s,若找不到则返回0

思路1: (discuss)“Two Pointers”用两个指针和一个记录最短长度的min,首先第一个指针用来叠加,第二个指针用来大于s时减去前面的数,并且更新最短长度min,时间复杂度为O(n)(虽然用了两层while)

  1. public int minSubArrayLen(int s, int[] nums) {
  2. if (nums == null || nums.length == 0) {
  3. return 0;
  4. }
  5. int i = 0, j = 0, sum = 0, min = Integer.MAX_VALUE;
  6. while (i < nums.length) {
  7. sum += nums[i++];
  8. while (sum >= s) {
  9. //超过s就减去前面一位,保留最短长度
  10. min = Math.min(min, i - j);
  11. sum -= nums[j++];
  12. }
  13. }
  14. return min == Integer.MAX_VALUE ? 0 : min;
  15. }

思路2:(discuss)叠加和法

第一个循环先求出sums数组,其中sums[i] = sums[i - 1] + nums[i - 1],此数组i项代表前i项的累计和

第二个循环用二分法查找sums[i]+s的位置end,即代表sums[end] - sums[i] = s,此时子数组长度是end-i,复杂度为O(nlogn)

  1. public int minSubArrayLen(int s, int[] nums) {
  2. int[] sums = new int[nums.length + 1];
  3. //求前i项和
  4. for (int i = 1; i < sums.length; i++) {
  5. sums[i] = sums[i - 1] + nums[i - 1];
  6. }
  7. int minLen = Integer.MAX_VALUE;
  8. for (int i = 0; i < sums.length; i++) {
  9. //二分法找累计和的差值为s的位置end
  10. int end = binarySearch(i + 1, sums.length - 1, sums[i] + s, sums);
  11. if (end == sums.length) break;
  12. if (end - i < minLen) minLen = end - i;
  13. }
  14. return minLen == Integer.MAX_VALUE ? 0 : minLen;
  15. }
  16.  
  17. private int binarySearch(int lo, int hi, int key, int[] sums) {
  18. while (lo <= hi) {
  19. int mid = (lo + hi) / 2;
  20. if (sums[mid] >= key){
  21. hi = mid - 1;
  22. } else {
  23. lo = mid + 1;
  24. }
  25. }
  26. return lo;
  27. }

2016-12-17

189.旋转数组

要求:旋转n长度的数组,位数为k,用三种以上方法,其中最好消耗O(1)额外空间(即原地)

思路1: (九章思路+自写)假如原数组为1234567,旋转度为3,变成5671234,就从第4、5位中间(nums.length - k处)分割,进行三次首尾反转,前面1234首尾反转为4321,后面变成765,再统一首尾反转一次

warn:注意k大于数组长度的情况,用k %= nums.length解决

  1. public void rotate(int[] nums, int k) {
  2. if (nums == null || nums.length == 0) {
  3. return;
  4. }
  5. //防止k大于数组长度
  6. k %= nums.length;
  7. //由nums.length - k分割,进行三次首尾反转
  8. reverse(nums, 0, nums.length - k - 1);
  9. reverse(nums, nums.length - k, nums.length - 1);
  10. reverse(nums, 0, nums.length - 1);
  11. }
  12. private void reverse(int[] nums, int start, int end) {
  13. for (int i = start, j = end; i < j; i++, j--) {
  14. int temp = nums[i];
  15. nums[i] = nums[j];
  16. nums[j] = temp;
  17. }
  18. }

思路2: (自写一次AC)创建另一数组存储,注意位置对应关系就行,由于无返回值,最后要重新赋给原数组

  1. public void rotate(int[] nums, int k) {
  2. int n = nums.length;
  3. if (nums == null || n == 0) {
  4. return;
  5. }
  6. k %= n;
  7. int[] res = new int[n];
  8. for (int i = 0; i < k; i++) {
  9. res[i] = nums[n - k + i];
  10. }
  11. for (int i = k; i < n; i++) {
  12. res[i] = nums[i - k];
  13. }
  14. for (int i = 0; i < n; i++) {
  15. nums[i] = res[i];
  16. }
  17. }

思路3: (discuss)用数学方法(最大公因数GCD)计算反转次数,比较复杂,还没理解

  1. public void rotate(int[] nums, int k) {
  2. if (nums.length <= 1) {
  3. return;
  4. }
  5. //step each time to move
  6. int step = k % nums.length;
  7. //find GCD between nums length and step
  8. int gcd = findGcd(nums.length, step);
  9. int position, count;
  10.  
  11. //gcd path to finish movie
  12. for (int i = 0; i < gcd; i++) {
  13. //beginning position of each path
  14. position = i;
  15. //count is the number we need swap each path
  16. count = nums.length / gcd - 1;
  17. for (int j = 0; j < count; j++) {
  18. position = (position + step) % nums.length;
  19. //swap index value in index i and position
  20. nums[i] ^= nums[position];
  21. nums[position] ^= nums[i];
  22. nums[i] ^= nums[position];
  23. }
  24. }
  25. }
  26.  
  27. public int findGcd(int a, int b) {
  28. return (a == 0 || b == 0) ? a + b : findGcd(b, a % b);
  29. }
  1. Here use a example input array [1,2,3,4,5,6,7,8] (n = 8) to explain:
  2.  
  3. 1.suppose k = 3:
  4.  
  5. GCD = gcd(3,8) = 1, which means there is only one path.
  6.  
  7. Count = (n / GCD) - 1 = 7, which means we need 7 swaps to finish the path. (actually for a path have x element, we need x - 1 swaps)
  8.  
  9. Then we can simulate the process of the algorithm,
  10.  
  11. path0(each time swap index0 element and indexPosition element):
  12.  
  13. [1,2,3,4,5,6,7,8] (position = 3) -> [4,2,3,1,5,6,7,8] (position = 6) -> [7,2,3,1,5,6,4,8](position = 1) -> [2,7,3,1,5,6,4,8](position = 4) -> [5,7,3,1,2,6,4,8](position = 7) -> [8,7,3,1,2,6,4,5](position = 2) -> [3,7,8,1,2,6,4,5](position = 5) -> [6,7,8,1,2,3,4,5] -> finished, total 7 times swap. Final result [6,7,8,1,2,3,4,5]
  14.  
  15. 2.suppose k = 2:
  16.  
  17. Similary, GCD = 2, which means there are 2 paths.
  18.  
  19. count = 3, which means we need 3 swaps to finish each path.
  20.  
  21. Give the process:
  22.  
  23. path0(swap index0 and position element):
  24.  
  25. [1,2,3,4,5,6,7,8](position = 2) -> [3,2,1,4,5,6,7,8](position = 4) ->[5,2,1,4,3,6,7,8](position = 6) -> [7,2,1,4,3,6,5,8] -> path0 finished
  26.  
  27. Then we continue processing path1(swap index1 and position element):
  28.  
  29. [7,2,1,4,3,6,5,8](position = 3) -> [7,4,1,2,3,6,5,8](position = 5) -> [7,6,1,2,3,4,5,8](position = 7) ->[7,8,1,2,3,4,5,6] -> path1 finished -> all path finished we get the result [7,8,1,2,3,4,5,6]

example

2016-12-17

169.众数

要求:找出数组中出现次数超过n / 2的数,已设数组非空,并且众数肯定存在(若没此假设就要判断是否为空数组,最多的出现次数是否超过n / 2)

思路1: (自写一次AC)摩尔投票,简单

  1. public int majorityElement(int[] nums) {
  2. if (nums.length == 1) {
  3. return nums[0];
  4. }
  5. int number = nums[0], count = 0;
  6. for (int i = 0; i < nums.length; i++) {
  7. if (nums[i] == number) {
  8. count++;
  9. } else if (count == 0) {
  10. number = nums[i];
  11. } else {
  12. count--;
  13. }
  14. }
  15. return number;
  16. }

思路2: (discuss)排序,直接用Java内置的Arrays.sort()方法,返回n / 2位置的数

思路3: (discuss)利用HashMap,key用来表示数组的值,每增加1次,key对应的值+ 1,空间复杂度高

warn:记得map初始化<>有key, value两项

  1. public int majorityElement(int[] nums) {
  2. int number = 0;
  3. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  4. for (int num : nums) {
  5. //第一次放入对应的值为1
  6. if (! map.containsKey(num)) {
  7. map.put(num, 1);
  8. } else {
  9. map.put(num, map.get(num) + 1); //之前的值+ 1
  10. }
  11. if (map.get(num) > nums.length / 2) {
  12. number = num;
  13. }
  14. }
  15. return number;
  16. }

思路4: (discuss)还可以用位运算...

2016-12-17

1.两数之和

要求:给出target,从数组找出两数相加是它,并返回对应下标,假设只有一个解

思路: (自写一次AC)HashMap特性,键、值相反输入,找target - nums[i]是否存在就行

warn:循环外一定要有return

  1. public int[] twoSum(int[] nums, int target) {
  2. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  3. for (int i = 0; i < nums.length; i++) {
  4. if (map.containsKey(target - nums[i])) {
  5. return new int[]{map.get(target - nums[i]), i};
  6. }
  7. map.put(nums[i], i);
  8. }
  9. return new int[2];
  10. }

2016-12-21

167.两数之和II--输入有序数组

要求:数组升序排序,找出两个数能够相加等于目标数字,返回这两个数的下标,如[2, 7, 11, 15],得到目标9,返回1和2,假设只有一个解

思路1: (有思路但写不出来)利用升序特性和two pointers,两个指针分别在首尾,首指针增大,和增大,尾指针减小,和减小

  1. public int[] twoSum(int[] numbers, int target) {
  2. int i = 0, j = numbers.length - 1;
  3. int[] res = new int[2];
  4. if (numbers == null || numbers.length < 2) {
  5. return res;
  6. }
  7. while (i < j) {
  8. int sum = numbers[i] + numbers[j];
  9. //利用升序特性
  10. if (sum == target) {
  11. res[0] = i + 1;
  12. res[1] = j + 1;
  13. break;
  14. } else if (sum > target) {
  15. j--;
  16. } else {
  17. i++;
  18. }
  19. }
  20. return res;
  21. }

思路2: (discuss)二分查找,由于是两个数的和,把第一个数numbers[i]遍历,第二个数就在剩下的范围里面用二分查找搜target - numbers[i],时间复杂度高

2016-12-18

162.找峰值
要求:找出数组中任意的峰值,且数组没有重复的数

思路1: (自写一次AC)二分查找,中值比右小就抛弃左边,比右大就抛弃右边,时间复杂度为O(logn)

  1. public int findPeakElement(int[] nums) {
  2. int low = 0;
  3. int high = nums.length - 1;
  4. while (low < high) {
  5. int mid = low + (high - low) / 2;
  6. if (nums[mid] < nums[mid + 1]){
  7. low = mid + 1;
  8. } else {
  9. high = mid;
  10. }
  11. }
  12. return low;
  13. }

思路2: 笨方法,就是从开头遍历,看nums[i]是否同时比i + 1与i - 1大,时间复杂度为O(n)

  1. public int findPeakElement(int[] nums) {
  2. for (int i = 1; i < nums.length-1; i++) {
  3. if (nums[i] > nums[i+1] && nums[i] > nums[i-1]) {
  4. return i;
  5. }
  6. }
  7. return nums.length <= 1 || nums[0] > nums[1] ? 0 : nums.length-1;
  8. }

2016-12-18

153.旋转排序数组找最小值

要求:一排序数组在未知位置旋转,求最小值,假设数组不重复

思路: (discuss)二分查找,边界条件难找,判断nums[mid] < nums[mid - 1]证明mid是旋转点(最小点),并利用mid与low和high指向的数大小比较确定新的分界点

tips:分为三种情况,一没有旋转,二mid点在小的一边,三mid点在大的一边,考虑各种情况下边界情况应如何改变

  1. public int findMin(int[] nums) {
  2. if (nums == null || nums.length == 0) {
  3. return 0;
  4. }
  5. if (nums.length == 1) {
  6. return nums[0];
  7. }
  8. int low = 0;
  9. int high = nums.length - 1;
  10. while (low < high) {
  11. int mid = low + (high - low) / 2;
  12. //考虑没有旋转的情况nums[0]最小
  13. if (mid > 0 && nums[mid] < nums[mid - 1]) {
  14. return nums[mid];
  15. }
  16. if (nums[mid] >= nums[low] && nums[mid] > nums[high]) {
  17. low = mid + 1;
  18. } else {
  19. high = mid - 1;
  20. }
  21. }
  22. return nums[low];
  23. }

还有很多种边界的考虑方法,作为拓展可自己思考

2016-12-18

152.子数组的最大积

要求:找出子数组的最大乘积,子数组最少包含1个数

自写时以为排除所有负数,没考虑到负负得正...

2016-12-18

其实需要动态规划(Dynamic Programming)

2016-12-19

121.最好的时刻购买与出售股票

要求:数组中的第i个元素代表第i天股票的价格,限制只能买卖一次,而且买只能在卖之前,求最大利润

思路: (掌握思路后自写一次AC)“Dynamic Programming”,设置最低购买加个curMin来保存一直以来的最低价格,maxProfit来更新最大利润,也是第i天的“最优解”

  1. public int maxProfit(int[] prices) {
  2. if (prices == null || prices.length < 2) {
  3. return 0;
  4. }
  5. int maxProfit = 0;
  6. int curMin = prices[0];
  7. for (int i = 1; i < prices.length; i++) {
  8. //确保当前的比前面小才更新
  9. curMin = Math.min(curMin, prices[i]);
  10. maxProfit = Math.max(maxProfit, prices[i] - curMin);
  11. }
  12. return maxProfit;
  13. }

注:还有II(不限制买卖次数)、III题,比较难

2016-12-19

118.杨辉三角形:

要求:输入行数得到对应的三角形阵列

思路: (自写但出现错误后改过来)利用上下层的关系即可,用另一数组保存上层结果

warn:每次赋值备用数组前记得清空!!!

  1. public List<List<Integer>> generate(int numRows) {
  2. List<List<Integer>> ans = new ArrayList<>();
  3. if (numRows < 1) {
  4. return ans;
  5. }
  6. List<Integer> pre = new ArrayList<Integer>();
  7. for (int i = 1; i <= numRows; i++) {
  8. List<Integer> pas = new ArrayList<Integer>();
  9. pas.add(1);
  10. for (int j = 0; j < pre.size() - 1; j++) {
  11. pas.add(pre.get(j) + pre.get(j + 1));
  12. }
  13. if (i > 1) {
  14. pas.add(1);
  15. }
  16. ans.add(pas);
  17. pre.clear();
  18. pre.addAll(pas);
  19. }
  20. return ans;
  21. }

2016-12-19

119.杨辉三角形II:

要求:输入特定第k行的元素

思路1: (自写一次AC)与118代码几乎一样

  1. public List<Integer> getRow(int rowIndex) {
  2. List<Integer> pre = new ArrayList<Integer>();
  3. List<Integer> pas = new ArrayList<Integer>();
  4. for (int i = 0; i <= rowIndex; i++) {
  5. pas.clear();
  6. pas.add(1);
  7. for (int j = 0; j < pre.size() - 1; j++) {
  8. pas.add(pre.get(j) + pre.get(j + 1));
  9. }
  10. if (i > 0) {
  11. pas.add(1);
  12. }
  13. pre.clear();
  14. pre.addAll(pas);
  15. }
  16. return pas;
  17. }

思路2: (discuss)在新的一行开头插入1,然后从第2个元素(j = 1)开始计算与其下一个元素的和,并用set设为新一行的当前元素,此思路不用额外的空间,在原地添加,可应用到118题

tips:此方法code较简洁,且省空间

  1. public List<Integer> getRow(int rowIndex) {
  2. List<Integer> pas = new ArrayList<Integer>();
  3. for (int i = 0; i <= rowIndex; i++) {
  4. //在新一行的头部插入1
  5. pas.add(0, 1);
  6. for (int j = 1; j < pas.size() - 1; j++) {
  7. //令j指向的为新元素
  8. pas.set(j, pas.get(j) + pas.get(j + 1));
  9. }
  10. }
  11. return pas;
  12. }

2016-12-19

88.合并有序数组

要求:给出m长数组num1,n长数组num2,按顺序合并成m+n长的数组

思路: (没注意到数组足够长)由于nums1的数组长度是m + n,所以若在数组尾部开始赋值,不会覆盖掉nums1的内容

tips:由于在nums1内进行操作,只要保证nums2赋值完就行,第一循环条件为都没赋值完,而第二循环判断的是 是否nums2赋值完,若赋值完证明操作完毕,无需判断nums1

2016-12-19

80.去除有序数组的重复II

要求:保留重复两次,其余的删除,并返回新长度

思路1: (根据26题经验自写一次AC)设置计数器count,判断其重复次数,两次以内就继续保留下来

  1. public int removeDuplicates(int[] nums) {
  2. if (nums.length < 2) {
  3. return nums.length;
  4. }
  5. int length = 1, count = 1;
  6. for (int i = 1; i < nums.length; i++) {
  7. if (nums[i] == nums[i - 1]) {
  8. count++;
  9. if (count <= 2) {
  10. nums[length++] = nums[i];
  11. }
  12. } else {
  13. count = 1;
  14. nums[length++] = nums[i];
  15. }
  16. }
  17. return length;
  18. }

思路2: (discuss)根据有序的优点,判断此项是否与前两项相等,就可以知道是否重复两次

  1. public int removeDuplicates(int[] nums) {
  2. int length = 0;
  3. for (int num : nums) {
  4. if (length < 2 || num != nums[length - 2]) {
  5. nums[length++] = num;
  6. }
  7. }
  8. return length;
  9. }

2016-12-19

26.去除有序数组的重复

要求:去掉有序数组中所有重复的数,并返回新的长度,不能分配额外的空间,只能在原数组本地进行

思路: (自写没AC)思路正确,但忘记调整数组,只返回了新长度。可用长度length作为指针来更新数组

  1. public int removeDuplicates(int[] nums) {
  2. if (nums == null || nums.length == 0 || nums.length == 1) {
  3. return nums.length;
  4. }
  5. int length = 1;
  6. for (int i = 1; i < nums.length; i++) {
  7. if (nums[i] != nums[i - 1]) {
  8. nums[length++] = nums[i];
  9. }
  10. }
  11. return length;
  12. }

discuss中使用foreach,省了一个指针的空间,思路一样

2016-12-19

75.排序颜色

要求:把红白蓝用0, 1, 2表示,将数组重新排序成红白蓝,且相同颜色相邻的形式

思路1: (看了follow up后自写一次AC)就是数0, 1的个数,然后按顺序赋值就行

  1. public void sortColors(int[] nums) {
  2. int count0 = 0, count1 = 0;
  3. for (int num : nums) {
  4. if (num == 0) {
  5. count0++;
  6. } else if (num ==1) {
  7. count1++;
  8. }
  9. }
  10. int i = 0;
  11. while (i < count0) nums[i++] = 0;
  12. while (i < count0 + count1) nums[i++] = 1;
  13. while (i < nums.length) nums[i++] = 2;
  14. }

思路2: (discuss)把0和2丢到两边,即碰到0或者2就和两边的数交换,而剩下中间的就是1

  1. public void sortColors(int[] nums) {
  2. int zero = 0; int two = nums.length - 1;
  3. //遍历到2的位置即可
  4. for (int i = 0; i <= two; i++) {
  5. while (nums[i] == 2 && i < two) {
  6. int temp = nums[i];
  7. nums[i] = nums[two];
  8. nums[two--] = temp;
  9. }
  10. while (nums[i] == 0 && i > zero) {
  11. int temp = nums[i];
  12. nums[i] = nums[zero];
  13. nums[zero++] = temp;
  14. }
  15. }
  16. }

2016-12-19

74.搜索二维矩阵

要求:矩阵的特点有一行中从小到大排列,一行的头元素大于上一行的所有元素,在矩阵中找出指定数

思路1: (自写一次AC)“二分查找思想”,选右上角的数(也可选左下角),大于target就列减,小于target就行加

warn:判断空矩阵,二维数组求列数是matrix[0].length

  1. public boolean searchMatrix(int[][] matrix, int target) {
  2. if (matrix == null || matrix.length == 0) {
  3. return false;
  4. }
  5. int i = 0, j = matrix[0].length - 1;
  6. while (i < matrix.length && j >= 0) {
  7. if(matrix[i][j] == target) {
  8. return true;
  9. } else if (matrix[i][j] > target) {
  10. j --;
  11. } else {
  12. i++;
  13. }
  14. }
  15. return false;
  16. }

思路2: (discuss)还是二分查找,根据条件可把其当作普通有序一维数组,其中在一维数组的位置mid可通过 / 列数,% 列数获得在二维数组的坐标

warn:判断条件是low <= high,可防止数组只有一个数的情况下无法判断

  1. public boolean searchMatrix(int[][] matrix, int target) {
  2. if (matrix == null || matrix.length == 0) {
  3. return false;
  4. }
  5. int m = matrix.length, n = matrix[0].length;
  6. int low = 0, high = m * n - 1;
  7. while (low <= high) {
  8. int mid = low + (high - low) / 2;
  9. if (matrix[mid / n][mid % n] == target) {
  10. return true;
  11. } else if (matrix[mid / n][mid % n] > target) {
  12. high = mid - 1;
  13. } else {
  14. low = mid + 1;
  15. }
  16. }
  17. return false;
  18. }

2016-12-20

73.矩阵中置0

要求:凡是存在0元素,其对应的行和列上的所有元素都应为0

思路: (discuss)用第0行和第0列的元素置0来记录对应的j列(第0行第j个对应)和i行(第0列第i个对应)上的所有元素为0,但要区分它们是否本来就为0(用fr和fc),若本来就为0,则相应的0行0列也要全置为0

  1. public void setZeroes(int[][] matrix) {
  2. if (matrix == null || matrix.length == 0) {
  3. return;
  4. }
  5. boolean fr = false, fc = false; //用来分辨第0行与第0列的元素是否本身为0
  6. //找出矩阵中所有的0,并将其对应的第0行及第0列的元素置0
  7. for (int i = 0; i < matrix.length; i++) {
  8. for (int j = 0; j < matrix[0].length; j++) {
  9. if (matrix[i][j] == 0) {
  10. if (i == 0) fr = true;
  11. if (j == 0) fc = true;
  12. matrix[0][j] = 0;
  13. matrix[i][0] = 0;
  14. }
  15. }
  16. }
  17. for (int i = 1; i < matrix.length; i++) {
  18. for (int j = 1; j < matrix[0].length; j++) {
  19. if (matrix[0][j] == 0 || matrix[i][0] == 0) {
  20. matrix[i][j] = 0;
  21. }
  22. }
  23. }
  24. if (fr) {
  25. for (int j = 0; j < matrix[0].length; j++) {
  26. matrix[0][j] = 0;
  27. }
  28. }
  29. if (fc) {
  30. for (int i = 0; i < matrix.length; i++) {
  31. matrix[i][0] = 0;
  32. }
  33. }
  34. }

2016-12-20

66.加一

要求:数组代表一个数,越靠前权重越高

思路: (读懂题后自写一次AC)就是判断是否大于9,简单

tips:若溢出,数组长度要+ 1,新建数组默认每位都是0

  1. public int[] plusOne(int[] digits) {
  2. int n = digits.length;
  3. digits[n - 1]++;
  4. for (int i = n - 1; i > 0; i--) {
  5. if (digits[i] > 9) {
  6. digits[i - 1]++;
  7. digits[i] = 0;
  8. } else {
  9. break;
  10. }
  11. }
  12. if (digits[0] > 9) {
  13. int[] res = new int[n + 1];
  14. res[0] = 1;
  15. return res;
  16. }
  17. return digits;
  18. }

2016-12-20

54.螺旋矩阵

要求:给出m * n的矩阵,用螺旋顺序来返回它

思路: (discuss)用四个边界,定义每次开始与结束,分为“右->下->左->上”四步

warn:约束条件要想清楚

  1. public List<Integer> spiralOrder(int[][] matrix) {
  2. List<Integer> res = new ArrayList<Integer>();
  3. if (matrix.length == 0) {
  4. return res;
  5. }
  6. int rowBegin = 0;
  7. int rowEnd = matrix.length-1;
  8. int colBegin = 0;
  9. int colEnd = matrix[0].length - 1;
  10.  
  11. while (rowBegin <= rowEnd && colBegin <= colEnd) {
  12. // Traverse Right
  13. for (int j = colBegin; j <= colEnd; j ++) {
  14. res.add(matrix[rowBegin][j]);
  15. }
  16. rowBegin++;
  17. // Traverse Down
  18. for (int j = rowBegin; j <= rowEnd; j ++) {
  19. res.add(matrix[j][colEnd]);
  20. }
  21. colEnd--;
  22. if (rowBegin <= rowEnd) {
  23. // Traverse Left
  24. for (int j = colEnd; j >= colBegin; j --) {
  25. res.add(matrix[rowEnd][j]);
  26. }
  27. }
  28. rowEnd--;
  29. if (colBegin <= colEnd) {
  30. // Traverse Up
  31. for (int j = rowEnd; j >= rowBegin; j --) {
  32. res.add(matrix[j][colBegin]);
  33. }
  34. }
  35. colBegin++;
  36. }
  37. return res;
  38. }

2016-12-20

59.螺旋矩阵II

要求:给出n,生成1到n ^ 2的螺旋矩阵

思路: (自写一次AC)参照54题

  1. public int[][] generateMatrix(int n) {
  2. int[][] matrix = new int[n][n];
  3. if (n == 0) {
  4. return matrix;
  5. }
  6. int startRow = 0, startCol = 0;
  7. int endRow = n - 1, endCol = n - 1;
  8. int count = 1;
  9. while (startRow <= endRow && startCol <= endCol) {
  10. for (int j = startRow; j <= endRow; j++) {
  11. matrix[startCol][j] = count++;
  12. }
  13. startCol++;
  14. for (int i = startCol; i <= endCol; i++) {
  15. matrix[i][endRow] = count++;
  16. }
  17. endRow--;
  18. if (startCol <= endCol) {
  19. for (int j = endRow; j >= startRow; j--) {
  20. matrix[endCol][j] = count++;
  21. }
  22. }
  23. endCol--;
  24. if (startRow <= endRow) {
  25. for (int i = endCol; i >= startCol; i--) {
  26. matrix[i][startRow] = count++;
  27. }
  28. }
  29. startRow++;
  30. }
  31. return matrix;
  32. }

tips:方阵时,约束条件可以少一点,后两个if判断可以去掉,只判断startRow <= endRow即可

2016-12-21

48.翻转图片

要求:用n * n矩阵代替图片,把此方阵顺时针旋转90度

思路: (自写一次AC)分为两步:1、根据对角元素进行逐行逐列交换 2、把首尾列依次交换

  1. public void rotate(int[][] matrix) {
  2. int n = matrix.length;
  3. if (n < 2) {
  4. return;
  5. }
  6. for (int i = 0; i <= n - 2; i++) {
  7. for (int j = i + 1; j <= n - 1; j++) {
  8. int temp = matrix[i][j];
  9. matrix[i][j] = matrix[j][i];
  10. matrix[j][i] = temp;
  11. }
  12. }
  13. for (int col = 0; col < n / 2; col++) {
  14. for (int row = 0; row < n; row++) {
  15. int temp = matrix[row][col];
  16. matrix[row][col] = matrix[row][n - 1 - col];
  17. matrix[row][n - 1 - col] = temp;
  18. }
  19. }
  20. }

2016-12-21

35.寻找插入位置

要求:给出有序数组,若数组中存在数target,返回对应下标,否则返回它应该插入的位置,假设无重复

思路: (自写但一次没AC)就是简单的二分查找实现

tips:约束条件为low <= high

  1. public int searchInsert(int[] nums, int target) {
  2. int low = 0, high = nums.length - 1;
  3. while (low <= high) {
  4. int mid = (low + high) / 2;
  5. if (nums[mid] == target) {
  6. return mid;
  7. } else if (nums[mid] > target) {
  8. high = mid - 1;
  9. } else {
  10. low = mid + 1;
  11. }
  12. }
  13. return low;
  14. }

2016-12-21

34.寻找数的区间

要求:给出有序数组,返回目标数target所在开始到结束的区间,若不存在target则返回[-1, -1],时间复杂度为O(logn)

思路1: (自写第二次AC)先用二分查找搜到target,然后在mid左右进行缩进,直到不是target为止,得到首末位置(虽AC但不知道满不满足复杂度)

warn:第一次没AC原因是在while循环内定义mid,导致在下一循环中mid有可能没定义

  1. public int[] searchRange(int[] nums, int target) {
  2. int[] res = {-1, -1};
  3. int low = 0, high = nums.length - 1, mid = 0;
  4. boolean ex = false;
  5. while (low <= high) {
  6. mid = (low + high) / 2;
  7. if (nums[mid] == target) {
  8. ex = true;
  9. break;
  10. } else if (nums[mid] > target) {
  11. high = mid - 1;
  12. } else {
  13. low = mid + 1;
  14. }
  15. }
  16. if (ex) {
  17. int i = mid;
  18. while (i > 0 && nums[i - 1] == nums[i]) {
  19. i--;
  20. }
  21. res[0] = i;
  22. int j = mid;
  23. while (j < nums.length - 1 && nums[j + 1] == nums[j]) {
  24. j++;
  25. }
  26. res[1] = j;
  27. }
  28. return res;
  29. }

思路2: (discuss)用两次二分查找,找出target和target + 1第一次出现的时候,构成区间

warn:数组溢出条件,无论是||还是&&都要放在前面!

tips:开始令high = nums.length,找第一次出现时,满足nums[mid] >= target时high = mid;同理找最后一次时,满足nums[mid] <= target时low = mid

  1. public int[] searchRange(int[] nums, int target) {
  2. int start = firstEqual(nums, target);
  3. if (start == nums.length || nums[start] != target) {
  4. return new int[]{-1, -1};
  5. }
  6. return new int[]{start, firstEqual(nums, target + 1) - 1}; //利用有序,寻找更大的数位置
  7. }
  8. private int firstEqual(int[] nums, int target) {
  9. int low = 0, high = nums.length;
  10. while (low < high) {
  11. int mid = (low + high) / 2;
  12. if (nums[mid] < target) {
  13. low = mid + 1;
  14. } else { //找第一次出现,相等或大于target都要令high = mid
  15. high = mid;
  16. }
  17. }
  18. return low;
  19. }

思路3: (discuss)就是按照tips所说直接找第一和最后一次出现,代码略

2016-12-21

27.去除元素

要求:给出数组和一个数,去除数组中的该数(其实就是将其放到结尾)并返回去除后数组长度,数的顺序可改变,但必须原地进行

思路1: (自写一次AC)two pointers问题,首尾一个指针,判断首部是否为val值,是的话就判断尾部是否为val,不是val就交换,是就左移直到不是val为止

  1. public int removeElement(int[] nums, int val) {
  2. int n = nums.length;
  3. int i = 0, j = n - 1;
  4. while (i <= j) {
  5. if (nums[i] == val) {
  6. n--;
  7. while (j > i && nums[j] == val) {
  8. j--;
  9. n--;
  10. }
  11. nums[i++] = nums[j];
  12. nums[j--] = val;
  13. } else {
  14. i++;
  15. }
  16. }
  17. return n;
  18. }

思路2: (自写一次AC)foreach方法,直接把新长度同时作为指针,不等于val值时才在头部开始赋值,较思路1简单

  1. public int removeElement(int[] nums, int val) {
  2. int n = 0;
  3. for (int num : nums) {
  4. if (num != val) {
  5. nums[n++] = num;
  6. }
  7. }
  8. return n;
  9. }

2016-12-21

11.装最多的水

要求:把数组的下标当成横坐标,值代表纵坐标,题目求的是a(i), a(j), i, j作为端点时,容器最多装多少水(这道题真难理解...)

思路: (明白题目后一次AC)two pointers,设首尾指针i, j,找最长的边(ai大时j左移,反之i右移),并每次都算v,用Math.max与之前的比较保留较大值

  1. public int maxArea(int[] height) {
  2. int i = 0, j = height.length - 1;
  3. int v = 0;
  4. while (i < j) {
  5. v = Math.max(v, (j - i) * Math.min(height[i], height[j]));
  6. if (height[i] < height[j]) {
  7. i++;
  8. } else {
  9. j--;
  10. }
  11. }
  12. return v;
  13. }

2016-12-21

15.三数之和

要求:找出所有a, b, c,满足a + b + c = 0的集合

错误思路:想着用HashMap,先确定第一个数,然后后面两数加起来等于nums[i],但这样会出现重复,无法去除类似于[0, 0, 0, 0]这种情况

  1. public List<List<Integer>> threeSum(int[] nums) {
  2. Arrays.sort(nums);
  3. List<List<Integer>> res = new ArrayList<>();
  4. for (int i = 0; i <= nums.length - 3; i++) {
  5. if (i == 0 || nums[i] != nums[i - 1]) {
  6. Map<Integer, Integer> map = new HashMap<Integer, Integer>();
  7. for (int j = i + 1; j < nums.length; j++) {
  8. if (map.containsKey(-nums[i] - nums[j])) {
  9. List<Integer> three = new ArrayList<Integer>();
  10. three.add(nums[i]);
  11. three.add(-nums[i] - nums[j]);
  12. three.add(nums[j]);
  13. res.add(three);
  14. }
  15. map.put(nums[j], j);
  16. }
  17. }
  18. }
  19. return res;
  20. }

如何改进HashMap方法才能不重复?

正确思路:(discuss)two pointers,思路和我的一样,但确定第一个数后,要判断是否与之前确定的数一样,再用两个指针可排除后面出现的重复情况,出现第一次等于0后,指针要一直跳到不重复的数为止

warn:先排序能确保用来确定的数(作为后面two pointers的和)不重复,新情况时不用new,直接用Arrays.asList();

  1. public List<List<Integer>> threeSum(int[] nums) {
  2. Arrays.sort(nums);
  3. List<List<Integer>> res = new ArrayList<>();
  4. for (int i = 0; i <= nums.length - 3; i++) {
  5. if (i == 0 || nums[i] != nums[i - 1]) {
  6. int low = i + 1, high = nums.length - 1;
  7. while (low < high) {
  8. if (nums[low] + nums[high] == - nums[i]) {
  9. res.add(Arrays.asList(nums[i], nums[low], nums[high]));
  10. while (low < high && nums[low + 1] == nums[low]) low++;
  11. while (low < high && nums[high - 1] == nums[high]) high--;
  12. low++;
  13. high--;
  14. } else if (nums[low] + nums[high] < - nums[i]) {
  15. low++;
  16. } else {
  17. high--;
  18. }
  19. }
  20. }
  21. }
  22. return res;
  23. }

2016-12-22

16.最接近的三数和

要求:找出相加和最接近目标数target的三个数,并返回它们的和,假设只存在一个解

思路:和15题一样,但不可以像它一样用while判断不重复,不然会错过了一些情况

  1. public int threeSumClosest(int[] nums, int target) {
  2. Arrays.sort(nums);
  3. int res = nums[0] + nums[1] + nums[nums.length - 1];
  4. for (int i = 0; i < nums.length - 2; i++) {
  5. if (i == 0 || nums[i] != nums[i - 1]) {
  6. int low = i + 1, high = nums.length - 1;
  7. while (low < high) {
  8. int sum = nums[i] + nums[low] + nums[high];
  9. if (Math.abs(target - sum) < Math.abs(target - res)) {
  10. res = sum;
  11. if (res == target) {
  12. return res;
  13. }
  14. }
  15. if (sum > target) {
  16. high--;
  17. } else {
  18. low++;
  19. }
  20. }
  21. }
  22. }
  23. return res;
  24. }

2016-12-22

18.四数之和

要求:找出所有a, b, c, d相加等于target的集合

思路:和15题一样,但这样的话时间复杂度就要O(n ^ 3)了,其实就是多一重循环,多的循环从j = i + 1开始

  1. public List<List<Integer>> fourSum(int[] nums, int target) {
  2. List<List<Integer>> res = new ArrayList<>();
  3. Arrays.sort(nums);
  4. for (int i = 0; i < nums.length - 3; i++) {
  5. if (i == 0 || nums[i - 1] != nums[i]) {
  6. for (int j = i + 1; j < nums.length - 2; j++) {
  7. if (j == i + 1 || nums[j - 1] != nums[j]) {
  8. int low = j + 1, high = nums.length - 1;
  9. while (low < high) {
  10. int sum = nums[i] + nums[j] + nums[low] + nums[high];
  11. if (sum == target) {
  12. res.add(Arrays.asList(nums[i], nums[j], nums[low], nums[high]));
  13. while (low < high && nums[low] == nums[low + 1]) low++;
  14. while (low < high && nums[high] == nums[high - 1]) high--;
  15. low++;
  16. high--;
  17. } else if (sum < target) {
  18. low++;
  19. } else {
  20. high--;
  21. }
  22. }
  23. }
  24. }
  25. }
  26. }
  27. return res;
  28. }

2016-12-22

581. Shortest Unsorted Continuous Subarray

要求:找出最短需要排序的连续子序列

思路:取前后指针,并每次更新前面的最大,后面的最小,若发现前面的下一位比较小,就证明需要排序,更新所需排序子序列的末端点,后面反之。

  1. public class Solution {
  2. public int findUnsortedSubarray(int[] nums) {
  3. int n = nums.length, beg = -1, end = -2, max = nums[0], min = nums[n - 1];
  4. for (int i = 1; i < n; i++) {
  5. max = Math.max(max, nums[i]);
  6. min = Math.min(min, nums[n - i - 1]);
  7. if (nums[i] < max) {
  8. end = i;
  9. }
  10. if (nums[n - i - 1] > min) {
  11. beg = n - i - 1;
  12. }
  13. }
  14. return end - beg + 1;
  15. }
  16. }

2017-06-15

Hash Table

3.不含重复字符的最大子字符串(Longest Substring Without Repeating Characters)

要求:如题,一定要是子字符串

思路1:(自写经修改后AC)HashSet不重复特性,若能添加则计数器count++,并更新长度,不能添加的时候删除i - count不与i相同的字符,同时count--,直到相同为止

(sliding window)

  1. public int lengthOfLongestSubstring(String s) {
  2. Set<Character> set = new HashSet<Character>();
  3. int length = 0, count = 0;
  4. for (int i = 0; i < s.length(); i++) {
  5. if (set.add(s.charAt(i))) {
  6. count++;
  7. length = Math.max(count, length);
  8. } else {
  9. while (s.charAt(i - count) != s.charAt(i)) {
  10. set.remove(s.charAt(i - count));
  11. count--;
  12. }
  13. }
  14. }
  15. return length;
  16. }

另一写法:more simple, together with two pointers

  1. public int lengthOfLongestSubstring(String s) {
  2. Set<Character> set = new HashSet<Character>();
  3. int i = 0, j = 0, length = 0;
  4. //two pointers, while j for the end of the substring, i for the start
  5. while (j < s.length()) {
  6. if (set.add(s.charAt(j))) {
  7. j++;
  8. length = Math.max(length, j - i);
  9. } else {
  10. set.remove(s.charAt(i++));
  11. }
  12. }
  13. return length;
  14. }

思路2:(Optimized Solution)HashMap,不是很清楚为什么要+ 1

  1. public int lengthOfLongestSubstring(String s) {
  2. int n = s.length(), ans = 0;
  3. Map<Character, Integer> map = new HashMap<>(); // current index of character
  4. // try to extend the range [i, j]
  5. for (int j = 0, i = 0; j < n; j++) {
  6. if (map.containsKey(s.charAt(j))) {
  7. i = Math.max(map.get(s.charAt(j)), i);
  8. }
  9. ans = Math.max(ans, j - i + 1);
  10. map.put(s.charAt(j), j + 1);
  11. }
  12. return ans;
  13. }

2016-12-22

36.有效九宫格(Valid Sudoku)

要求:分辨九宫格是否有效,九宫格有可能有空格,用'.'表示,只考虑已填的是否正确

思路1:(discuss)巧妙运用对应关系,只需要i,j两个循环,如i = 0对应0行0列0大格

tips:用i作为支点,与j相反就可以调转行和列,而i = 0, 1, 2,对应的是前三个大格,即对应关系为row = 3 * (i / 3), column = 3 * (i % 3)

  1. public boolean isValidSudoku(char[][] board) {
  2. for (int i = 0; i < board.length; i++) {
  3. Set<Character> rows = new HashSet<Character>();
  4. Set<Character> columns = new HashSet<Character>();
  5. Set<Character> cube = new HashSet<Character>();
  6. for (int j = 0; j < board[0].length; j++) {
  7. //判断第i行
  8. if (board[i][j] != '.' && ! rows.add(board[i][j])) {
  9. return false;
  10. }
  11. //判断第i列
  12. if (board[j][i] != '.' && ! columns.add(board[j][i])) {
  13. return false;
  14. }
  15. //计算第i个大格,算对应关系
  16. int row = 3 * (i / 3);
  17. int col = 3 * (i % 3);
  18. if (board[row + j / 3][col + j % 3] != '.' && ! cube.add(board[row + j / 3][col + j % 3])) {
  19. return false;
  20. }
  21. }
  22. }
  23. return true;
  24. }

思路2:(discuss)为每个数添加相同的第几行几列几格的标记,只有满足同一数同一标记才返回false,more simple

  1. public boolean isValidSudoku(char[][] board) {
  2. //存任何类型的set
  3. Set seen = new HashSet();
  4. for (int i = 0; i < 9; i++) {
  5. for (int j = 0; j < 9; j++) {
  6. if (board[i][j] != '.') {
  7. //为此项打上标签输入
  8. String b = "(" + board[i][j] + ")";
  9. //i行在后面添加i标记,j列在前面加j标记,这样就把行列分开了
  10. if (! seen.add(b + i) || ! seen.add(j + b) || ! seen.add(i / 3 + b + j / 3))
  11. return false;
  12. }
  13. }
  14. }
  15. return true;
  16. }

2016-12-22

49.组合颠倒字符串(Group Anagrams)

要求:把颠倒的字符串放在一起

思路1:(discuss + 自己修改)把字符串排序后当成key,用它来标记一个ArrayList,存放排序后一样的字符串

  1. public List<List<String>> groupAnagrams(String[] strs) {
  2. Map<String, List<String>> map = new HashMap<>();
  3. for (String s : strs) {
  4. //让旋转后的字符串按照顺序排列
  5. char[] chars = s.toCharArray();
  6. Arrays.sort(chars);
  7. String keyStr = String.valueOf(chars);
  8. if (! map.containsKey(keyStr)) {
  9. map.put(keyStr, new ArrayList<String>(){{add(s);}});
  10. } else {
  11. map.get(keyStr).add(s);
  12. }
  13. }
  14. return new ArrayList<List<String>>(map.values());
  15. }

思路2:(discuss)用prime number,hard to read,代码略

2016-12-23

136.单身数(Single Number)

要求:除了一个数,其他数全部出现两次,找出这一个

思路:异或自己为0,之前碰过

  1. public int singleNumber(int[] nums) {
  2. int res = 0;
  3. for (int num : nums) {
  4. res ^= num;
  5. }
  6. return res;
  7. }

另一思路:若使用HashMap,key用nums填入,返回value为2对应的key值(没有用value找key方法?)

2016-12-23

166.分数转成循环小数(Fraction to Recurring Decimal)

要求:给出分子与分母,以字符串的形式返回对应的小数,其中小数部分若循环的话用()括起来

思路:(discuss)1、判断正负 2、获得整数部分 3、获得小数部分,用map的key判断是否重复,用value记录每次除完的长度,出现重复时就在当前数前一次出现时插入“(”

tips:用StringBuider来进行添加与插入,把分子分母转成long,求小数时每次除之前要乘以10(即做除法时补0)

  1. public String fractionToDecimal(int numerator, int denominator) {
  2. if (numerator == 0) {
  3. return "0";
  4. }
  5. StringBuilder res = new StringBuilder();
  6. //判断正负
  7. res.append(((numerator > 0) ^ (denominator > 0)) ? "-" : "");
  8. long num = Math.abs((long)numerator);
  9. long den = Math.abs((long)denominator);
  10. //整数部分
  11. res.append(num / den);
  12. num %= den;
  13. if (num == 0) {
  14. return res.toString();
  15. }
  16. //小数部分
  17. res.append(".");
  18. Map<Long, Integer> map = new HashMap<Long, Integer>();
  19. while (num != 0) {
  20. //存放余数
  21. map.put(num, res.length());
  22. num *= 10;
  23. res.append(num / den);
  24. num %= den;
  25. if (map.containsKey(num)) {
  26. res.insert(map.get(num), "(");
  27. res.append(")");
  28. break;
  29. }
  30. }
  31. return res.toString();
  32. }

2016-12-26

187.重复DNA片段(Repeated DNA Sequences)

要求:DNA中包含ACGT四个字母,求重复出现的10位长的DNA片段

思路1:(discuss+自己修改)HashMap+位运算,由于只含有ACGT,可把它用00, 01, 10, 11表示,即每读一个10位长的DNA,就变成了20位的二进制数,用HashMap来存储,第一次出现value设为0,出现过后设为1,以防止添加重复的子串

  1. public List<String> findRepeatedDnaSequences(String s) {
  2. Map<Integer, Integer> map = new HashMap<>();
  3. List<String> rv = new ArrayList<>();
  4. //把ACGT转换成两位二进制数
  5. char[] trans = new char[26];
  6. //trans['A' - 'A'] = 0;
  7. trans['C' - 'A'] = 1;
  8. trans['G' - 'A'] = 2;
  9. trans['T' - 'A'] = 3;
  10.  
  11. for(int i = 0; i < s.length() - 9; i++) {
  12. int v = 0;
  13. for(int j = i; j < i + 10; j++) {
  14. //移位存储每一个字母
  15. v |= trans[s.charAt(j) - 'A'];
  16. v <<= 2;
  17. }
  18. if (map.containsKey(v)) {
  19. //判断是否第一次重复
  20. if (map.get(v) == 0) {
  21. rv.add(s.substring(i, i + 10));
  22. }
  23. map.put(v, 1);
  24. } else {
  25. map.put(v, 0);
  26. }
  27. }
  28. return rv;
  29. }

思路2:(自写一次AC)HashMap,根本就不用转换,遍历一次,把每个位置后面以10位子字符串ss截取,都存进map中即可,照样用value判断是否第一次重复

  1. public List<String> findRepeatedDnaSequences(String s) {
  2. Map<String, Integer> map = new HashMap<>();
  3. List<String> rv = new ArrayList<>();
  4. for (int i = 0; i < s.length() - 9; i++) {
  5. String ss = s.substring(i, i + 10);
  6. if (map.containsKey(ss)) {
  7. //判断是否第一次重复
  8. if (map.get(ss) == 0) {
  9. rv.add(ss);
  10. }
  11. map.put(ss, 1);
  12. } else {
  13. map.put(ss, 0);
  14. }
  15. }
  16. return rv;
  17. }

另一写法:用两个HashSet防止结果重复

  1. public List<String> findRepeatedDnaSequences(String s) {
  2. Set seen = new HashSet(), repeated = new HashSet();
  3. for (int i = 0; i + 9 < s.length(); i++) {
  4. String ten = s.substring(i, i + 10);
  5. if (!seen.add(ten))
  6. repeated.add(ten);
  7. }
  8. return new ArrayList(repeated);
  9. }

2016-12-26

202.快乐数字(Happy Number)

要求:判断这个数是否happy number,就是把各个位上的数取平方相加,直到循环之前能够实现和为1

思路1:(自写一次AC)就是通过整除10来获得每一位,然后HashSet判断重复

  1. public boolean isHappy(int n) {
  2. Set<Integer> set = new HashSet<>();
  3. while (n != 1) {
  4. if (! set.add(n)) {
  5. return false;
  6. }
  7. int sum = 0;
  8. while (n != 0) {
  9. int temp = n % 10;
  10. sum += temp * temp;
  11. n /= 10;
  12. }
  13. n = sum;
  14. }
  15. return true;
  16. }

思路2:(discuss)可以不用set,空间复杂度为O(1)

2016-12-26

204.数质数(Count Primes)

要求:数出少于n的所有质数的个数

思路:(答案)把所有不是质数的算出来,剩下的就是质数 O(n)空间,O(n log log n)时间

  1. public int countPrimes(int n) {
  2. boolean[] isPrime = new boolean[n];
  3. for (int i = 2; i < n; i++) {
  4. isPrime[i] = true;
  5. }
  6. // Loop's ending condition is i * i < n instead of i < sqrt(n)
  7. // to avoid repeatedly calling an expensive function sqrt().
  8. for (int i = 2; i * i < n; i++) {
  9. if (!isPrime[i]) continue;
  10. for (int j = i * i; j < n; j += i) {
  11. isPrime[j] = false;
  12. }
  13. }
  14. int count = 0;
  15. for (int i = 2; i < n; i++) {
  16. if (isPrime[i]) count++;
  17. }
  18. return count;
  19. }

另一种写法,每次循环时的设置不一样,但同样是求非质数

  1. public int countPrimes(int n) {
  2. boolean[] notPrime = new boolean[n];
  3. int count = 0;
  4. for (int i = 2; i < n; i++) {
  5. if (notPrime[i] == false) {
  6. count++;
  7. for (int j = 2; i*j < n; j++) {
  8. notPrime[i*j] = true;
  9. }
  10. }
  11. }
  12.  
  13. return count;
  14. }

2016-12-26

205.同构字符串(Isomorphic Strings)

要求:判断s能否把相同的字符一起转换成另一个字符来得到t(包括数字),假设s, t两字符串长度相等

思路1:(discuss)用256位数组来存储可能出现的256个字符的位置,先判断当前字符上一次在的位置是否一样,然后再更新它的位置

warn:要考虑256种,不用减‘a’,由于初始化时数组为0,为了区分,需要用i + 1确保i = 0时的情况

  1. public boolean isIsomorphic(String s, String t) {
  2. int[] ss = new int[256];
  3. int[] ts = new int[256];
  4. for (int i = 0; i < s.length(); i++) {
  5. if (ss[s.charAt(i)] != ts[t.charAt(i)]) {
  6. return false;
  7. }
  8. ss[s.charAt(i)] = i + 1;
  9. ts[t.charAt(i)] = i + 1;
  10. }
  11. return true;
  12. }

(10ms)

思路2:(自写一次AC)用两张HashMap来判断,一张key是s的字符,另一张则是t,用此来判断每次来的新配对是否与之前的一样

tips:discuss中可用一张map完成,但用containsValue判断的话逻辑比较乱,自写后理顺得到第三种代码,当前的输入若之前不存在这个key但存在这个value,证明与之前的配对不一样

  1. public boolean isIsomorphic(String s, String t) {
  2. Map<Character, Character> map1 = new HashMap<>();
  3. Map<Character, Character> map2 = new HashMap<>();
  4. for (int i = 0; i < s.length(); i++) {
  5. char a = s.charAt(i);
  6. char b = t.charAt(i);
  7. if (map1.containsKey(a) && map1.get(a) != b) {
  8. return false;
  9. } else if (map2.containsKey(b) && map2.get(b) != a){
  10. return false;
  11. } else {
  12. map1.put(a, b);
  13. map2.put(b, a);
  14. }
  15. }
  16. return true;
  17. }

(49ms)

  1. public boolean isIsomorphic(String s, String t) {
  2. Map<Character, Character> map = new HashMap<>();
  3. for (int i = 0; i < s.length(); i++) {
  4. char a = s.charAt(i);
  5. char b = t.charAt(i);
  6. if (map.containsKey(a)) {
  7. if (map.get(a) != b) {
  8. return false;
  9. }
  10. } else if (map.containsValue(b)){ //在不含key的情况下有一样的value,证明与之前的配对不一样
  11. return false;
  12. } else {
  13. map.put(a, b);
  14. }
  15. }
  16. return true;
  17. }

(19ms)

2016-12-27

242.有效旋转(Valid Anagram)

要求:判断t是否经过s移位获得

思路1:(自写一次AC)HashSet+Sort把字符串转成数组排序之后用Set判断是否重复(7ms)

  1. public boolean isAnagram(String s, String t) {
  2. char[] sc = s.toCharArray();
  3. char[] tc = t.toCharArray();
  4. Arrays.sort(sc);
  5. Arrays.sort(tc);
  6. s = String.valueOf(sc);
  7. t = String.valueOf(tc);
  8. Set<String> set = new HashSet<>();
  9. set.add(s);
  10. if (set.add(t)) return false;
  11. return true;
  12. }

思路2:(discuss)不用排序,用26位长的数组分别代表26个字母,然后用来计数每一个字母的数量,s含有某字母对应位置+ 1,而t则- 1,最后判断是否每个位都为0(8ms)

  1. public boolean isAnagram(String s, String t) {
  2. if (s.length() != t.length()) {
  3. return false;
  4. }
  5. int[] alps = new int[26];
  6. for (int i = 0; i < s.length(); i++) {
  7. //得到某字母对应的位置+ 1
  8. alps[s.charAt(i) - 'a']++;
  9. alps[t.charAt(i) - 'a']--;
  10. }
  11. for (int alp : alps) {
  12. if (alp != 0) {
  13. return false;
  14. }
  15. }
  16. return true;
  17. }

2016-12-26

290.单词模式(Word Pattern)

要求:按照Pattern如abba,造字符串str"dog cat cat dog",判断是否合规

思路1:(自写经修改成功)对应205题思路2,就是用一张map建立pattern与str之间的链接,判断新的是否与之前的一样

tips:由于这题判断的是String,要用equals方法

  1. public boolean wordPattern(String pattern, String str) {
  2. Map<Character, String> map = new HashMap<>();
  3. String[] words = str.split(" ");
  4. if (words.length != pattern.length()) {
  5. return false;
  6. }
  7. for (int i = 0; i < words.length; i++) {
  8. char c = pattern.charAt(i);
  9. String s = words[i];
  10. if (map.containsKey(c)) {
  11. if (!map.get(c).equals(s)) {
  12. return false;
  13. }
  14. } else if (map.containsValue(s)) {
  15. return false;
  16. } else {
  17. map.put(c, s);
  18. }
  19. }
  20. return true;
  21. }

(2ms)

思路2:(discuss)对应205思路1,不过这里可以用一张map记录位置,因为pattern的key是char,而str的key是string,彼此不相同

warn:若使用!= 来判断位置的话,位置太大时会报错

  1. public boolean wordPattern(String pattern, String str) {
  2. Map map = new HashMap();
  3. String[] words = str.split(" ");
  4. if (words.length != pattern.length()) {
  5. return false;
  6. }
  7. for (int i = 0; i < words.length; i++) {
  8. if (!Objects.equals(map.put(pattern.charAt(i), i), map.put(words[i], i))) { //此处用!=的话会超过长度
  9. return false;
  10. }
  11. }
  12. return true;
  13. }

(3ms)

2016-12-27

299.Bulls and Cows

要求:题目真难懂,就是两个四位数,位置与数字相同的时候bull++,位置不同但数字相同的时候cow++,但这种情况重复只算一个猜对

思路1:(discuss)用10位数组代表10个数(其实相当于长度为10的HashMap吧),当两个数字对应位置不相同时更新数组,然后用每个位上代表每种数的有无,判断大小于0确定是否有一样但位置不一样的

  1. public String getHint(String secret, String guess) {
  2. int bull = 0;
  3. int cow = 0;
  4. int[] nums = new int[10];
  5. for (int i = 0; i < secret.length(); i++) {
  6. if (secret.charAt(i) == guess.charAt(i)) {
  7. bull++;
  8. } else {
  9. //数字字符转成整数
  10. if (nums[secret.charAt(i) - '0']++ < 0) cow++;
  11. if (nums[guess.charAt(i) - '0']-- > 0) cow++;
  12. }
  13. }
  14. return bull + "A" + cow + "B";
  15. }

思路2:(discuss)用两个数组分别存储位置不同时各自的数,最后遍历一次对比哪个少,只有guess数组中大于等于1时才会有cow,代码略

2016-12-27

349.两数组交集(Intersection of Two Arrays)

要求:把两数组交集无重复地列出

思路1:(自写一次AC)two pointers,先排序,后用ArrayList(需判断同个数组相邻两个是否重复)或者HashSet存储相同的数,O(nlogn)

  1. public int[] intersection(int[] nums1, int[] nums2) {
  2. Arrays.sort(nums1);
  3. Arrays.sort(nums2);
  4. int i = 0, j = 0;
  5. List<Integer> list = new ArrayList<Integer>();
  6. while (i < nums1.length && j < nums2.length) {
  7. if (nums1[i] == nums2[j]) {
  8. list.add(nums1[i]);
  9. while (i < nums1.length - 1 && nums1[i] == nums1[i + 1]) i++;
  10. while (j < nums2.length - 1 && nums2[j] == nums2[j + 1]) j++;
  11. i++;
  12. j++;
  13. } else if (nums1[i] < nums2[j]) {
  14. i++;
  15. } else {
  16. j++;
  17. }
  18. }
  19. int[] res = new int[list.size()];
  20. for (int k = 0; k < list.size(); k++) {
  21. res[k] = list.get(k);
  22. }
  23. return res;
  24. }

(ArrayList)

思路2:(自写修改)两个set,第一个set排除原数组重复,第二次循环时判断当第一个set中有当前数时再添加,第二个set防止了子数组的重复,O(n)

  1. public int[] intersection(int[] nums1, int[] nums2) {
  2. Set<Integer> set1 = new HashSet<Integer>();
  3. Set<Integer> set2 = new HashSet<Integer>();
  4. for (int num : nums1) {
  5. set1.add(num);
  6. }
  7. for (int num : nums2) {
  8. if (set1.contains(num)) {
  9. set2.add(num);
  10. }
  11. }
  12. int[] res = new int[set2.size()];
  13. int i = 0;
  14. for (int num : set2) {
  15. res[i++] = num;
  16. }
  17. return res;
  18. }

思路3:(discuss)二分查找,遍历nums1,查找nums2中与nums1相同的数,放入set中防止重复,O(nlogn),代码略

2016-12-27

350.两数组交集II(Intersection of Two Arrays II)

要求:重复也要列出

思路和349完全一样,还不用判断结果是否有重复,给出ArrayList存储代码吧

  1. public int[] intersect(int[] nums1, int[] nums2) {
  2. Arrays.sort(nums1);
  3. Arrays.sort(nums2);
  4. int i = 0, j = 0;
  5. List<Integer> list = new ArrayList<Integer>();
  6. while (i < nums1.length && j < nums2.length) {
  7. if (nums1[i] == nums2[j]) {
  8. list.add(nums1[i]);
  9. i++;
  10. j++;
  11. } else if (nums1[i] < nums2[j]) {
  12. i++;
  13. } else {
  14. j++;
  15. }
  16. }
  17. int[] res = new int[list.size()];
  18. for (int k = 0; k < list.size(); k++) {
  19. res[k] = list.get(k);
  20. }
  21. return res;
  22. }

2016-12-27

389.找不同(Find the Difference)

要求:找出两个字符串中,某字符串中多出来的那个

思路:(自写)异或,char也可以直接运算,求和也行

tips:把结果首先赋成较长字符串最后的那个字符,要用^=

  1. public char findTheDifference(String s, String t) {
  2. char c = t.charAt(t.length() - 1);
  3. for (int i = 0; i < s.length(); i++) {
  4. c ^= (s.charAt(i) ^ t.charAt(i));
  5. }
  6. return c;
  7. }

不能用set直接做,因为有可能插入的是已存在的字符

2016-12-28

409.最长回文(Longest Palindrome)

要求:用字符串的字符以任意顺序组成一个最长的对称字符串,返回长度

思路1:(discuss+自修改)需要出现双数次才能对称,每次set中有重复时就去掉这个字符并count++(也就是记录出现双数次),没重复就是出现单数次正常添加,最后把count * 2就是总次数,判断此时set是否为空可确定回文长度的奇偶

  1. public int longestPalindrome(String s) {
  2. Set<Character> set = new HashSet<>();
  3. int count = 0;
  4. char[] cc = s.toCharArray();
  5. //计算出现的双数次保证对称
  6. for (char c : cc) {
  7. if (set.contains(c)) {
  8. set.remove(c);
  9. count++;
  10. } else {
  11. set.add(c);
  12. }
  13. }
  14. //判断回文长度是奇数还是偶数
  15. return set.isEmpty() ? count * 2 : count * 2 + 1;
  16. }

(21ms)

思路2:(discuss)分别用两个26位数组代表52个大小写字母,然后计算所有字母出现的次数

tips:除2再乘2可把奇数减1变成偶数(1变0),符合对称要求,a的ASCII码是97

  1. public int longestPalindrome(String s) {
  2. int[] lower = new int[26];
  3. int[] upper = new int[26];
  4. int res = 0;
  5. for (int i = 0; i < s.length(); i++) {
  6. char c = s.charAt(i);
  7. if (c >= 97) {
  8. lower[c - 'a']++;
  9. } else {
  10. upper[c - 'A']++;
  11. }
  12. }
  13. for (int i = 0; i < 26; i++) {
  14. //保证输出的都是双数
  15. res += (lower[i] / 2) * 2;
  16. res += (upper[i] / 2) * 2;
  17. }
  18. //只有等于原s长度时回文长度才是偶数
  19. return res == s.length() ? res : res + 1;
  20. }

(11ms)

2016-12-28

438.找出字符串所有颠倒的子字符串(Find All Anagrams in a String)

要求:如题,返回子字符串开始的位置,Input: s: "cbaebabacd" p: "abc"  Output: [0, 6]

思路:(discuss)Sliding Windows,(还没完全理解:假定窗口长度(right - left)是字符串p的长度并设置同样的count,两指针都从左边开始,rigth指针一直移动,相应位置上的字符数量一直--,出现p的字符时count--;left指针只有当两指针的差值等于p长度时才移动(确保窗口长度能够一直不变),移动时相应位置上的字符一直++(确保存储数组中各个字符的数量不变,包括p中不存在的),出现p的字符时count++;当count为0时输出left位置)

还不理解left移动时为什么要count++??也许是为了防止right已经遍历过p存在的数算多了一次   2016-12-28

  1. public List<Integer> findAnagrams(String s, String p) {
  2. List<Integer> list = new ArrayList<>();
  3. if (s == null || s.length() == 0 || p == null || p.length() == 0) return list;
  4. int[] hash = new int[256]; //character hash
  5. //record each character in p to hash
  6. for (char c : p.toCharArray()) {
  7. hash[c]++;
  8. }
  9. //two points, initialize count to p's length
  10. int left = 0, right = 0, count = p.length();
  11. while (right < s.length()) {
  12. //move right everytime, if the character exists in p's hash, decrease the count
  13. //current hash value >= 1 means the character is existing in p
  14. if (hash[s.charAt(right++)]-- >= 1) count--;
  15.  
  16. //when the count is down to 0, means we found the right anagram
  17. //then add window's left to result list
  18. if (count == 0) list.add(left);
  19.  
  20. //if we find the window's size equals to p, then we have to move left (narrow the window) to find the new match window
  21. //++ to reset the hash because we kicked out the left
  22. //only increase the count if the character is in p
  23. //the count >= 0 indicate it was original in the hash, cuz it won't go below 0
  24. if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;
  25. }
  26. return list;
  27. }

同类子字符串问题的总结:

https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems

还缺一些游戏题和堆排序的题没做

String

5.最长回文子字符串(Longest Palindromic Substring)

要求:找出字符串s中最长的对称子字符串

思路:(discuss)把s遍历一次,在每个位置i用two pointers判断最长的回文(判断是否与旁边字符相同确保回文长度为偶数的情况),超过之前的情况就更新回文开始点与长度

tips:为了每个方法都能调用变量,可定义类private属性,substring的用法,substring(a, b)是截取从a到b - 1的子字符串

  1. private int lo, maxLen; //定义类的private属性,使类中所有方法都可用
  2. public String longestPalindrome(String s) {
  3. if (s.length() < 2) {
  4. return s;
  5. }
  6.  
  7. //以每个位置为中心向两边找回文
  8. for (int i = 0; i < s.length() - 1; i++) {
  9. extendPalindrome(s, i, i); //寻找以i为中心的回文
  10. extendPalindrome(s, i, i + 1); //若i与i + 1相同,则中心为它们两个
  11. }
  12. return s.substring(lo, lo + maxLen);
  13. }
  14.  
  15. //two pointers以中心向头尾移动
  16. private void extendPalindrome(String s, int j, int k) {
  17. while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
  18. j--;
  19. k++;
  20. }
  21. if (maxLen < k - j - 1) {
  22. lo = j + 1;
  23. maxLen = k - j - 1;
  24. }
  25. }

2017-01-02

6.Z型变换(ZigZag Conversion)

要求:把字符串按照Z型分布排成numRows行,并逐行读出

思路:(discuss)创建numRows行的不定长度的存储空间(数组),然后逐个存进,最后逐行读出,用StringBuilder(Buffer)或者ArrayList存储

tips:用StringBuilder更快且更省空间,因为可用append一次过把一行都加起来,最后把后面的行都加在第一行就toString输出了,StringBuilder的线程不安全

  1. public String convert(String s, int numRows) {
  2. StringBuilder[] rows = new StringBuilder[numRows]; //数组
  3. for (int row = 0; row < numRows; row++) {
  4. rows[row] = new StringBuilder(); //数组每一个元素都是一个builder
  5. }
  6. int i = 0;
  7. //Z型存储进数组
  8. while (i < s.length()) {
  9. for (int row = 0; row < numRows && i < s.length(); row++) {
  10. rows[row].append(s.charAt(i++));
  11. }
  12. for (int row = numRows - 2; row > 0 && i < s.length(); row--) {
  13. rows[row].append(s.charAt(i++));
  14. }
  15. }
  16. //读取
  17. for (int row = 1; row < numRows; row++) {
  18. rows[0].append(rows[row]);
  19. }
  20. return rows[0].toString();
  21. }

(StringBuilder)

  1. public String convert(String s, int numRows) {
  2. ArrayList<Character>[] rows = (ArrayList<Character>[]) new ArrayList[numRows];
  3. for (int row = 0; row < numRows; row++) {
  4. rows[row] = new ArrayList<Character>();
  5. }
  6. int i = 0;
  7. //Z型存储进数组
  8. while (i < s.length()) {
  9. for (int row = 0; row < numRows && i < s.length(); row++) {
  10. rows[row].add(s.charAt(i++));
  11. }
  12. for (int row = numRows - 2; row > 0 && i < s.length(); row--) {
  13. rows[row].add(s.charAt(i++));
  14. }
  15. }
  16. //读取
  17. StringBuilder sb = new StringBuilder(s.length());
  18. for (int row = 0; row < numRows; row++) {
  19. for (int col = 0; col < rows[row].size(); col++) {
  20. sb.append(rows[row].get(col));
  21. }
  22. }
  23. return sb.toString();
  24. }

(ArrayList)

2017-01-02

8.字符串转整数(String to Integer)

要求:考虑各种情况

思路:(discuss)考虑四方面:1、空字符串 2、去掉前面的空格 3、判断正负 4、逐个字符转成整数并加起来,判断是否溢出

tips:字符转整数可- ‘0’也可用Character.getNumericValue(char)

  1. public int myAtoi(String str) {
  2. int index = 0, sign = 1, total = 0;
  3. //1. Empty string
  4. if (str.length() == 0) {
  5. return 0;
  6. }
  7.  
  8. //2. Remove Spaces
  9. while (str.charAt(index) == ' ' && index < str.length()) {
  10. index++;
  11. }
  12.  
  13. //3. Handle signs
  14. if (str.charAt(index) == '+' || str.charAt(index) == '-') {
  15. sign = str.charAt(index) == '+' ? 1 : -1;
  16. index++;
  17. }
  18.  
  19. //4. Convert number and avoid overflow
  20. while (index < str.length()) {
  21. int digit = str.charAt(index) - '0'; //char to int
  22. if(digit < 0 || digit > 9) break; //if not a number, break
  23.  
  24. //check if total will be overflow after 10 times and add digit
  25. if (Integer.MAX_VALUE / 10 < total || Integer.MAX_VALUE / 10 == total && Integer.MAX_VALUE % 10 < digit) {
  26. return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
  27. }
  28. total = 10 * total + digit;
  29. index++;
  30. }
  31. return total * sign;
  32. }

2017-01-02

12.整数转罗马数字(Integer to Roman)

要求:如题

思路:(discuss)最主要是罗马数字个十百千怎么表示,然后直接除就完了 1 5 10 50 100 500 1000分别是I V X L C D M

  1. public static String intToRoman(int num) {
  2. String M[] = {"", "M", "MM", "MMM"};
  3. String C[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
  4. String X[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
  5. String I[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
  6. return M[num / 1000] + C[(num % 1000) / 100] + X[(num % 100) / 10] + I[num % 10];
  7. }

2017-01-02

13.罗马数字转整数(Roman to Integer)

要求:如题

思路1:(discuss)把所有'4'当成'6','9'当成'11'先相加,然后减去出现4,9的情况

  1. public int romanToInt(String s) {
  2. int sum = 0;
  3. //排除个十百位上出现的4,9
  4. if (s.indexOf("IV") != -1) sum -= 2;
  5. if (s.indexOf("IX") != -1) sum -= 2;
  6. if (s.indexOf("XL") != -1) sum -= 20;
  7. if (s.indexOf("XC") != -1) sum -= 20;
  8. if (s.indexOf("CD") != -1) sum -= 200;
  9. if (s.indexOf("CM") != -1) sum -= 200;
  10.  
  11. char[] c = s.toCharArray();
  12. for (int i = 0; i < s.length(); i++) {
  13. if (c[i] == 'M') sum += 1000;
  14. if (c[i] == 'D') sum += 500;
  15. if (c[i] == 'C') sum += 100;
  16. if (c[i] == 'L') sum += 50;
  17. if (c[i] == 'X') sum += 10;
  18. if (c[i] == 'V') sum += 5;
  19. if (c[i] == 'I') sum += 1;
  20. }
  21. return sum;
  22. }

思路2:(自写AC)用Map或者数组来存储字母与数字的对应关系,然后按照罗马数字的规则来算,比如I在V左边,就是V(5) - I(1),否则就是加(就是判断前后两个字母对应的数字的大小

  1. public int romanToInt(String s) {
  2. Map<Character, Integer> map = new HashMap<>();
  3. map.put('I', 1);
  4. map.put('V', 5);
  5. map.put('X', 10);
  6. map.put('L', 50);
  7. map.put('C', 100);
  8. map.put('D', 500);
  9. map.put('M', 1000);
  10.  
  11. char[] c = s.toCharArray();
  12. int sum = map.get(c[c.length - 1]);
  13. for (int i = 0; i < s.length() - 1; i++) {
  14. if (map.get(c[i]) < map.get(c[i + 1])) {
  15. sum -= map.get(c[i]);
  16. } else {
  17. sum += map.get(c[i]);
  18. }
  19. }
  20. return sum;
  21. }

2017-01-02

14.最长相同前缀(Longest Common Prefix)

要求:找出字符串数组中,所有字符串共同的最长前缀

思路1:(自写修改后AC)用str[0]与后面的相比,比到maxLen或者字符不同为止,然后更新maxLen

warn:注意数组的边界条件,留意后面的字符串的长度有可能比之前的maxLen还要小

  1. public String longestCommonPrefix(String[] strs) {
  2. if (strs.length == 0) {
  3. return "";
  4. }
  5. int maxLen = strs[0].length();
  6. for (int i = 1; i < strs.length; i++) {
  7. maxLen = Math.min(maxLen, strs[i].length());
  8. int j = 0;
  9. while (j < maxLen && strs[0].charAt(j) == strs[i].charAt(j)) {
  10. j++;
  11. }
  12. maxLen = j;
  13. }
  14. return strs[0].substring(0, maxLen);
  15. }

思路2:(discuss+自改)把字符串数组排序,然后比较头尾两个,因为头尾两个的差异最大,判断它们两个最长前缀即是结果

  1. public String longestCommonPrefix(String[] strs) {
  2. if (strs.length == 0) {
  3. return "";
  4. }
  5. //排序确保了中间项出现不一样的问题
  6. Arrays.sort(strs);
  7. char[] a = strs[0].toCharArray();
  8. char[] b = strs[strs.length - 1].toCharArray();
  9. int i = 0;
  10. while (i < a.length && i < b.length && a[i] == b[i]) {
  11. i++;
  12. }
  13. return strs[0].substring(0, i);
  14. }

另:还可以用indexOf是否为0来判断一个子字符串是否存在,且是否在头部开始

2017-01-04

20.有效括号(Vaild Parentheses)

要求:每个(, [, {要有对用的), ], }

思路1:(discuss)用左括号进,当前输入的右括号满足与上一次输入的左括号对应就弹,否则出错

tips:最后判断栈是否为空,就能确定每一个左括号是否都有对应的右括号,easy to understand

  1. public boolean isValid(String s) {
  2. Stack<Character> stack = new Stack<Character>();
  3. //如果左括号则入栈,有上一个入栈对应的右括号则出栈,否则错误
  4. for (int i = 0; i < s.length(); i++) {
  5. if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
  6. stack.push(s.charAt(i));
  7. } else if (s.charAt(i) == ')' && !stack.empty() && stack.peek() == '(') {
  8. stack.pop();
  9. } else if (s.charAt(i) == ']' && !stack.empty() && stack.peek() == '[') {
  10. stack.pop();
  11. } else if (s.charAt(i) == '}' && !stack.empty() && stack.peek() == '{') {
  12. stack.pop();
  13. } else {
  14. return false;
  15. }
  16. }
  17. //判断是否为空,确保都有右括号对应
  18. return stack.empty();
  19. }

(9ms)

思路2:(discuss)若当前的是左括号,则让它对应的右括号入栈;若当前的是右括号,则栈为空(意味着把左括号的情况全弹完)或者不等于上次输入的右括号的时候就出错

tips:可以把pop()和判断相等同时进行,more simple

  1. public boolean isValid(String s) {
  2. Stack<Character> stack = new Stack<Character>();
  3. //入栈的是与当前左括号对应的右括号
  4. for (char c : s.toCharArray()) {
  5. if (c == '(') {
  6. stack.push(')');
  7. } else if (c == '[') {
  8. stack.push(']');
  9. } else if (c == '{') {
  10. stack.push('}');
  11. } else if (stack.empty() || stack.pop() != c) {
  12. return false;
  13. }
  14. }
  15. return stack.empty();
  16. }

(8ms)

2017-01-04

28.实现strStr()函数(Implement strStr())

要求:实现Java中String类的indexOf()

思路:(自写AC)最笨的方法,不考虑KMP等算法,一位位的比较字符串与目标子字符串是否一样

  1. public int strStr(String haystack, String needle) {
  2. int m = haystack.length();
  3. int n = needle.length();
  4. for (int i = 0; i <= m - n; i++) {
  5. for (int j = 0; ; j++) {
  6. if (j == n) return i;
  7. if (needle.charAt(j) != haystack.charAt(i + j)) break;
  8. }
  9. }
  10. return -1;
  11. }

还有更高级的算法有待学习

2017-01-04

38.Count and Say(数和读)

要求:初始为1,第二个是“1个1”,即11,第三个是两个1,即“21”

思路:(discuss)找出每一个字符串,并读上一个字符串时,按照其规律,相同就继续数count++,不相同就say(添加到新的字符串中)

  1. public String countAndSay(int n) {
  2. StringBuilder cur = new StringBuilder("1");
  3. StringBuilder pre;
  4. //标记要说的字符及其数量
  5. int count;
  6. char say;
  7. //从头开始数
  8. for (int i = 1; i < n; i++) {
  9. pre = cur;
  10. //存放say之后的字符串
  11. cur = new StringBuilder();
  12. count = 1;
  13. say = pre.charAt(0);
  14. for (int j = 1; j < pre.length(); j++) {
  15. if (pre.charAt(j) == say) {
  16. count++;
  17. } else {
  18. cur.append(count).append(say);
  19. count = 1;
  20. say = pre.charAt(j);
  21. }
  22. }
  23. cur.append(count).append(say);
  24. }
  25. return cur.toString();
  26. }

2017-01-05

43.字符串相乘(Multiply Strings)

要求:返回两个字符串相乘的结果,它们所代表的数字不大于110,而且头部没有0

思路:(discuss)用数组存储相乘结果,按照相乘法则,记录每次相乘后所放的位置,并把个位与上一次循环中的十位相加,再把当前循环的十位(十位上的数要加上本来在该位的数)个位放进去相应位置

tips:每次循环中的数字相乘为0~9,其乘积只有两位

  1. public String multiply(String num1, String num2) {
  2. //用数组来存储相乘结果
  3. int[] pos = new int[num1.length() + num2.length()];
  4. //从末尾开始相乘
  5. for (int i = num1.length() - 1; i >= 0; i--) {
  6. for (int j = num2.length() - 1; j >= 0; j--) {
  7. //字符转数字后相乘
  8. int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
  9. //当前乘积的十位个位应当放的位置
  10. int p1 = i + j, p2 = i + j + 1;
  11. //算出与上一次循环中十位上的数的和
  12. int sum = mul + pos[p2];
  13. //算出当前乘积的最后结果的十位个位
  14. pos[p1] += sum / 10;
  15. pos[p2] = sum % 10;
  16. }
  17. }
  18.  
  19. //把数组转字符串
  20. StringBuilder res = new StringBuilder();
  21. for (int p : pos) {
  22. //不输入头部的0
  23. if (!(res.length() == 0 && p == 0)) {
  24. res.append(p);
  25. }
  26. }
  27. //判断是否为0
  28. return res.length() == 0 ? "0" : res.toString();
  29. }

2017-01-05

58.最后一个单词的长度(Length of Last Word)

要求:以' '分割单词,求最后一个单词长度

思路1:(自写AC)从尾判断,不是空格就len++,是空格且len不等于0(避免空格在末尾的情况)就跳出

  1. public int lengthOfLastWord(String s) {
  2. int len = 0;
  3. for (int i = s.length() - 1; i >= 0; i--) {
  4. if (s.charAt(i) != ' ') {
  5. len++;
  6. }
  7. if (s.charAt(i) == ' ' && len != 0) {
  8. break;
  9. }
  10. }
  11. return len;
  12. }

思路2:(discuss)用lastIndexOf判断最后一个' '的位置

  1. public int lengthOfLastWord(String s) {
  2. return s.trim().length()-s.trim().lastIndexOf(" ")-1;
  3. }

2017-01-05

67.二进制相加(Add Binary)

要求:给出两个二进制的字符串,返回相加后的二进制的字符串

思路:(discuss)自写思路虽对,但太复杂,逐位算时,每次相加结果sum % 2是该位的数,sum / 2是进位(和用异或、与那题不同)

tips:记得最后一个进位,从个位开始加就放进StringBuilder里面,最后可以转置reverse()

  1. public String addBinary(String a, String b) {
  2. StringBuilder res = new StringBuilder();
  3. int i = a.length() - 1, j = b.length() - 1, ca = 0;
  4. while (i >= 0 || j >= 0) {
  5. int sum = ca;
  6. if (i >= 0) sum += a.charAt(i--) - '0';
  7. if (j >= 0) sum += b.charAt(j--) - '0';
  8. //余数是该位结果,商是进位
  9. res.append(sum % 2);
  10. ca = sum / 2;
  11. }
  12. //最后一个进位
  13. if (ca == 1) res.append(1);
  14. //转置
  15. return res.reverse().toString();
  16. }

2017-01-05

125.有效回文(Valid Palindrome)

要求:判断一个字符串是不是回文,但不算除字母和数字外的字符,大小写忽略

思路:(知道怎么做但写不出来)two pointers在首尾向里移动,排除非字母数字,转为小写,不同返回错误

tips:排除可用isLetterOrDigit()方法,转小写用toLowerCase()

warn:要考虑只含非字母数字的情况

  1. public boolean isPalindrome(String s) {
  2. int i = 0, j = s.length() - 1;
  3. while (i < j) {
  4. //判断是否数字或字母
  5. char a = s.charAt(i);
  6. char b = s.charAt(j);
  7. if (!Character.isLetterOrDigit(a)) {
  8. i++;
  9. } else if (!Character.isLetterOrDigit(b)) {
  10. j--;
  11. } else {
  12. if (Character.toLowerCase(a) != Character.toLowerCase(b)) {
  13. return false;
  14. }
  15. i++;
  16. j--;
  17. }
  18. }
  19. return true;
  20. }

2017-01-06

344.旋转字符串(Reverse String)

要求:如题

思路:(自写AC)简单

  1. public String reverseString(String s) {
  2. StringBuilder res = new StringBuilder();
  3. for (int i = s.length() - 1; i >= 0; i--) {
  4. res.append(s.charAt(i));
  5. }
  6. return res.toString();
  7. }

还有多种用位运算、首尾交换的方法

2017-01-06

345.旋转字符串中的元音字母(Reverse Vowels of a String)

要求:如题

思路:(有思路但写不出)two pointers首尾向里移动,判断是否元音,两边都是元音的时候则交换

tips:用String代替Set保存元音,也可以用contains()方法,但Set读取更快

warn:要转成字符数组才能交换,字符数组转回字符串可用New String(char[])

  1. public String reverseVowels(String s) {
  2. //string直接用作set
  3. String vowels = "aeiouAEIOU";
  4. char[] res = s.toCharArray();
  5. int i = 0, j = s.length() - 1;
  6. while (i < j) {
  7. while (i < j && !vowels.contains(res[i] + "")) i++;
  8. while (i < j && !vowels.contains(res[j] + "")) j--;
  9. //i = j时与自己交换
  10. char c = res[i];
  11. res[i] = res[j];
  12. res[j] = c;
  13. i++;
  14. j--;
  15. }
  16. return new String(res);
  17. }

2017-01-06

383.Ransom Note

要求:用字符串magazine里含有的字符来组成ransomNote,m中字符只能用一次

思路:(自写AC)用26位数组存储各个字符的数量

  1. public boolean canConstruct(String ransomNote, String magazine) {
  2. int[] chars = new int[26];
  3. for (int i = 0; i < ransomNote.length(); i++) {
  4. chars[ransomNote.charAt(i) - 'a']++;
  5. }
  6. for (int i = 0; i < magazine.length(); i++) {
  7. chars[magazine.charAt(i) - 'a']--;
  8. }
  9. for (int c : chars) {
  10. if (c > 0) {
  11. return false;
  12. }
  13. }
  14. return true;
  15. }

discuss写得更simple

  1. public boolean canConstruct(String ransomNote, String magazine) {
  2. int[] arr = new int[26];
  3. for (int i = 0; i < magazine.length(); i++) {
  4. arr[magazine.charAt(i) - 'a']++;
  5. }
  6. for (int i = 0; i < ransomNote.length(); i++) {
  7. if(--arr[ransomNote.charAt(i)-'a'] < 0) {
  8. return false;
  9. }
  10. }
  11. return true;
  12. }

2017-01-09

434.字符串的分区数目(Number of Segments in a String)

要求:用空格来分开字符串,求字符串的分区数

思路1:(自写修改AC)数空格数来确定分区数

warn:1、空字符串(返回0) 2、头部有空格(决定赋值res是0还是1) 3、分区之间有多个空格(移动到没空格为止) 4、尾部有空格(若最后一个仍是空格则不计数)

  1. public int countSegments(String s) {
  2. if (s.length() == 0) {
  3. return 0;
  4. }
  5. //保证开始不是空格
  6. int res = s.charAt(0) == ' ' ? 0 : 1;
  7. for (int i = 0; i < s.length(); i++) {
  8. if (s.charAt(i) == ' ') {
  9. //排除中间有多个空格
  10. while (i < s.length() - 1 && s.charAt(i + 1) == ' ') i++;
  11. //排除最后一个是空格
  12. if (i != s.length() - 1) res++;
  13. }
  14. }
  15. return res;
  16. }

思路2:(discuss)数出现i不是空格,且i - 1是空格的情况来确定分区,more simple

warn:注意排除首字符无i - 1的情况

  1. public int countSegments(String s) {
  2. int res=0;
  3. for(int i=0; i<s.length(); i++)
  4. if(s.charAt(i)!=' ' && (i==0 || s.charAt(i-1)==' '))
  5. res++;
  6. return res;
  7. }

2017-01-09

459.重复子字符串模式(Repeated Substring Pattern)

要求:判断一个字符串是否由一个子字符串复制而成

思路1:(自写但一直没AC?)用数组先存储字符出现次数,然后判断每个字符应该出现一样次数

  1. public boolean repeatedSubstringPattern(String str) {
  2. int[] chars = new int[26];
  3. for (int i = 0; i < str.length(); i++) {
  4. chars[str.charAt(i) - 'a']++;
  5. }
  6. int temp = 0;
  7. for (int c : chars) {
  8. if (temp == 0 && c != 0) {
  9. temp = c;
  10. } else if (c != temp) {
  11. return false;
  12. }
  13. }
  14. return true;
  15. }

???

思路2:(dicuss)从长度一半开始判断,若能整除str的长度,则复制从开始到截取点的子字符串,再与str比较(其实就是根据题意)

  1. public boolean repeatedSubstringPattern(String str) {
  2. int len = str.length();
  3. for (int i = len / 2; i > 0; i--) {
  4. //若被整除则有可能组成str
  5. if (len % i == 0) {
  6. int m = len / i;
  7. String subStr = str.substring(0, i);
  8. StringBuilder sb = new StringBuilder();
  9. //复制m次判断是否str
  10. for (int j = 0; j < m; j++) {
  11. sb.append(subStr);
  12. }
  13. if (sb.toString().equals(str)) {
  14. return true;
  15. }
  16. }
  17. }
  18. return false;
  19. }

2017-01-09

Math

7.反转数字(Reverse Integer)

要求:如题

思路1:(discuss)由于头尾反转,每次取末尾,然后把上次的乘以10(左移)就行

  1. public int reverse(int x) {
  2. long rev = 0;
  3. while (x != 0) {
  4. //把之前保存的左移
  5. rev = 10 * rev + x % 10;
  6. x = x /10;
  7. //判断溢出
  8. if (rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE) {
  9. return 0;
  10. }
  11. }
  12. return (int)rev;
  13. }

思路2:(自想)用字符串强行反转,但这样处理溢出很麻烦

2017-01-09

9.回文数字(Palindrome Number)

要求:如题,不实用额外空间

思路1:(自写AC)反转后判断是否一样,方法与7题一样,不过翻转后有可能溢出

  1. public boolean isPalindrome(int x) {
  2. if (x < 0) {
  3. return false;
  4. }
  5. int rev = 0;
  6. int temp = x;
  7. while (temp != 0) {
  8. rev = rev * 10 + temp % 10;
  9. temp /= 10;
  10. }
  11. return x == rev;
  12. }

(有缺陷)

思路2:(discuss)反转一半看是否一样,更快但难理解,防止了溢出

warn:考虑奇偶长度

  1. public boolean isPalindrome(int x) {
  2. if (x < 0 || (x != 0 && x % 10 == 0)) { //末尾为0不可能回文
  3. return false;
  4. }
  5. int rev = 0;
  6. //判断一半是否相同
  7. while (x > rev) {
  8. rev = rev * 10 + x % 10;
  9. x /= 10;
  10. }
  11. return (x == rev || x == rev / 10); //考虑奇偶数
  12. }

2017-01-09

29.整数相除(Divide Two Integers)

要求:不能用乘除,取余数

思路:(discuss)递归,从1个ldivisor开始叠加,数到ldividend

tips:进入递归,每次递归重置ldividend,ldivisor不变,跳出递归,设置条件ldividend < ldivisor(return 0应在递归表达式return ldivide(n - 1)之前才能跳出)

  1. public int divide(int dividend, int divisor) {
  2. int sign = 1;
  3. if ((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0)) {
  4. sign = -1;
  5. }
  6. if (divisor == 0) {
  7. return Integer.MAX_VALUE;
  8. }
  9. long ldividend = Math.abs((long)dividend);
  10. long ldivisor = Math.abs((long)divisor);
  11. if (ldividend < ldivisor) {
  12. return 0;
  13. }
  14. long res = ldivide(ldividend, ldivisor);
  15. int ans;
  16. if (res > Integer.MAX_VALUE) {
  17. return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
  18. } else {
  19. ans = (int)((sign == 1) ? res : -res);
  20. }
  21. return ans;
  22. }
  23. private long ldivide(long ldividend, long ldivisor) {
  24. //增加这句能跳出递归
  25. if (ldividend < ldivisor) {
  26. return 0;
  27. }
  28. long sum = ldivisor;
  29. long multiple = 1;
  30. while ((sum + sum) <= ldividend) {
  31. sum += sum;
  32. multiple += multiple;
  33. }
  34. return multiple + ldivide(ldividend - sum, ldivisor);
  35. }

2017-01-11

50.Pow(x, n)???

  1. public double myPow(double x, int n) {
  2. double ans = 1;
  3. long absN = Math.abs((long)n);
  4. while(absN > 0) {
  5. if((absN & 1) == 1) ans *= x;
  6. absN >>= 1;
  7. x *= x;
  8. }
  9. return n < 0 ? 1 / ans : ans;
  10. }

2017-01-11

168.Excel表格列名(Excel Sheet Column Title)

要求:1 -> 'A', 2 -> 'B', 27 -> 'AA'...

思路:(自写修改AC)把n整除26得到末尾,然后更新n(n /= 26),原理与十进制时候一样

warn:由于1对应A而不是0,故要先--n再整除26,还有记得reverse()

  1. public String convertToTitle(int n) {
  2. StringBuilder res = new StringBuilder();
  3. while (n != 0) {
  4. res.append((char)('A' + --n % 26));
  5. n /= 26;
  6. }
  7. return res.reverse().toString();
  8. }

2017-01-11

171.Excel表格列数(Excel Sheet Column Number)

要求:171相反过来

思路:(自写AC)从字符串头部开始读,然后上一次的 * 26,简单

  1. public int titleToNumber(String s) {
  2. int sum = 0;
  3. for (int i = 0; i < s.length(); i++) {
  4. int temp = s.charAt(i) - 'A' + 1;
  5. sum = sum * 26 + temp;
  6. }
  7. return sum;
  8. }

2017-01-12

172.阶乘的尾0(Factorial Trailing Zeroes)

要求:如题

思路:(自写AC)做过,数5的个数

  1. public int trailingZeroes(int n) {
  2. int num = 0;
  3. while (n != 0) {
  4. n /= 5;
  5. num += n;
  6. }
  7. return num;
  8. }

2017-01-12

223.Rectangle Area(矩形面积)

要求:求有可能重叠的两个举行组成的面积之和,其中A、B、C、D代表一个举行的左下和右上的顶点坐标,E、F、G、H代表另一个

思路:(discuss)求出重叠面积,巧用max来包括两矩形不重叠的情况

  1. public int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) {
  2. //已假设C > A, D > B; G > E, H > F
  3. int left = Math.max(A, E), right = Math.max(Math.min(C, G), left); //防止两长方形左右不重叠
  4. int bottom = Math.max(B, F), top = Math.max(Math.min(D, H), bottom); //防止上下不重叠
  5. return (C - A) * (D - B) + (G - E) * (H - F) - (right - left) * (top - bottom);
  6. }

2017-01-16

231.2的次方(Power of Two)

要求:判断是否二的次方

思路1:(discuss)由于2的次方只有1个位上是1,减去1与自己位运算的结果为0

  1. public boolean isPowerOfTwo(int n) {
  2. if (n <= 0) return false;
  3. //2的次方对应二进制只有一个位是1
  4. return (n & (n - 1)) == 0;
  5. }

思路2:(discuss)直接数1的个数,bitCount()方法

  1. public boolean isPowerOfTwo(int n) {
  2. return n > 0 && Integer.bitCount(n) == 1;
  3. }

2017-01-16

258.叠加数字(Add Digits)

要求:把数字的每一位加起来,直到只有个位数

思路1:(自写AC)按照题意,转成String遍历,暴力解法

  1. public int addDigits(int num) {
  2. while (num / 10 != 0) {
  3. int sum = 0;
  4. String numStr = num + "";
  5. char[] numArr = numStr.toCharArray();
  6. for (char c : numArr) {
  7. int temp = c - '0';
  8. sum += temp;
  9. }
  10. num = sum;
  11. }
  12. return num;
  13. }

思路2:(discuss)利用9的性质,被9整除的数最终加起来是等于9,否则加起来是余数

  1. public int addDigits(int num) {
  2. return num == 0 ? 0 : (num % 9 == 0 ? 9 : (num % 9));
  3. }

更优美的写法:

  1. public int addDigits(int num) {
  2. return 1 + (num - 1) % 9;
  3. }

2017-01-16

263.丑数(Ugly Number)

要求:判断是否只被质数2, 3, 5整除,若是则为丑数

思路:(自写AC)三个if,能整除则除,最后为1则是丑数

warn:排除0,排除0,排除0

  1. public boolean isUgly(int num) {
  2. while (num != 0 && (num % 2 == 0 || num % 3 == 0 || num % 5 == 0)) {
  3. if (num % 2 == 0) {
  4. num /= 2;
  5. }
  6. if (num % 3 == 0) {
  7. num /= 3;
  8. }
  9. if (num % 5 == 0) {
  10. num /= 5;
  11. }
  12. }
  13. return num == 1;
  14. }

2017-01-16

264.丑数II(Ugly Number II)

要求:算出第n个丑数

思路:(自写AC)三个指针,与前一数比较,添加最小的一个数(动态规划...)

  1. public int nthUglyNumber(int n) {
  2. List<Integer> uglys = new ArrayList<>();
  3. uglys.add(1);
  4. int p2 = 0, p3 = 0, p5 = 0;
  5. for (int i = 0; i <= n - 2; i++) {
  6. int lastNum = uglys.get(i);
  7. while (uglys.get(p2) * 2 <= lastNum) p2++;
  8. while (uglys.get(p3) * 3 <= lastNum) p3++;
  9. while (uglys.get(p5) * 5 <= lastNum) p5++;
  10. uglys.add(Math.min(uglys.get(p5) * 5, //三个数最小值
  11. Math.min(uglys.get(p2) * 2,
  12. uglys.get(p3) * 3)));
  13. }
  14. return uglys.get(n - 1);
  15. }

2017-01-16

319.灯泡开关(Bulb Switcher)

要求:进行到第i次操作时,凡是含有i为因子的灯泡序号,都进行一次开关操作

思路:(discuss)除了平方数,都有一个对称的数将其熄灭,所以直接算开方就行

  1. public int bulbSwitch(int n) {
  2. return (int)Math.sqrt(n);
  3. }

2017-01-16

326.3的次方(Power of Three)

要求:判断一个数是否3的次方,不能用循环

思路:(cheating)直接算出int中最大的3的次方...还有其他数学运算上的方法,没意思

  1. public boolean isPowerOfThree(int n) {
  2. return n>0 && 1162261467 % n == 0;
  3. }

2017-01-16

365.水桶问题(Water and Jug Problem)

要求:给出x和y容量的水桶,求能否得出z容量的水

思路:(search)根据定理,有解的情况就是x和y的最大公约数能整除z

tips:记住求最大公约数的方法,也就是一直做%运算

  1. public boolean canMeasureWater(int x, int y, int z) {
  2. return z == 0 || x + y >= z && z % gcd(x, y) == 0;
  3. }
  4. //求最大公约数
  5. private int gcd(int x, int y) {
  6. return y == 0 ? x : gcd(y, x % y);
  7. }

2017-01-16

367.平方数(Valid Perfect Square)

要求:判断一个数是否为某数的平方

思路1:(discuss)二分法,查找1到num是否有数乘以自己是num

warn:定义为long类型防止溢出

  1. public boolean isPerfectSquare(int num) {
  2. long low = 1, high = num;
  3. while (low <= high) {
  4. long mid = (low + high) / 2;
  5. if (mid * mid == num) {
  6. return true;
  7. } else if (mid * mid < num) {
  8. low = mid + 1;
  9. } else {
  10. high = mid - 1;
  11. }
  12. }
  13. return false;
  14. }

(Olog(n))

思路2:(discuss)平方数可以拆分成1 + 3 + 5 + ...

  1. public boolean isPerfectSquare(int num) {
  2. int i = 1;
  3. while (num > 0) {
  4. num -= i;
  5. i += 2;
  6. }
  7. return num == 0;
  8. }

(O(sqrt(n)))

2017-01-16

415.字符串相加(Add Strings)

要求:如题

思路:(自写AC)把每一位加起来就行

tips:for循环中只要有一个不溢出或还有进位就进入,进入后才判断有没有溢出的

  1. public String addStrings(String num1, String num2) {
  2. StringBuilder res = new StringBuilder();
  3. int ca = 0;
  4. for (int i = num1.length() - 1, j = num2.length() - 1;
  5. i >= 0 || j >= 0 || ca == 1; i--, j--) {
  6. int x = 0, y = 0;
  7. if (i >= 0) {
  8. x = num1.charAt(i) - '0';
  9. }
  10. if (j >= 0) {
  11. y = num2.charAt(j) - '0';
  12. }
  13. int sum = x + y + ca;
  14. res.append(sum % 10);
  15. ca = (sum >= 10) ? 1 : 0;
  16. }
  17. return res.reverse().toString();
  18. }

2017-01-16

Stack

456. 132 Pattern

要求:判断是否数组中能找出满足i < j < k,ai < ak < aj的三个数

思路:把一对数(两个)放进stack中,然后分析它们与第三个数的关系

  1. public class Solution {
  2. class Pair {
  3. int min, max;
  4. public Pair(int min, int max) {
  5. this.min = min;
  6. this.max = max;
  7. }
  8. }
  9. public boolean find132pattern(int[] nums) {
  10. Stack<Pair> stack = new Stack();
  11. for (int n : nums) {
  12. if (stack.isEmpty() || n < stack.peek().min) {
  13. //压入较小的新元素n
  14. stack.push(new Pair(n, n));
  15. }
  16. else if (n > stack.peek().min) {
  17. Pair last = stack.pop();
  18. if (n < last.max) {
  19. return true;
  20. }
  21. else {
  22. last.max = n;
  23. //把不够新元素n大的对都弹出
  24. while (!stack.isEmpty() && n >= stack.peek().max) {
  25. stack.pop();
  26. }
  27. if (!stack.isEmpty() && stack.peek().min < n) {
  28. return true;
  29. }
  30. stack.push(last);
  31. }
  32. }
  33. }
  34. return false;
  35. }
  36. }

Leetcode分类刷题答案&心得的更多相关文章

  1. [LeetCode] 系统刷题5_Dynamic Programming

    Dynamic Programming 实际上是[LeetCode] 系统刷题4_Binary Tree & Divide and Conquer的基础上,加上记忆化的过程.就是说,如果这个题 ...

  2. leetcode top-100-liked-questions刷题总结

    一.起因 宅在家中,不知该做点什么.没有很好的想法,自己一直想提升技能,语言基础自不必言,数据结构还算熟悉,算法能力一般.于是乎,就去刷一通题. 刷题平台有很多,我选择了在leetcode进行刷题.回 ...

  3. LeetCode的刷题利器(伪装到老板都无法diss你没有工作)

    在工程效率大行其道的今天,如果不会写点代码以后也不容易在测试圈混下去.今天给大家推荐一个LeetCode的刷题利器,可以伪装到连你老板在这里走过去都无法确认你是在干活呢,还是在干活呢. LeetCod ...

  4. LeetCode 高效刷题路径

    LeetCode 高效刷题路径 Hot 100 https://leetcode.com/problemset/hot-100/ https://leetcode-cn.com/problemset/ ...

  5. leetcode 算法刷题(一)

    今天开始刷Leetcode上面的算法题.我会更新我刷题过程中提交的代码(成功和不成功的都有)和比较好的解法 第二题 Add Two Numbers 题目的意思:输入两个链表,这两个链表都是倒序的数字, ...

  6. leetcode每日刷题计划-简单篇day5

    刷题成习惯以后感觉挺好的 Num 27 移除元素 Remove Element 跟那个排序去掉相同的一样,len标记然后新的不重复的直接放到len class Solution { public: i ...

  7. leetcode每日刷题计划-简单篇day3

    收到swe提前批面试hhh算是ep挂了的后续 努力刷题呀争取今年冲进去! Num 21 合并两个有序链表 Merge Two Sorted Lists 注意新开的链表用来输出结果的是ListNode ...

  8. leetcode每日刷题计划-简单篇day1

    orzorz开始刷题 争取坚持每周平均下来简单题一天能做两道题吧 非常简单的题奇奇怪怪的错误orz越不做越菜 Num 7 整数反转 Reverse Integer 刚开始多给了一个变量来回折腾占地方, ...

  9. Leetcode OJ 刷题

    Valid Palindrome吐槽一下Leetcode上各种不定义标准的输入输出(只是面试时起码能够问一下输入输出格式...),此篇文章不是详细的题解,是自己刷LeetCode的一个笔记吧,尽管没有 ...

随机推荐

  1. 使用VS Code从零开始开发调试.NET Core 1.0

    使用VS Code 从零开始开发调试.NET Core 1.0. .NET Core 是一个开源的.跨平台的 .NET 实现. VS Code 全称是 Visual Studio Code,Visua ...

  2. 基于redis的处理session的方法

    一个基于redis的处理session的方法,如下. <?php class Session_custom { private $redis; // redis实例 private $prefi ...

  3. Python 操作 MS Excel 文件

    利用 Python 对 Excel 文件进行操作需要使用第三方库: openpyxl,可执行 pip install openpyxl 进行安装 1. 导入 openpyxl 模块 导入 openpy ...

  4. java Io流向指定文件输入内容

    package com.hp.io; import java.io.*; public class BufferedWriterTest{ public static void main(String ...

  5. Android的历史与花边

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 历史 现在的Android如日中天.每天150万部的Android设备被激活,全球 ...

  6. Lind.DDD敏捷领域驱动框架~介绍

    回到占占推荐博客索引 最近觉得自己的框架过于复杂,在实现开发使用中有些不爽,自己的朋友们也经常和我说,框架太麻烦了,要引用的类库太多:之前架构之所以这样设计,完全出于对职责分离和代码附复用的考虑,主要 ...

  7. powerdesigner显示列描述信息

    将Comment中的字符COPY至Name中 -------------------------------------------------- Option   Explicit Validati ...

  8. 解决motools和jquery之间的冲突

    在同一个页面需要同时使用motools和jquery,对于$,发生了冲突,以下是解决的办法. <head> <script src="./Scripts/lib/jquer ...

  9. Dynamics CRM 2015-如何修改Optionset Default Value

    在日常工作中,我们时不时会遇到在CRM测试环境上添加Optionset的时候,Default Value是某个值,但换到Production环境或者其他环境,添加的时候,Default Value可能 ...

  10. 转载---javascript 定时器总结

    转载:http://www.jb51.net/article/40193.htm JS里设定延时: 使用SetInterval和设定延时函数setTimeout 很类似.setTimeout 运用在延 ...