1、三数之和(L15)
//给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重 //复的三元组。 // // 注意:答案中不可以包含重复的三元组。 // // // // 示例 1: // // //输入:nums = [-1,0,1,2,-1,-4] //输出:[[-1,-1,2],[-1,0,1]] // // // 示例 2: // // //输入:nums = [] //输出:[] // // // 示例 3: // // //输入:nums = [0] //输出:[] // // // // // 提示: // // // 0 <= nums.length <= 3000 // -105 <= nums[i] <= 105 // // Related Topics 数组 双指针
三数之和其实最简单直观的方法就是直接采用for循环来做,但是这样一来时间复杂度高,二来后期需要花比较大的代价进行去重。代码如下:
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
for (int j = i+1; j < nums.length - 1; j++) {
int num = nums[i]+nums[j];
for (int k = j+1; k < nums.length; k++) {
if(num == -1 * nums[k]){
ArrayList<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
Collections.sort(list);
if(!lists.contains(list)){
lists.add(list);
}
}
}
}
}
return lists;
}
其次就是不单独采用集合进行降重,这样就需要我们对数进行排序,这样如果保证我们的三个数是a不大于b,b不大于c。但是这样仍然会存在比较严重的重复问题,比如:
[1,2,3,4,5,5,-6]
这一串数,如果我们取了a、b、c分别为1、5、-6,然后继续向后取值,b仍然会取5,然后就会出现重复,所以我们每一个元素在自己的循环里都需要避免重复。
即使这样做了之后,如下:
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length - 2; i++) {
if(i == 0 || nums[i] != nums[i-1]){
for (int j = i+1; j < nums.length - 1; j++) {
if(j == i + 1 || nums[j] != nums[j-1]){
for (int k = j+1; k < nums.length; k++) {
if(k == j+1 || nums[k] != nums[k-1]){
if(nums[i] + nums[k] + nums[j] == 0){
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
lists.add(list);
}
}
}
}
}
}
}
return lists;
}
依然是超时,因为没有脱离三层循环的框架,但其实我们细琢磨,我们可以直接每一轮循环看成是两数相和,因为每次循环a都是固定的,所以我们就只需要管b和c就行了,而b和c之和要求是固定值,那么增加b,c必定要减小,所以我们可以看成是一个双指针的问题,每次固定a然后取b值,随后不断减小c值,直到b+c<=-a。这个时候我们的c值是可以记录下来的,下次b只会更大,所以c的指针只能从当前位置往前移,这样就避免了重复的循环的发生。关键是一些判别条件,具体看程序
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> lists = new ArrayList<>();
for (int i = 0; i < nums.length-2; i++) {
if(i == 0 || nums[i] != nums[i-1]){
int k = nums.length-1;
for (int j = i+1; j < nums.length-1; j++) {
if(j == i+1 || nums[j] != nums[j - 1]){
while (j < k && nums[j] + nums[k] + nums[i] > 0) {
--k;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出b的循环,可以改变a了
if (j == k) {
break;
}
if (nums[i] + nums[j] + nums[k] == 0) {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
lists.add(list);
}
}
}
}
}
return lists;
}
2、电话号码的字母组合(L17)
//给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 // // 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 // // // // // // 示例 1: // // //输入:digits = "23" //输出:["ad","ae","af","bd","be","bf","cd","ce","cf"] // // // 示例 2: // // //输入:digits = "" //输出:[] // // // 示例 3: // // //输入:digits = "2" //输出:["a","b","c"] // // // // // 提示: // // // 0 <= digits.length <= 4 // digits[i] 是范围 ['2', '9'] 的一个数字。 // // Related Topics 深度优先搜索 递归 字符串 回溯算法
这个就相当于是一个N叉树,然后找到每一根节点在叶子节点的路径,所以用回溯的方法
class Solution {
private String[] letterMap = {" ",
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
private List<String> res;
public List<String> letterCombinations(String digits) {
res = new ArrayList<String>();
if(digits.equals("")){
return res;
}
helper(digits, 0, "");
return res;
}
private void helper(String digits, int index, String s){
//一旦形成的字母字符串的长度等于了数字字符串的长度就直接返回
if(index == digits.length()){
res.add(s);
return;
}
char c = digits.charAt(index);
String letters = letterMap[c - '0'];
for (int i = 0; i < letters.length(); i++) {
helper(digits, index + 1, s+letters.charAt(i));
}
}
}
这个底下还有一个比较小巧牛逼的方法,就是利用队列来进行求解,这种方法十分巧妙,比如我们的数字数组是[2,3,4]
首先在队列中存[a,b,c],然后我们将a给移出去,然后在后面添加ad,ae,af,这个时候数组就变成了[b,c,ad,ae,af],然后就是这个逻辑,最后就能将一整个N叉树遍历完成。
public List<String> letterCombinations(String digits) {
LinkedList<String> ans = new LinkedList<String>();
if(digits.isEmpty()) return ans;
String[] mapping = new String[] {"0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
ans.add("");
for(int i =0; i<digits.length();i++){
int x = Character.getNumericValue(digits.charAt(i));
while(ans.peek().length()==i){
String t = ans.remove();
for(char s : mapping[x].toCharArray())
ans.add(t+s);
}
}
return ans;
}