1、两数之和 II - 输入有序数组
问题:给定一个已按照 非递减顺序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
示例 1:
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
示例 2:
输入:numbers = [2,3,4], target = 6
输出:[1,3]
示例 3:
输入:numbers = [-1,0], target = -1
输出:[1,2]
1 package LeetCode.test2_shuangzhizheng; 2 3 import java.util.Arrays; 4 5 public class ques_167_两数之和II_输入有序数组 { 6 public static void main(String[] args) { 7 int[] numbers = {2, 7, 11, 15}; 8 // int[] numbers = {2, 3, 4}; 9 int target = 9; 10 // int target = 6; 11 System.out.println(Arrays.toString(twoSum(numbers, target))); 12 } 13 14 public static int[] twoSum(int[] numbers, int target) { 15 int left = 0; 16 int right = numbers.length - 1; 17 while (left < right) { 18 if (numbers[left] + numbers[right] > target) { 19 right--; 20 } else if (numbers[left] + numbers[right] < target) { 21 left++; 22 } else { 23 break; 24 } 25 } 26 return new int[]{left + 1, right + 1}; 27 } 28 }
2、合并两个有序数组
问题:给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
1 package LeetCode.test2_shuangzhizheng; 2 3 import java.util.Arrays; 4 5 /** 6 * 两种情况:m>=0,n>=0 n>=0 7 * (第一种运行过程中也可能会变为第二种情况) 8 */ 9 public class ques_88_合并两个有序数组 { 10 public static void main(String[] args) { 11 // int[] nums1 = {1, 2, 3, 0, 0, 0}; 12 // int m = 3; 13 // int[] nums2 = {2, 5, 6}; 14 // int n = 3; 15 16 // int[] nums1 = {1}; 17 // int m = 1; 18 // int[] nums2 = {}; 19 // int n = 0; 20 21 // int[] nums1 = {0}; 22 // int m = 0; 23 // int[] nums2 = {1}; 24 // int n = 1; 25 26 int[] nums1 = {2, 0}; 27 int m = 1; 28 int[] nums2 = {1}; 29 int n = 1; 30 merge(nums1, m, nums2, n); 31 } 32 33 public static void merge(int[] nums1, int m, int[] nums2, int n) { 34 int pos = m-- + n-- - 1; // 当前pos = m + n - 1 35 while (m >= 0 && n >= 0) { // 当前m = m-1, n = n-1 (第一种情况) 36 nums1[pos--] = nums2[n] > nums1[m] ? nums2[n--] : nums1[m--]; 37 } 38 while (n >= 0) { // (第二种情况) 39 nums1[pos--] = nums2[n--]; 40 } 41 System.out.println(Arrays.toString(nums1)); 42 } 43 }
3、环形链表II
问题:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
1 package LeetCode.test2_shuangzhizheng; 2 3 public class ques_142_环形链表II { 4 public static class ListNode { 5 int value; 6 ListNode next; 7 8 ListNode(){ 9 10 } 11 12 ListNode(int value) { 13 this.value = value; 14 } 15 } 16 17 public static void main(String[] args) { 18 19 } 20 21 public static ListNode detectCycle(ListNode head) { 22 if (head == null) { 23 return null; 24 } 25 ListNode fast = head; 26 ListNode slow = head; 27 while (fast != null && fast.next != null) { //至少有两个节点 28 fast = fast.next.next; 29 slow = slow.next; 30 //是带环链表 31 if (fast == slow) { 32 break; 33 } 34 } 35 //不是带环链表(fast==slow==null或者只有一个head节点) 36 if (fast == null || fast.next == null) { 37 return null; 38 } 39 fast = head; 40 while (fast != slow) { 41 fast = fast.next; 42 slow = slow.next; 43 } 44 return fast; 45 } 46 }
4、最小覆盖子串
问题:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
思路:
1 /** 2 * 思路:以s = "KADOBECODEBANC";t = "ABC"为例。 3 * flag[K]=0 -> flag[K]=-1 count = 3 4 * flag[A]=1 -> flag[A]=0 count = 2 5 * ...... D,O,E=-1 6 * flag[C]=1 -> flag[C]=0 count = 0 此时,left=0,right=6 7 * 8 * ans = KADOBEC 9 * flag[K]=-1 -> flag[K]=0 count=0 left = 1 10 * ans = ADOBEC min = 6 11 * 12 * flag[A]=0 -> flag[A]=1 count=1 left = 2 13 * 退出while(count==0)循环,此时right=7 字母O位置 14 * flag[O]=-1 -> flag[O]=-2 count = 1 15 * ...... D,E,B = -2,-2,-1 16 * flag[A]=1 -> flag[A]=0 count = 0 此时,left=2,right=11 17 * 18 * ans = DOBECODEBA > 6 ,所以 ans = ADOBEC 19 * flag[D]=-2 -> flag[D]=-1 count=0 left = 3 20 * flag[O]=-2 -> flag[O]=-1 count=0 left = 4 21 * flag[B]=-1 -> flag[B]=0 count=0 left = 5 22 * flag[E]=-2 -> flag[E]=-1 count=0 left = 6 23 * ans = CODEBA = 6 ,所以 ans = ADOBEC CODEBA 24 * 25 * flag[C]=0 -> flag[C]=1 count=1 left = 7 26 * 退出while(count==0)循环,此时right=12 字母N位置 27 * ...... 28 * ans = BANC 29 * 30 * ans变化过程:KADOBEC->ADOBEC->DOBECODEBA->CODEBA->ODEBANC->BANC(ans中最小的) 31 */
1 package LeetCode.test2_shuangzhizheng; 2 3 public class ques_76_最小覆盖子串 { 4 public static void main(String[] args) { 5 String s = "KADOBECODEBANC"; 6 String t = "ABC"; 7 System.out.println(minWindow(s, t)); 8 } 9 10 public static String minWindow(String s, String t) { 11 int[] flag = new int[128]; 12 //记录t字符串每个字符出现的次数 13 for (int i = 0; i < t.length(); i++) { 14 flag[t.charAt(i)]++; 15 } 16 17 int left = 0; // 窗口左边界 18 int right = 0; // 窗口右边界 19 int count = t.length(); // 需要指定元素的个数 20 int min = Integer.MAX_VALUE; // 最下窗口大小 21 String ans = ""; // 记录结果 22 23 while (right < s.length()) { 24 char ch = s.charAt(right); 25 if (flag[ch] > 0) { //如果ch是需要的字符,count减一 26 count--; 27 } 28 flag[ch]--; //如果ch是需要的字符flag[ch]--的值最小为0,如果ch不是需要的字符flag[ch]--的值为负数 29 while (count == 0) { 30 if (min > right - left + 1) { // 最优字符串 31 min = right - left + 1; 32 ans = s.substring(left, right + 1); 33 } 34 ch = s.charAt(left); 35 if (flag[ch] == 0) { //flag[ch]==0说明这个ch是需要的字符,并且刚好包含一个,把它去除就不能完全覆盖t 36 count++; 37 } 38 flag[ch]++; // 需要的ch数量加1 39 left++; 40 } 41 right++; 42 } 43 return ans; 44 } 45 }
5、平方数之和
问题:给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。
示例 1:
输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5
示例 2:
输入:c = 3
输出:false
示例 3:
输入:c = 4
输出:true
示例 4:
输入:c = 2
输出:true
示例 5:
输入:c = 1
输出:true
1 package LeetCode.test2_shuangzhizheng; 2 3 /** 4 * 双指针法 5 * 如果生成数较大,则大值减小 6 * 生成数较小,则小值变大 7 */ 8 public class ques_633_平方数之和 { 9 public static void main(String[] args) { 10 int c = 2147483600; 11 System.out.println(judgeSquareSum(c)); 12 } 13 14 public static boolean judgeSquareSum(int c) { 15 if (c < 0) { 16 return false; 17 } 18 double left = 0; 19 double right = Math.floor(Math.sqrt(c)); 20 while (left <= right) { 21 double sum = left * left + right * right; 22 if (sum > c) { 23 right--; 24 } else if (sum < c) { 25 left++; 26 } else { 27 return true; 28 } 29 } 30 return false; 31 } 32 }
6、验证回文字符串Ⅱ
问题:给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
示例 1:
输入: s = "aba"
输出: true
示例 2:
输入: s = "abca"
输出: true
解释: 你可以删除c字符。
示例 3:
输入: s = "abc"
输出: false
思路:在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。
在判断是否为回文字符串时,不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。
在试着删除字符时,既可以删除左指针指向的字符,也可以删除右指针指向的字符.(important)
1 package LeetCode.test2_shuangzhizheng; 2 3 public class ques_680_验证回文字符串Ⅱ { 4 public static void main(String[] args) { 5 // String s = "aba"; 6 // String s = "deeee"; 7 String s = "cupffpucu"; 8 // String s = "abddca"; 9 // String s = "cxccxcx"; 10 System.out.println(validPalindrome(s)); 11 } 12 13 public static boolean validPalindrome(String s) { 14 for (int i = 0, j = s.length() - 1; i < j; i++, j--) { 15 if (s.charAt(i) != s.charAt(j)) { 16 return isPalindrome(s, i + 1, j) || isPalindrome(s, i, j - 1); 17 } 18 } 19 return true; 20 } 21 22 public static boolean isPalindrome(String s, int i, int j) { 23 while (i < j) { 24 if (s.charAt(i++) != s.charAt(j--)) { 25 return false; 26 } 27 } 28 return true; 29 } 30 }
7、通过删除字母匹配到字典里最长单词
问题:给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:s = "abpcplea", dictionary = ["ale","apple","monkey","plea"]
输出:"apple"
示例 2:
输入:s = "abpcplea", dictionary = ["a","b","c"]
输出:"a"
1 package LeetCode.test2_shuangzhizheng; 2 3 import java.util.*; 4 5 public class ques_524_通过删除字母匹配到字典里最长单词 { 6 public static void main(String[] args) { 7 String s = "abpcplea"; 8 List<String> dictionary = new ArrayList<>(); 9 dictionary.add("ale"); 10 dictionary.add("apple"); 11 dictionary.add("monkey"); 12 dictionary.add("plea"); 13 System.out.println(findLongestWord(s, dictionary)); 14 15 } 16 17 public static String findLongestWord(String s, List<String> dictionary) { 18 String res = ""; 19 for (String str : dictionary) { 20 // 如果结果字符串比当前遍历的字符串更长,那就可以直接跳过 21 // 如果两个字符串长度相等,那就比较一下字典顺序,如果当前遍历的字符串更小,就跳过 22 if (res.length() > str.length() || (res.length() == str.length() && res.compareTo(str) < 0)) { 23 continue; 24 } 25 // 如果是子序列,就覆盖结果字符串 26 if (isSubStr(s, str)) { 27 res = str; 28 } 29 } 30 return res; 31 } 32 33 public static boolean isSubStr(String s, String str) { // 判断是否是子序列 34 int i = 0; 35 int j = 0; 36 while (i < s.length() && j < str.length()) { 37 if (s.charAt(i) == str.charAt(j)) { 38 j++; 39 } 40 i++; 41 } 42 return j == str.length(); 43 } 44 }
8、至多包含K个不同字符的最长子串
问题:给定一个字符串s ,找出至多包含k个不同字符的最长子串T。
示例 1:
输入: s = "eceba", k = 2
输出: 3
解释: 则 T 为 "ece",所以长度为 3。
示例 2:
输入: s = "aa", k = 1
输出: 2
解释: 则 T 为 "aa",所以长度为 2。
思路:双指针,指针中间的是可能的答案。符合要求右指针向右扩,否则更新答案,左指针往右缩。
1 package LeetCode.test2_shuangzhizheng; 2 3 import java.util.Collections; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 /** 8 * eg: s = "ecebeea" k = 2 9 * e->0 left=0 right=1 max=(0,1-0)=1 10 * e->0 c->1 left=0 right=2 max=(1,2-0)=2 11 * e->2 c->1 left=0 right=3 max=(2,3-0)=3 12 * e->2 c->1 b->3 (e->2 b->3) left=2 right=4 max=(3,4-2)=3 13 * e->4 b->3 left=2 right=5 max=(3,5-2)=3 14 * e->5 b->3 left=2 right=6 max=(3,6-2)=4 15 * e->5 b->3 a->6 (e->5 a->6) left=4 right=7 max=(4,7-24)=4 16 */ 17 18 public class ques_340_至多包含K个不同字符的最长子串 { 19 public static void main(String[] args) { 20 String s = "eceba"; 21 int k = 2; 22 System.out.println(lengthOfLongestSubstringKDistinct(s, k)); 23 } 24 25 public static int lengthOfLongestSubstringKDistinct(String s, int k) { 26 if (s.length() == k) { 27 return s.length(); 28 } 29 int left = 0; 30 int right = 0; 31 Map<Character, Integer> map = new HashMap<>(); 32 int max = 0; 33 while (right < s.length()) { 34 if (map.size() <= k) { 35 map.put(s.charAt(right), right); 36 right++; 37 } 38 if (map.size() == k + 1) { 39 int index = Collections.min(map.values()); 40 map.remove(s.charAt(index)); 41 left = index + 1; 42 } 43 max = Math.max(max, right - left); 44 } 45 return max; 46 } 47 }