Array

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

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

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

tips:注意数组溢出

    public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> disList = new ArrayList<Integer>(); //用数组下标来记录出现过的数
for (int i = 0; i < nums.length; i++) {
int item = Math.abs(nums[i]) - 1;
//判断是否已经出现过
if (nums[item] > 0) {
nums[item] = -nums[item];
}
} //把仍然是正数所对应的下标加入数组列表中
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
disList.add(i + 1);
}
}
return disList;
}

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

    public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> disList = new ArrayList<Integer>();
int n = nums.length;
//用数组下标来记录出现过的数
for (int i = 0; i < nums.length; i++) {
nums[(nums[i] - 1) % n] += n;
} //把a[i]小于n时的i+1加入数组列表中
for (int i = 0; i < nums.length; i++) {
if (nums[i] <= n) {
disList.add(i + 1);
}
}
return disList;
}

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

    public List<Integer> findDisappearedNumbers(int[] nums) {
for (int i = 0; i < nums.length; i++) {
while (nums[i] != i + 1 && nums[i] != nums[nums[i] - 1]) {
int tmp = nums[i];
nums[i] = nums[tmp - 1];
nums[tmp - 1] = tmp;
}
}
List<Integer> res = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i++) {
if (nums[i] != i + 1) {
res.add(i + 1);
}
}
return res;
}

2016-12-14

442.找出数组重复的数

要求与448类似

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

    public List<Integer> findDuplicates(int[] nums) {
List<Integer> dupList = new ArrayList<Integer>();
if (nums == null || nums.length == 0) {
return dupList;
}
int n = nums.length;
//出现一次就把相应下标所指整数+n
for (int i = 0; i < n; i++) {
nums[(nums[i] - 1) % n] += n;
}
for (int i = 0; i < n; i++) {
if (nums[i] > 2*n) {
dupList.add(i + 1);
}
}
return dupList;
}

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

    public List<Integer> findDuplicates(int[] nums) {
List<Integer> dupList = new ArrayList<Integer>();
if (nums == null || nums.length == 0) {
return dupList;
}
for (int i = 0; i < nums.length; i++) {
//获得原下标
int item = Math.abs(nums[i]) - 1;
//只有出现过一次并且item一样才会满足
if (nums[item] < 0) {
dupList.add(Math.abs(item + 1));
}
nums[item] = -nums[item];
}
return dupList;
}

2016-12-14

414.第三大的数

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

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

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

    public int thirdMax(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
//创建三个变量存储前三大的数
long max, mid, min;
max = mid = min = Long.MIN_VALUE;
//判断第三个数是否赋值
int count = 0; for (int x : nums) {
//防止重复输入,continue跳出本次循环
if (x == max || x == mid) {
continue;
}
//x大于max则三个变量都向右移
if (x > max) {
min = mid;
mid = max;
max = x;
count++;
} else if (x > mid) {
min = mid;
mid = x;
count++;
} else if (x >= min) {
min = x;
count++;
}
}
if (count >= 3) {
return (int)min;
}
return (int)max;
}

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

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

2016-12-15

283.移动零

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

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

tips:注意超过数组长度

    public void moveZeroes(int[] nums) {
for (int i = nums.length - 1; i >= 0; i--) {
if (nums[i] == 0) {
int j = i;
while (j < nums.length - 1 && nums[j + 1] != 0) {
nums[j] = nums[j + 1];
nums[j + 1] = 0;
j++;
}
}
}
}

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

    public void moveZeroes(int[] nums) {
int nonzero = 0;
for (int num : nums) {
if (num != 0) {
nums[nonzero] = num;
nonzero++;
}
}
//补全0
while (nonzero < nums.length) {
nums[nonzero] = 0;
nonzero++;
}
}

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

    public void moveZeroes(int[] nums) {
//指向非0数的指针
int j = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
j++;
}
}
}

2016-12-15

268.消失的数

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

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

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

    public int missingNumber(int[] nums) {
int n = nums.length;
for (int i = 0; i < n; i++) {
//先排除n的情况,因为超出了长度
while (nums[i] != i && nums[i] < n) {
int t = nums[i];
nums[i] = nums[t];
nums[t] = t;
}
}
for (int i = 0; i < n; i++) {
if (nums[i] != i) {
return i;
}
}
return n;
}

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

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

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

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

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

2016-12-15

238.排除自己后的乘积

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

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

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

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

    public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] output = new int[n];
output[0] = 1; //从i=1开始,先算nums[i]左边数的乘积
for (int i = 1; i < n; i++) {
output[i] = output[i - 1] * nums[i - 1];
}
//新的累加值,算右边的乘积
int right = 1;
for (int i = n - 1; i >= 0; i--) {
output[i] *= right;
right *= nums[i];
}
return output;
}

2016-12-15

229.多数元素(众数)II

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

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

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

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

    public List<Integer> majorityElement(int[] nums) {
List<Integer> res = new ArrayList<Integer>();
int n = nums.length;
if (nums == null || n == 0) {
return res;
}
if (n == 1) {
res.add(nums[0]);
return res;
} //设2个候选数,2个对应的计票器
int number1, number2, count1, count2;
number1 = number2 = nums[0];
count1 = count2 = 0; //挑选候选数
for (int i = 0; i < n; i++) {
if (nums[i] == number1) {
count1++;
} else if (nums[i] == number2) {
count2++;
} else if (count1 == 0) { //count减为0就换候选数
number1 = nums[i];
count1++;
} else if (count2 == 0) {
number2 = nums[i];
count2++;
} else {
count1--;
count2--;
}
} //清空票数,计算两个候选数的真正票数
count1 = count2 = 0;
for (int i = 0; i < n; i++) {
if (nums[i] == number1) {
count1++;
}
if (nums[i] == number2) {
count2++;
}
} //判断票数是否达到n/3以上
if (count1 > n / 3) {
res.add(number1);
}
//考虑两数相等的情况
if (count2 > n / 3 && number2 != number1) {
res.add(number2);
}
return res;
}

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

2016-12-15

228.总结范围数

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

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

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

    public List<String> summaryRanges(int[] nums) {
List<String> res = new ArrayList<String>();
int n = nums.length;
if (nums == null || n == 0) {
return res;
}
if (n == 1) {
String s = String.valueOf(nums[0]);
res.add(s);
return res;
}
for (int i = 0; i < n - 1; i++) {
if (nums[i + 1] == nums[i] + 1) {
int j = i;
while (i < n - 1 && nums[i + 1] == nums[i] + 1) {
i++;
}
String s = nums[j] + "->" + nums[i];
res.add(s);
} else {
String s = String.valueOf(nums[i]);
res.add(s);
}
}
if (nums[n - 1] != nums[n - 2] + 1) {
String s = String.valueOf(nums[n - 1]);
res.add(s);
}
return res;
}

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])

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

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

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

    public boolean containsNearbyDuplicate(int[] nums, int k) {
if (nums == null || nums.length == 0 || k == 0) {
return false;
}
Map<Integer, Integer> map = new HashMap();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
//get(nums[i])是找之前的
if (i - map.get(nums[i]) <= k) {
return true;
}
}
//调转键与值
map.put(nums[i], i);
}
return false;
}

2016-12-16

217.包含重复

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

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

    public boolean containsDuplicate(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
return true;
}
map.put(nums[i], i);
}
return false;
}

2016-12-16

216.组合和III

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

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

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

    public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> ans = new ArrayList<>();
backTracking(ans, new ArrayList<Integer>(), k, 1, n);
return ans;
} private void backTracking(List<List<Integer>> ans, List<Integer> comb, int k, int start, int n) {
//数组中有k个数并相加为n就返回
if (comb.size() == k && n == 0) {
List<Integer> real = new ArrayList<Integer>(comb);
ans.add(real);
return;
}
for (int i = start; i <= 9; i++) {
//添加新数
comb.add(i);
//递归
backTracking(ans, comb, k, i+1, n-i);
//弹出最后一位
comb.remove(comb.size() - 1);
}
}

2016-12-17

209.最小子数组之和

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

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

    public int minSubArrayLen(int s, int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int i = 0, j = 0, sum = 0, min = Integer.MAX_VALUE;
while (i < nums.length) {
sum += nums[i++];
while (sum >= s) {
//超过s就减去前面一位,保留最短长度
min = Math.min(min, i - j);
sum -= nums[j++];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
}

思路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)

    public int minSubArrayLen(int s, int[] nums) {
int[] sums = new int[nums.length + 1];
//求前i项和
for (int i = 1; i < sums.length; i++) {
sums[i] = sums[i - 1] + nums[i - 1];
}
int minLen = Integer.MAX_VALUE;
for (int i = 0; i < sums.length; i++) {
//二分法找累计和的差值为s的位置end
int end = binarySearch(i + 1, sums.length - 1, sums[i] + s, sums);
if (end == sums.length) break;
if (end - i < minLen) minLen = end - i;
}
return minLen == Integer.MAX_VALUE ? 0 : minLen;
} private int binarySearch(int lo, int hi, int key, int[] sums) {
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (sums[mid] >= key){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo;
}

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解决

    public void rotate(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return;
}
//防止k大于数组长度
k %= nums.length;
//由nums.length - k分割,进行三次首尾反转
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 start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}

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

    public void rotate(int[] nums, int k) {
int n = nums.length;
if (nums == null || n == 0) {
return;
}
k %= n;
int[] res = new int[n];
for (int i = 0; i < k; i++) {
res[i] = nums[n - k + i];
}
for (int i = k; i < n; i++) {
res[i] = nums[i - k];
}
for (int i = 0; i < n; i++) {
nums[i] = res[i];
}
}

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

    public void rotate(int[] nums, int k) {
if (nums.length <= 1) {
return;
}
//step each time to move
int step = k % nums.length;
//find GCD between nums length and step
int gcd = findGcd(nums.length, step);
int position, count; //gcd path to finish movie
for (int i = 0; i < gcd; i++) {
//beginning position of each path
position = i;
//count is the number we need swap each path
count = nums.length / gcd - 1;
for (int j = 0; j < count; j++) {
position = (position + step) % nums.length;
//swap index value in index i and position
nums[i] ^= nums[position];
nums[position] ^= nums[i];
nums[i] ^= nums[position];
}
}
} public int findGcd(int a, int b) {
return (a == 0 || b == 0) ? a + b : findGcd(b, a % b);
}
Here use a example input array [1,2,3,4,5,6,7,8] (n = 8) to explain:

1.suppose k = 3:

GCD = gcd(3,8) = 1, which means there is only one path.

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)

Then we can simulate the process of the algorithm,

path0(each time swap index0 element and indexPosition element):

[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]

2.suppose k = 2:

Similary, GCD = 2, which means there are 2 paths.

count = 3, which means we need 3 swaps to finish each path.

Give the process:

path0(swap index0 and position element):

[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

Then we continue processing path1(swap index1 and position element):

[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)摩尔投票,简单

    public int majorityElement(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
int number = nums[0], count = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == number) {
count++;
} else if (count == 0) {
number = nums[i];
} else {
count--;
}
}
return number;
}

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

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

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

    public int majorityElement(int[] nums) {
int number = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums) {
//第一次放入对应的值为1
if (! map.containsKey(num)) {
map.put(num, 1);
} else {
map.put(num, map.get(num) + 1); //之前的值+ 1
}
if (map.get(num) > nums.length / 2) {
number = num;
}
}
return number;
}

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

2016-12-17

1.两数之和

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

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

warn:循环外一定要有return

    public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(target - nums[i])) {
return new int[]{map.get(target - nums[i]), i};
}
map.put(nums[i], i);
}
return new int[2];
}

2016-12-21

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

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

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

    public int[] twoSum(int[] numbers, int target) {
int i = 0, j = numbers.length - 1;
int[] res = new int[2];
if (numbers == null || numbers.length < 2) {
return res;
}
while (i < j) {
int sum = numbers[i] + numbers[j];
//利用升序特性
if (sum == target) {
res[0] = i + 1;
res[1] = j + 1;
break;
} else if (sum > target) {
j--;
} else {
i++;
}
}
return res;
}

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

2016-12-18

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

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

    public int findPeakElement(int[] nums) {
int low = 0;
int high = nums.length - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (nums[mid] < nums[mid + 1]){
low = mid + 1;
} else {
high = mid;
}
}
return low;
}

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

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

2016-12-18

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

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

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

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

    public int findMin(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums.length == 1) {
return nums[0];
}
int low = 0;
int high = nums.length - 1;
while (low < high) {
int mid = low + (high - low) / 2;
//考虑没有旋转的情况nums[0]最小
if (mid > 0 && nums[mid] < nums[mid - 1]) {
return nums[mid];
}
if (nums[mid] >= nums[low] && nums[mid] > nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return nums[low];
}

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

2016-12-18

152.子数组的最大积

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

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

2016-12-18

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

2016-12-19

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

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

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

    public int maxProfit(int[] prices) {
if (prices == null || prices.length < 2) {
return 0;
}
int maxProfit = 0;
int curMin = prices[0];
for (int i = 1; i < prices.length; i++) {
//确保当前的比前面小才更新
curMin = Math.min(curMin, prices[i]);
maxProfit = Math.max(maxProfit, prices[i] - curMin);
}
return maxProfit;
}

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

2016-12-19

118.杨辉三角形:

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

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

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

    public List<List<Integer>> generate(int numRows) {
List<List<Integer>> ans = new ArrayList<>();
if (numRows < 1) {
return ans;
}
List<Integer> pre = new ArrayList<Integer>();
for (int i = 1; i <= numRows; i++) {
List<Integer> pas = new ArrayList<Integer>();
pas.add(1);
for (int j = 0; j < pre.size() - 1; j++) {
pas.add(pre.get(j) + pre.get(j + 1));
}
if (i > 1) {
pas.add(1);
}
ans.add(pas);
pre.clear();
pre.addAll(pas);
}
return ans;
}

2016-12-19

119.杨辉三角形II:

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

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

    public List<Integer> getRow(int rowIndex) {
List<Integer> pre = new ArrayList<Integer>();
List<Integer> pas = new ArrayList<Integer>();
for (int i = 0; i <= rowIndex; i++) {
pas.clear();
pas.add(1);
for (int j = 0; j < pre.size() - 1; j++) {
pas.add(pre.get(j) + pre.get(j + 1));
}
if (i > 0) {
pas.add(1);
}
pre.clear();
pre.addAll(pas);
}
return pas;
}

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

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

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

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,判断其重复次数,两次以内就继续保留下来

    public int removeDuplicates(int[] nums) {
if (nums.length < 2) {
return nums.length;
}
int length = 1, count = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1]) {
count++;
if (count <= 2) {
nums[length++] = nums[i];
}
} else {
count = 1;
nums[length++] = nums[i];
}
}
return length;
}

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

    public int removeDuplicates(int[] nums) {
int length = 0;
for (int num : nums) {
if (length < 2 || num != nums[length - 2]) {
nums[length++] = num;
}
}
return length;
}

2016-12-19

26.去除有序数组的重复

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

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

    public int removeDuplicates(int[] nums) {
if (nums == null || nums.length == 0 || nums.length == 1) {
return nums.length;
}
int length = 1;
for (int i = 1; i < nums.length; i++) {
if (nums[i] != nums[i - 1]) {
nums[length++] = nums[i];
}
}
return length;
}

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

2016-12-19

75.排序颜色

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

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

    public void sortColors(int[] nums) {
int count0 = 0, count1 = 0;
for (int num : nums) {
if (num == 0) {
count0++;
} else if (num ==1) {
count1++;
}
}
int i = 0;
while (i < count0) nums[i++] = 0;
while (i < count0 + count1) nums[i++] = 1;
while (i < nums.length) nums[i++] = 2;
}

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

    public void sortColors(int[] nums) {
int zero = 0; int two = nums.length - 1;
//遍历到2的位置即可
for (int i = 0; i <= two; i++) {
while (nums[i] == 2 && i < two) {
int temp = nums[i];
nums[i] = nums[two];
nums[two--] = temp;
}
while (nums[i] == 0 && i > zero) {
int temp = nums[i];
nums[i] = nums[zero];
nums[zero++] = temp;
}
}
}

2016-12-19

74.搜索二维矩阵

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

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

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

    public boolean searchMatrix(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0) {
return false;
}
int i = 0, j = matrix[0].length - 1;
while (i < matrix.length && j >= 0) {
if(matrix[i][j] == target) {
return true;
} else if (matrix[i][j] > target) {
j --;
} else {
i++;
}
}
return false;
}

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

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

    public boolean searchMatrix(int[][] matrix, int target) {
if (matrix == null || matrix.length == 0) {
return false;
}
int m = matrix.length, n = matrix[0].length;
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (matrix[mid / n][mid % n] == target) {
return true;
} else if (matrix[mid / n][mid % n] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return false;
}

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

    public void setZeroes(int[][] matrix) {
if (matrix == null || matrix.length == 0) {
return;
}
boolean fr = false, fc = false; //用来分辨第0行与第0列的元素是否本身为0
//找出矩阵中所有的0,并将其对应的第0行及第0列的元素置0
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[0].length; j++) {
if (matrix[i][j] == 0) {
if (i == 0) fr = true;
if (j == 0) fc = true;
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}
for (int i = 1; i < matrix.length; i++) {
for (int j = 1; j < matrix[0].length; j++) {
if (matrix[0][j] == 0 || matrix[i][0] == 0) {
matrix[i][j] = 0;
}
}
}
if (fr) {
for (int j = 0; j < matrix[0].length; j++) {
matrix[0][j] = 0;
}
}
if (fc) {
for (int i = 0; i < matrix.length; i++) {
matrix[i][0] = 0;
}
}
}

2016-12-20

66.加一

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

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

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

    public int[] plusOne(int[] digits) {
int n = digits.length;
digits[n - 1]++;
for (int i = n - 1; i > 0; i--) {
if (digits[i] > 9) {
digits[i - 1]++;
digits[i] = 0;
} else {
break;
}
}
if (digits[0] > 9) {
int[] res = new int[n + 1];
res[0] = 1;
return res;
}
return digits;
}

2016-12-20

54.螺旋矩阵

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

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

warn:约束条件要想清楚

    public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<Integer>();
if (matrix.length == 0) {
return res;
}
int rowBegin = 0;
int rowEnd = matrix.length-1;
int colBegin = 0;
int colEnd = matrix[0].length - 1; while (rowBegin <= rowEnd && colBegin <= colEnd) {
// Traverse Right
for (int j = colBegin; j <= colEnd; j ++) {
res.add(matrix[rowBegin][j]);
}
rowBegin++;
// Traverse Down
for (int j = rowBegin; j <= rowEnd; j ++) {
res.add(matrix[j][colEnd]);
}
colEnd--;
if (rowBegin <= rowEnd) {
// Traverse Left
for (int j = colEnd; j >= colBegin; j --) {
res.add(matrix[rowEnd][j]);
}
}
rowEnd--;
if (colBegin <= colEnd) {
// Traverse Up
for (int j = rowEnd; j >= rowBegin; j --) {
res.add(matrix[j][colBegin]);
}
}
colBegin++;
}
return res;
}

2016-12-20

59.螺旋矩阵II

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

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

    public int[][] generateMatrix(int n) {
int[][] matrix = new int[n][n];
if (n == 0) {
return matrix;
}
int startRow = 0, startCol = 0;
int endRow = n - 1, endCol = n - 1;
int count = 1;
while (startRow <= endRow && startCol <= endCol) {
for (int j = startRow; j <= endRow; j++) {
matrix[startCol][j] = count++;
}
startCol++;
for (int i = startCol; i <= endCol; i++) {
matrix[i][endRow] = count++;
}
endRow--;
if (startCol <= endCol) {
for (int j = endRow; j >= startRow; j--) {
matrix[endCol][j] = count++;
}
}
endCol--;
if (startRow <= endRow) {
for (int i = endCol; i >= startCol; i--) {
matrix[i][startRow] = count++;
}
}
startRow++;
}
return matrix;
}

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

2016-12-21

48.翻转图片

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

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

    public void rotate(int[][] matrix) {
int n = matrix.length;
if (n < 2) {
return;
}
for (int i = 0; i <= n - 2; i++) {
for (int j = i + 1; j <= n - 1; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
for (int col = 0; col < n / 2; col++) {
for (int row = 0; row < n; row++) {
int temp = matrix[row][col];
matrix[row][col] = matrix[row][n - 1 - col];
matrix[row][n - 1 - col] = temp;
}
}
}

2016-12-21

35.寻找插入位置

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

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

tips:约束条件为low <= high

    public int searchInsert(int[] nums, int target) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return low;
}

2016-12-21

34.寻找数的区间

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

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

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

    public int[] searchRange(int[] nums, int target) {
int[] res = {-1, -1};
int low = 0, high = nums.length - 1, mid = 0;
boolean ex = false;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] == target) {
ex = true;
break;
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
if (ex) {
int i = mid;
while (i > 0 && nums[i - 1] == nums[i]) {
i--;
}
res[0] = i;
int j = mid;
while (j < nums.length - 1 && nums[j + 1] == nums[j]) {
j++;
}
res[1] = j;
}
return res;
}

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

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

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

    public int[] searchRange(int[] nums, int target) {
int start = firstEqual(nums, target);
if (start == nums.length || nums[start] != target) {
return new int[]{-1, -1};
}
return new int[]{start, firstEqual(nums, target + 1) - 1}; //利用有序,寻找更大的数位置
}
private int firstEqual(int[] nums, int target) {
int low = 0, high = nums.length;
while (low < high) {
int mid = (low + high) / 2;
if (nums[mid] < target) {
low = mid + 1;
} else { //找第一次出现,相等或大于target都要令high = mid
high = mid;
}
}
return low;
}

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

2016-12-21

27.去除元素

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

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

    public int removeElement(int[] nums, int val) {
int n = nums.length;
int i = 0, j = n - 1;
while (i <= j) {
if (nums[i] == val) {
n--;
while (j > i && nums[j] == val) {
j--;
n--;
}
nums[i++] = nums[j];
nums[j--] = val;
} else {
i++;
}
}
return n;
}

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

    public int removeElement(int[] nums, int val) {
int n = 0;
for (int num : nums) {
if (num != val) {
nums[n++] = num;
}
}
return n;
}

2016-12-21

11.装最多的水

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

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

    public int maxArea(int[] height) {
int i = 0, j = height.length - 1;
int v = 0;
while (i < j) {
v = Math.max(v, (j - i) * Math.min(height[i], height[j]));
if (height[i] < height[j]) {
i++;
} else {
j--;
}
}
return v;
}

2016-12-21

15.三数之和

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

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

    public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i <= nums.length - 3; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int j = i + 1; j < nums.length; j++) {
if (map.containsKey(-nums[i] - nums[j])) {
List<Integer> three = new ArrayList<Integer>();
three.add(nums[i]);
three.add(-nums[i] - nums[j]);
three.add(nums[j]);
res.add(three);
}
map.put(nums[j], j);
}
}
}
return res;
}

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

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

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

    public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i <= nums.length - 3; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
int low = i + 1, high = nums.length - 1;
while (low < high) {
if (nums[low] + nums[high] == - nums[i]) {
res.add(Arrays.asList(nums[i], nums[low], nums[high]));
while (low < high && nums[low + 1] == nums[low]) low++;
while (low < high && nums[high - 1] == nums[high]) high--;
low++;
high--;
} else if (nums[low] + nums[high] < - nums[i]) {
low++;
} else {
high--;
}
}
}
}
return res;
}

2016-12-22

16.最接近的三数和

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

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

    public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int res = nums[0] + nums[1] + nums[nums.length - 1];
for (int i = 0; i < nums.length - 2; i++) {
if (i == 0 || nums[i] != nums[i - 1]) {
int low = i + 1, high = nums.length - 1;
while (low < high) {
int sum = nums[i] + nums[low] + nums[high];
if (Math.abs(target - sum) < Math.abs(target - res)) {
res = sum;
if (res == target) {
return res;
}
}
if (sum > target) {
high--;
} else {
low++;
}
}
}
}
return res;
}

2016-12-22

18.四数之和

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

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

    public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 3; i++) {
if (i == 0 || nums[i - 1] != nums[i]) {
for (int j = i + 1; j < nums.length - 2; j++) {
if (j == i + 1 || nums[j - 1] != nums[j]) {
int low = j + 1, high = nums.length - 1;
while (low < high) {
int sum = nums[i] + nums[j] + nums[low] + nums[high];
if (sum == target) {
res.add(Arrays.asList(nums[i], nums[j], nums[low], nums[high]));
while (low < high && nums[low] == nums[low + 1]) low++;
while (low < high && nums[high] == nums[high - 1]) high--;
low++;
high--;
} else if (sum < target) {
low++;
} else {
high--;
}
}
}
}
}
}
return res;
}

2016-12-22

581. Shortest Unsorted Continuous Subarray

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

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

public class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length, beg = -1, end = -2, max = nums[0], min = nums[n - 1];
for (int i = 1; i < n; i++) {
max = Math.max(max, nums[i]);
min = Math.min(min, nums[n - i - 1]);
if (nums[i] < max) {
end = i;
}
if (nums[n - i - 1] > min) {
beg = n - i - 1;
}
}
return end - beg + 1;
}
}

2017-06-15

Hash Table

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

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

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

(sliding window)

    public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<Character>();
int length = 0, count = 0;
for (int i = 0; i < s.length(); i++) {
if (set.add(s.charAt(i))) {
count++;
length = Math.max(count, length);
} else {
while (s.charAt(i - count) != s.charAt(i)) {
set.remove(s.charAt(i - count));
count--;
}
}
}
return length;
}

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

    public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<Character>();
int i = 0, j = 0, length = 0;
//two pointers, while j for the end of the substring, i for the start
while (j < s.length()) {
if (set.add(s.charAt(j))) {
j++;
length = Math.max(length, j - i);
} else {
set.remove(s.charAt(i++));
}
}
return length;
}

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

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

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)

    public boolean isValidSudoku(char[][] board) {
for (int i = 0; i < board.length; i++) {
Set<Character> rows = new HashSet<Character>();
Set<Character> columns = new HashSet<Character>();
Set<Character> cube = new HashSet<Character>();
for (int j = 0; j < board[0].length; j++) {
//判断第i行
if (board[i][j] != '.' && ! rows.add(board[i][j])) {
return false;
}
//判断第i列
if (board[j][i] != '.' && ! columns.add(board[j][i])) {
return false;
}
//计算第i个大格,算对应关系
int row = 3 * (i / 3);
int col = 3 * (i % 3);
if (board[row + j / 3][col + j % 3] != '.' && ! cube.add(board[row + j / 3][col + j % 3])) {
return false;
}
}
}
return true;
}

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

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

2016-12-22

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

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

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

    public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> map = new HashMap<>();
for (String s : strs) {
//让旋转后的字符串按照顺序排列
char[] chars = s.toCharArray();
Arrays.sort(chars);
String keyStr = String.valueOf(chars);
if (! map.containsKey(keyStr)) {
map.put(keyStr, new ArrayList<String>(){{add(s);}});
} else {
map.get(keyStr).add(s);
}
}
return new ArrayList<List<String>>(map.values());
}

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

2016-12-23

136.单身数(Single Number)

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

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

    public int singleNumber(int[] nums) {
int res = 0;
for (int num : nums) {
res ^= num;
}
return res;
}

另一思路:若使用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)

    public String fractionToDecimal(int numerator, int denominator) {
if (numerator == 0) {
return "0";
}
StringBuilder res = new StringBuilder();
//判断正负
res.append(((numerator > 0) ^ (denominator > 0)) ? "-" : "");
long num = Math.abs((long)numerator);
long den = Math.abs((long)denominator);
//整数部分
res.append(num / den);
num %= den;
if (num == 0) {
return res.toString();
}
//小数部分
res.append(".");
Map<Long, Integer> map = new HashMap<Long, Integer>();
while (num != 0) {
//存放余数
map.put(num, res.length());
num *= 10;
res.append(num / den);
num %= den;
if (map.containsKey(num)) {
res.insert(map.get(num), "(");
res.append(")");
break;
}
}
return res.toString();
}

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,以防止添加重复的子串

    public List<String> findRepeatedDnaSequences(String s) {
Map<Integer, Integer> map = new HashMap<>();
List<String> rv = new ArrayList<>();
//把ACGT转换成两位二进制数
char[] trans = new char[26];
//trans['A' - 'A'] = 0;
trans['C' - 'A'] = 1;
trans['G' - 'A'] = 2;
trans['T' - 'A'] = 3; for(int i = 0; i < s.length() - 9; i++) {
int v = 0;
for(int j = i; j < i + 10; j++) {
//移位存储每一个字母
v |= trans[s.charAt(j) - 'A'];
v <<= 2;
}
if (map.containsKey(v)) {
//判断是否第一次重复
if (map.get(v) == 0) {
rv.add(s.substring(i, i + 10));
}
map.put(v, 1);
} else {
map.put(v, 0);
}
}
return rv;
}

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

    public List<String> findRepeatedDnaSequences(String s) {
Map<String, Integer> map = new HashMap<>();
List<String> rv = new ArrayList<>();
for (int i = 0; i < s.length() - 9; i++) {
String ss = s.substring(i, i + 10);
if (map.containsKey(ss)) {
//判断是否第一次重复
if (map.get(ss) == 0) {
rv.add(ss);
}
map.put(ss, 1);
} else {
map.put(ss, 0);
}
}
return rv;
}

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

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

2016-12-26

202.快乐数字(Happy Number)

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

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

    public boolean isHappy(int n) {
Set<Integer> set = new HashSet<>();
while (n != 1) {
if (! set.add(n)) {
return false;
}
int sum = 0;
while (n != 0) {
int temp = n % 10;
sum += temp * temp;
n /= 10;
}
n = sum;
}
return true;
}

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

2016-12-26

204.数质数(Count Primes)

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

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

    public int countPrimes(int n) {
boolean[] isPrime = new boolean[n];
for (int i = 2; i < n; i++) {
isPrime[i] = true;
}
// Loop's ending condition is i * i < n instead of i < sqrt(n)
// to avoid repeatedly calling an expensive function sqrt().
for (int i = 2; i * i < n; i++) {
if (!isPrime[i]) continue;
for (int j = i * i; j < n; j += i) {
isPrime[j] = false;
}
}
int count = 0;
for (int i = 2; i < n; i++) {
if (isPrime[i]) count++;
}
return count;
}

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

    public int countPrimes(int n) {
boolean[] notPrime = new boolean[n];
int count = 0;
for (int i = 2; i < n; i++) {
if (notPrime[i] == false) {
count++;
for (int j = 2; i*j < n; j++) {
notPrime[i*j] = true;
}
}
} return count;
}

2016-12-26

205.同构字符串(Isomorphic Strings)

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

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

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

    public boolean isIsomorphic(String s, String t) {
int[] ss = new int[256];
int[] ts = new int[256];
for (int i = 0; i < s.length(); i++) {
if (ss[s.charAt(i)] != ts[t.charAt(i)]) {
return false;
}
ss[s.charAt(i)] = i + 1;
ts[t.charAt(i)] = i + 1;
}
return true;
}

(10ms)

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

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

    public boolean isIsomorphic(String s, String t) {
Map<Character, Character> map1 = new HashMap<>();
Map<Character, Character> map2 = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char a = s.charAt(i);
char b = t.charAt(i);
if (map1.containsKey(a) && map1.get(a) != b) {
return false;
} else if (map2.containsKey(b) && map2.get(b) != a){
return false;
} else {
map1.put(a, b);
map2.put(b, a);
}
}
return true;
}

(49ms)

    public boolean isIsomorphic(String s, String t) {
Map<Character, Character> map = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char a = s.charAt(i);
char b = t.charAt(i);
if (map.containsKey(a)) {
if (map.get(a) != b) {
return false;
}
} else if (map.containsValue(b)){ //在不含key的情况下有一样的value,证明与之前的配对不一样
return false;
} else {
map.put(a, b);
}
}
return true;
}

(19ms)

2016-12-27

242.有效旋转(Valid Anagram)

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

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

    public boolean isAnagram(String s, String t) {
char[] sc = s.toCharArray();
char[] tc = t.toCharArray();
Arrays.sort(sc);
Arrays.sort(tc);
s = String.valueOf(sc);
t = String.valueOf(tc);
Set<String> set = new HashSet<>();
set.add(s);
if (set.add(t)) return false;
return true;
}

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

    public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] alps = new int[26];
for (int i = 0; i < s.length(); i++) {
//得到某字母对应的位置+ 1
alps[s.charAt(i) - 'a']++;
alps[t.charAt(i) - 'a']--;
}
for (int alp : alps) {
if (alp != 0) {
return false;
}
}
return true;
}

2016-12-26

290.单词模式(Word Pattern)

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

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

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

    public boolean wordPattern(String pattern, String str) {
Map<Character, String> map = new HashMap<>();
String[] words = str.split(" ");
if (words.length != pattern.length()) {
return false;
}
for (int i = 0; i < words.length; i++) {
char c = pattern.charAt(i);
String s = words[i];
if (map.containsKey(c)) {
if (!map.get(c).equals(s)) {
return false;
}
} else if (map.containsValue(s)) {
return false;
} else {
map.put(c, s);
}
}
return true;
}

(2ms)

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

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

    public boolean wordPattern(String pattern, String str) {
Map map = new HashMap();
String[] words = str.split(" ");
if (words.length != pattern.length()) {
return false;
}
for (int i = 0; i < words.length; i++) {
if (!Objects.equals(map.put(pattern.charAt(i), i), map.put(words[i], i))) { //此处用!=的话会超过长度
return false;
}
}
return true;
}

(3ms)

2016-12-27

299.Bulls and Cows

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

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

    public String getHint(String secret, String guess) {
int bull = 0;
int cow = 0;
int[] nums = new int[10];
for (int i = 0; i < secret.length(); i++) {
if (secret.charAt(i) == guess.charAt(i)) {
bull++;
} else {
//数字字符转成整数
if (nums[secret.charAt(i) - '0']++ < 0) cow++;
if (nums[guess.charAt(i) - '0']-- > 0) cow++;
}
}
return bull + "A" + cow + "B";
}

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

2016-12-27

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

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

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

    public int[] intersection(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int i = 0, j = 0;
List<Integer> list = new ArrayList<Integer>();
while (i < nums1.length && j < nums2.length) {
if (nums1[i] == nums2[j]) {
list.add(nums1[i]);
while (i < nums1.length - 1 && nums1[i] == nums1[i + 1]) i++;
while (j < nums2.length - 1 && nums2[j] == nums2[j + 1]) j++;
i++;
j++;
} else if (nums1[i] < nums2[j]) {
i++;
} else {
j++;
}
}
int[] res = new int[list.size()];
for (int k = 0; k < list.size(); k++) {
res[k] = list.get(k);
}
return res;
}

(ArrayList)

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

    public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
for (int num : nums1) {
set1.add(num);
}
for (int num : nums2) {
if (set1.contains(num)) {
set2.add(num);
}
}
int[] res = new int[set2.size()];
int i = 0;
for (int num : set2) {
res[i++] = num;
}
return res;
}

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

2016-12-27

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

要求:重复也要列出

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

    public int[] intersect(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int i = 0, j = 0;
List<Integer> list = new ArrayList<Integer>();
while (i < nums1.length && j < nums2.length) {
if (nums1[i] == nums2[j]) {
list.add(nums1[i]);
i++;
j++;
} else if (nums1[i] < nums2[j]) {
i++;
} else {
j++;
}
}
int[] res = new int[list.size()];
for (int k = 0; k < list.size(); k++) {
res[k] = list.get(k);
}
return res;
}

2016-12-27

389.找不同(Find the Difference)

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

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

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

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

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

2016-12-28

409.最长回文(Longest Palindrome)

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

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

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

(21ms)

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

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

    public int longestPalindrome(String s) {
int[] lower = new int[26];
int[] upper = new int[26];
int res = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c >= 97) {
lower[c - 'a']++;
} else {
upper[c - 'A']++;
}
}
for (int i = 0; i < 26; i++) {
//保证输出的都是双数
res += (lower[i] / 2) * 2;
res += (upper[i] / 2) * 2;
}
//只有等于原s长度时回文长度才是偶数
return res == s.length() ? res : res + 1;
}

(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

    public List<Integer> findAnagrams(String s, String p) {
List<Integer> list = new ArrayList<>();
if (s == null || s.length() == 0 || p == null || p.length() == 0) return list;
int[] hash = new int[256]; //character hash
//record each character in p to hash
for (char c : p.toCharArray()) {
hash[c]++;
}
//two points, initialize count to p's length
int left = 0, right = 0, count = p.length();
while (right < s.length()) {
//move right everytime, if the character exists in p's hash, decrease the count
//current hash value >= 1 means the character is existing in p
if (hash[s.charAt(right++)]-- >= 1) count--; //when the count is down to 0, means we found the right anagram
//then add window's left to result list
if (count == 0) list.add(left); //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
//++ to reset the hash because we kicked out the left
//only increase the count if the character is in p
//the count >= 0 indicate it was original in the hash, cuz it won't go below 0
if (right - left == p.length() && hash[s.charAt(left++)]++ >= 0) count++;
}
return list;
}

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

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的子字符串

    private int lo, maxLen; //定义类的private属性,使类中所有方法都可用
public String longestPalindrome(String s) {
if (s.length() < 2) {
return s;
} //以每个位置为中心向两边找回文
for (int i = 0; i < s.length() - 1; i++) {
extendPalindrome(s, i, i); //寻找以i为中心的回文
extendPalindrome(s, i, i + 1); //若i与i + 1相同,则中心为它们两个
}
return s.substring(lo, lo + maxLen);
} //two pointers以中心向头尾移动
private void extendPalindrome(String s, int j, int k) {
while (j >= 0 && k < s.length() && s.charAt(j) == s.charAt(k)) {
j--;
k++;
}
if (maxLen < k - j - 1) {
lo = j + 1;
maxLen = k - j - 1;
}
}

2017-01-02

6.Z型变换(ZigZag Conversion)

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

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

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

    public String convert(String s, int numRows) {
StringBuilder[] rows = new StringBuilder[numRows]; //数组
for (int row = 0; row < numRows; row++) {
rows[row] = new StringBuilder(); //数组每一个元素都是一个builder
}
int i = 0;
//Z型存储进数组
while (i < s.length()) {
for (int row = 0; row < numRows && i < s.length(); row++) {
rows[row].append(s.charAt(i++));
}
for (int row = numRows - 2; row > 0 && i < s.length(); row--) {
rows[row].append(s.charAt(i++));
}
}
//读取
for (int row = 1; row < numRows; row++) {
rows[0].append(rows[row]);
}
return rows[0].toString();
}

(StringBuilder)

    public String convert(String s, int numRows) {
ArrayList<Character>[] rows = (ArrayList<Character>[]) new ArrayList[numRows];
for (int row = 0; row < numRows; row++) {
rows[row] = new ArrayList<Character>();
}
int i = 0;
//Z型存储进数组
while (i < s.length()) {
for (int row = 0; row < numRows && i < s.length(); row++) {
rows[row].add(s.charAt(i++));
}
for (int row = numRows - 2; row > 0 && i < s.length(); row--) {
rows[row].add(s.charAt(i++));
}
}
//读取
StringBuilder sb = new StringBuilder(s.length());
for (int row = 0; row < numRows; row++) {
for (int col = 0; col < rows[row].size(); col++) {
sb.append(rows[row].get(col));
}
}
return sb.toString();
}

(ArrayList)

2017-01-02

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

要求:考虑各种情况

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

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

    public int myAtoi(String str) {
int index = 0, sign = 1, total = 0;
//1. Empty string
if (str.length() == 0) {
return 0;
} //2. Remove Spaces
while (str.charAt(index) == ' ' && index < str.length()) {
index++;
} //3. Handle signs
if (str.charAt(index) == '+' || str.charAt(index) == '-') {
sign = str.charAt(index) == '+' ? 1 : -1;
index++;
} //4. Convert number and avoid overflow
while (index < str.length()) {
int digit = str.charAt(index) - '0'; //char to int
if(digit < 0 || digit > 9) break; //if not a number, break //check if total will be overflow after 10 times and add digit
if (Integer.MAX_VALUE / 10 < total || Integer.MAX_VALUE / 10 == total && Integer.MAX_VALUE % 10 < digit) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
}
total = 10 * total + digit;
index++;
}
return total * sign;
}

2017-01-02

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

要求:如题

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

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

2017-01-02

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

要求:如题

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

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

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

    public int romanToInt(String s) {
Map<Character, Integer> map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000); char[] c = s.toCharArray();
int sum = map.get(c[c.length - 1]);
for (int i = 0; i < s.length() - 1; i++) {
if (map.get(c[i]) < map.get(c[i + 1])) {
sum -= map.get(c[i]);
} else {
sum += map.get(c[i]);
}
}
return sum;
}

2017-01-02

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

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

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

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

    public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) {
return "";
}
int maxLen = strs[0].length();
for (int i = 1; i < strs.length; i++) {
maxLen = Math.min(maxLen, strs[i].length());
int j = 0;
while (j < maxLen && strs[0].charAt(j) == strs[i].charAt(j)) {
j++;
}
maxLen = j;
}
return strs[0].substring(0, maxLen);
}

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

    public String longestCommonPrefix(String[] strs) {
if (strs.length == 0) {
return "";
}
//排序确保了中间项出现不一样的问题
Arrays.sort(strs);
char[] a = strs[0].toCharArray();
char[] b = strs[strs.length - 1].toCharArray();
int i = 0;
while (i < a.length && i < b.length && a[i] == b[i]) {
i++;
}
return strs[0].substring(0, i);
}

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

2017-01-04

20.有效括号(Vaild Parentheses)

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

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

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

    public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
//如果左括号则入栈,有上一个入栈对应的右括号则出栈,否则错误
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
stack.push(s.charAt(i));
} else if (s.charAt(i) == ')' && !stack.empty() && stack.peek() == '(') {
stack.pop();
} else if (s.charAt(i) == ']' && !stack.empty() && stack.peek() == '[') {
stack.pop();
} else if (s.charAt(i) == '}' && !stack.empty() && stack.peek() == '{') {
stack.pop();
} else {
return false;
}
}
//判断是否为空,确保都有右括号对应
return stack.empty();
}

(9ms)

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

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

    public boolean isValid(String s) {
Stack<Character> stack = new Stack<Character>();
//入栈的是与当前左括号对应的右括号
for (char c : s.toCharArray()) {
if (c == '(') {
stack.push(')');
} else if (c == '[') {
stack.push(']');
} else if (c == '{') {
stack.push('}');
} else if (stack.empty() || stack.pop() != c) {
return false;
}
}
return stack.empty();
}

(8ms)

2017-01-04

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

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

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

    public int strStr(String haystack, String needle) {
int m = haystack.length();
int n = needle.length();
for (int i = 0; i <= m - n; i++) {
for (int j = 0; ; j++) {
if (j == n) return i;
if (needle.charAt(j) != haystack.charAt(i + j)) break;
}
}
return -1;
}

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

2017-01-04

38.Count and Say(数和读)

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

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

    public String countAndSay(int n) {
StringBuilder cur = new StringBuilder("1");
StringBuilder pre;
//标记要说的字符及其数量
int count;
char say;
//从头开始数
for (int i = 1; i < n; i++) {
pre = cur;
//存放say之后的字符串
cur = new StringBuilder();
count = 1;
say = pre.charAt(0);
for (int j = 1; j < pre.length(); j++) {
if (pre.charAt(j) == say) {
count++;
} else {
cur.append(count).append(say);
count = 1;
say = pre.charAt(j);
}
}
cur.append(count).append(say);
}
return cur.toString();
}

2017-01-05

43.字符串相乘(Multiply Strings)

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

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

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

    public String multiply(String num1, String num2) {
//用数组来存储相乘结果
int[] pos = new int[num1.length() + num2.length()];
//从末尾开始相乘
for (int i = num1.length() - 1; i >= 0; i--) {
for (int j = num2.length() - 1; j >= 0; j--) {
//字符转数字后相乘
int mul = (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
//当前乘积的十位个位应当放的位置
int p1 = i + j, p2 = i + j + 1;
//算出与上一次循环中十位上的数的和
int sum = mul + pos[p2];
//算出当前乘积的最后结果的十位个位
pos[p1] += sum / 10;
pos[p2] = sum % 10;
}
} //把数组转字符串
StringBuilder res = new StringBuilder();
for (int p : pos) {
//不输入头部的0
if (!(res.length() == 0 && p == 0)) {
res.append(p);
}
}
//判断是否为0
return res.length() == 0 ? "0" : res.toString();
}

2017-01-05

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

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

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

    public int lengthOfLastWord(String s) {
int len = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) != ' ') {
len++;
}
if (s.charAt(i) == ' ' && len != 0) {
break;
}
}
return len;
}

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

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

2017-01-05

67.二进制相加(Add Binary)

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

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

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

    public String addBinary(String a, String b) {
StringBuilder res = new StringBuilder();
int i = a.length() - 1, j = b.length() - 1, ca = 0;
while (i >= 0 || j >= 0) {
int sum = ca;
if (i >= 0) sum += a.charAt(i--) - '0';
if (j >= 0) sum += b.charAt(j--) - '0';
//余数是该位结果,商是进位
res.append(sum % 2);
ca = sum / 2;
}
//最后一个进位
if (ca == 1) res.append(1);
//转置
return res.reverse().toString();
}

2017-01-05

125.有效回文(Valid Palindrome)

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

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

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

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

    public boolean isPalindrome(String s) {
int i = 0, j = s.length() - 1;
while (i < j) {
//判断是否数字或字母
char a = s.charAt(i);
char b = s.charAt(j);
if (!Character.isLetterOrDigit(a)) {
i++;
} else if (!Character.isLetterOrDigit(b)) {
j--;
} else {
if (Character.toLowerCase(a) != Character.toLowerCase(b)) {
return false;
}
i++;
j--;
}
}
return true;
}

2017-01-06

344.旋转字符串(Reverse String)

要求:如题

思路:(自写AC)简单

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

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

2017-01-06

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

要求:如题

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

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

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

    public String reverseVowels(String s) {
//string直接用作set
String vowels = "aeiouAEIOU";
char[] res = s.toCharArray();
int i = 0, j = s.length() - 1;
while (i < j) {
while (i < j && !vowels.contains(res[i] + "")) i++;
while (i < j && !vowels.contains(res[j] + "")) j--;
//i = j时与自己交换
char c = res[i];
res[i] = res[j];
res[j] = c;
i++;
j--;
}
return new String(res);
}

2017-01-06

383.Ransom Note

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

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

    public boolean canConstruct(String ransomNote, String magazine) {
int[] chars = new int[26];
for (int i = 0; i < ransomNote.length(); i++) {
chars[ransomNote.charAt(i) - 'a']++;
}
for (int i = 0; i < magazine.length(); i++) {
chars[magazine.charAt(i) - 'a']--;
}
for (int c : chars) {
if (c > 0) {
return false;
}
}
return true;
}

discuss写得更simple

    public boolean canConstruct(String ransomNote, String magazine) {
int[] arr = new int[26];
for (int i = 0; i < magazine.length(); i++) {
arr[magazine.charAt(i) - 'a']++;
}
for (int i = 0; i < ransomNote.length(); i++) {
if(--arr[ransomNote.charAt(i)-'a'] < 0) {
return false;
}
}
return true;
}

2017-01-09

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

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

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

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

    public int countSegments(String s) {
if (s.length() == 0) {
return 0;
}
//保证开始不是空格
int res = s.charAt(0) == ' ' ? 0 : 1;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
//排除中间有多个空格
while (i < s.length() - 1 && s.charAt(i + 1) == ' ') i++;
//排除最后一个是空格
if (i != s.length() - 1) res++;
}
}
return res;
}

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

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

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

2017-01-09

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

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

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

    public boolean repeatedSubstringPattern(String str) {
int[] chars = new int[26];
for (int i = 0; i < str.length(); i++) {
chars[str.charAt(i) - 'a']++;
}
int temp = 0;
for (int c : chars) {
if (temp == 0 && c != 0) {
temp = c;
} else if (c != temp) {
return false;
}
}
return true;
}

???

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

    public boolean repeatedSubstringPattern(String str) {
int len = str.length();
for (int i = len / 2; i > 0; i--) {
//若被整除则有可能组成str
if (len % i == 0) {
int m = len / i;
String subStr = str.substring(0, i);
StringBuilder sb = new StringBuilder();
//复制m次判断是否str
for (int j = 0; j < m; j++) {
sb.append(subStr);
}
if (sb.toString().equals(str)) {
return true;
}
}
}
return false;
}

2017-01-09

Math

7.反转数字(Reverse Integer)

要求:如题

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

    public int reverse(int x) {
long rev = 0;
while (x != 0) {
//把之前保存的左移
rev = 10 * rev + x % 10;
x = x /10;
//判断溢出
if (rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE) {
return 0;
}
}
return (int)rev;
}

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

2017-01-09

9.回文数字(Palindrome Number)

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

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

    public boolean isPalindrome(int x) {
if (x < 0) {
return false;
}
int rev = 0;
int temp = x;
while (temp != 0) {
rev = rev * 10 + temp % 10;
temp /= 10;
}
return x == rev;
}

(有缺陷)

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

warn:考虑奇偶长度

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

2017-01-09

29.整数相除(Divide Two Integers)

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

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

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

    public int divide(int dividend, int divisor) {
int sign = 1;
if ((dividend > 0 && divisor < 0) || (dividend < 0 && divisor > 0)) {
sign = -1;
}
if (divisor == 0) {
return Integer.MAX_VALUE;
}
long ldividend = Math.abs((long)dividend);
long ldivisor = Math.abs((long)divisor);
if (ldividend < ldivisor) {
return 0;
}
long res = ldivide(ldividend, ldivisor);
int ans;
if (res > Integer.MAX_VALUE) {
return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
} else {
ans = (int)((sign == 1) ? res : -res);
}
return ans;
}
private long ldivide(long ldividend, long ldivisor) {
//增加这句能跳出递归
if (ldividend < ldivisor) {
return 0;
}
long sum = ldivisor;
long multiple = 1;
while ((sum + sum) <= ldividend) {
sum += sum;
multiple += multiple;
}
return multiple + ldivide(ldividend - sum, ldivisor);
}

2017-01-11

50.Pow(x, n)???

    public double myPow(double x, int n) {
double ans = 1;
long absN = Math.abs((long)n);
while(absN > 0) {
if((absN & 1) == 1) ans *= x;
absN >>= 1;
x *= x;
}
return n < 0 ? 1 / ans : ans;
}

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()

    public String convertToTitle(int n) {
StringBuilder res = new StringBuilder();
while (n != 0) {
res.append((char)('A' + --n % 26));
n /= 26;
}
return res.reverse().toString();
}

2017-01-11

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

要求:171相反过来

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

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

2017-01-12

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

要求:如题

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

    public int trailingZeroes(int n) {
int num = 0;
while (n != 0) {
n /= 5;
num += n;
}
return num;
}

2017-01-12

223.Rectangle Area(矩形面积)

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

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

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

2017-01-16

231.2的次方(Power of Two)

要求:判断是否二的次方

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

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

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

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

2017-01-16

258.叠加数字(Add Digits)

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

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

    public int addDigits(int num) {
while (num / 10 != 0) {
int sum = 0;
String numStr = num + "";
char[] numArr = numStr.toCharArray();
for (char c : numArr) {
int temp = c - '0';
sum += temp;
}
num = sum;
}
return num;
}

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

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

更优美的写法:

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

2017-01-16

263.丑数(Ugly Number)

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

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

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

    public boolean isUgly(int num) {
while (num != 0 && (num % 2 == 0 || num % 3 == 0 || num % 5 == 0)) {
if (num % 2 == 0) {
num /= 2;
}
if (num % 3 == 0) {
num /= 3;
}
if (num % 5 == 0) {
num /= 5;
}
}
return num == 1;
}

2017-01-16

264.丑数II(Ugly Number II)

要求:算出第n个丑数

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

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

2017-01-16

319.灯泡开关(Bulb Switcher)

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

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

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

2017-01-16

326.3的次方(Power of Three)

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

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

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

2017-01-16

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

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

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

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

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

2017-01-16

367.平方数(Valid Perfect Square)

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

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

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

    public boolean isPerfectSquare(int num) {
long low = 1, high = num;
while (low <= high) {
long mid = (low + high) / 2;
if (mid * mid == num) {
return true;
} else if (mid * mid < num) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return false;
}

(Olog(n))

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

    public boolean isPerfectSquare(int num) {
int i = 1;
while (num > 0) {
num -= i;
i += 2;
}
return num == 0;
}

(O(sqrt(n)))

2017-01-16

415.字符串相加(Add Strings)

要求:如题

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

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

    public String addStrings(String num1, String num2) {
StringBuilder res = new StringBuilder();
int ca = 0;
for (int i = num1.length() - 1, j = num2.length() - 1;
i >= 0 || j >= 0 || ca == 1; i--, j--) {
int x = 0, y = 0;
if (i >= 0) {
x = num1.charAt(i) - '0';
}
if (j >= 0) {
y = num2.charAt(j) - '0';
}
int sum = x + y + ca;
res.append(sum % 10);
ca = (sum >= 10) ? 1 : 0;
}
return res.reverse().toString();
}

2017-01-16

Stack

456. 132 Pattern

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

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

public class Solution {
class Pair {
int min, max;
public Pair(int min, int max) {
this.min = min;
this.max = max;
}
}
public boolean find132pattern(int[] nums) {
Stack<Pair> stack = new Stack();
for (int n : nums) {
if (stack.isEmpty() || n < stack.peek().min) {
//压入较小的新元素n
stack.push(new Pair(n, n));
}
else if (n > stack.peek().min) {
Pair last = stack.pop();
if (n < last.max) {
return true;
}
else {
last.max = n;
//把不够新元素n大的对都弹出
while (!stack.isEmpty() && n >= stack.peek().max) {
stack.pop();
}
if (!stack.isEmpty() && stack.peek().min < n) {
return true;
}
stack.push(last);
}
}
}
return false;
}
}

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. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  2. struts2学习之旅三 权限管理和导航设计

    1,权限管理的db设计和dao实现,尽量简单快速有效: db的设计如下:权限按照角色来赋给用户: 权限对应每一个具体的功能,有菜单级别的,有导航级别的,还有页面级别的功能: 涉及到权限的敏感操作一般都 ...

  3. request.getParameter()、request.getInputStream()和request.getReader()

    大家经常 用servlet和jsp,但是对 request.getInputStream()和request.getReader()比较陌生.request.getParameter()request ...

  4. 口碑外卖系统架构图(li)

  5. (转)SqlServer 数据库同步的两种方式 (发布、订阅),主从数据库之间的同步

    最近在琢磨主从数据库之间的同步,公司正好也需要,在园子里找了一下,看到这篇博文比较详细,比较简单,本人亲自按步骤来过,现在分享给大家. 在这里要提醒大家的是(为了更好的理解,以下是本人自己理解,如有错 ...

  6. xcode中的.h和.m文件分别是什么意思?各有什么用?

    .h 表示头文件,用来声明各种成员变量,方法,属性之类的.在import的时候用头文件. .m 主要用来实现.h 里声明的方法.举个例子,如果要写一个方法,你要在.h里先声明: - (void)myM ...

  7. SQLServer修改表字段名称

    EXEC sp_rename 'TableName.[ColumnName]','ColumnNew','COLUMN'

  8. win10 安装visual studio 2015遇到的坑

    最近win7系统不知啥原因无法访问域中的网络文件,打算升级到win10体验一下.结果发现这一路有太多的坑.首先安装win10基本上算顺利,但是当进入系统后,菜单模式对于PC的鼠标来说,用起来感觉不顺手 ...

  9. HTTP、HTTP2

      HTTP.HTTP2.0.SPDY.HTTPS 你应该知道的一些事 原文链接:http://www.alloyteam.com/2016/07/httphttp2-0spdyhttps-readi ...

  10. iOS 开发 -----公司测试打包上传流程

    打包iOS应用程序 如果想要将做的iOS应用程序安装到自己的iOS设备上测试.或者安装到别人的iOS设备上,或者想发布到App Store中,先要给应用签名.签名就要有证书,这就需要申请证书的过程了. ...