给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
1输入:nums = [2,7,11,15], target = 92输出:[0,1]3解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
xxxxxxxxxx21输入:nums = [3,2,4], target = 62输出:[1,2]
示例 3:
xxxxxxxxxx21输入:nums = [3,3], target = 62输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
xxxxxxxxxx231class Solution {2 public int[] twoSum(int[] nums, int target) {3 int[] res = new int[2];4 //讨论特殊情况5 if (nums == null || nums.length == 0) {6 return res;7 }8 //使用一个Map来记录数组的值和索引值9 Map<Integer,Integer> map = new HashMap<>();10 for (int i = 0; i < nums.length; i++) {11 //定义一个临时变量12 int temp = target - nums[i];13 //更新res数组14 if (map.containsKey(temp)){15 res[0] = i;16 res[1] = map.get(temp);17 break;//结束循环18 }19 map.put(nums[i],i);20 }21 return res;22 }23}给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
xxxxxxxxxx31输入:l1 = [2,4,3], l2 = [5,6,4]2输出:[7,0,8]3解释:342 + 465 = 807.
示例 2:
xxxxxxxxxx21输入:l1 = [0], l2 = [0]2输出:[0]
示例 3:
xxxxxxxxxx21输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]2输出:[8,9,9,9,0,0,0,1]
提示:
每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零
Related Topics
递归
链表
数学
xxxxxxxxxx351class Solution {2 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {3 //辅助的哑节点4 ListNode pre = new ListNode(-1);5 //辅助遍历的节点6 ListNode cur = pre;7 //carry表示十位,要么是1要么是0,这里的十位是指两个指针指向的两个数相加后的十位8 int carry = 0;9 while (l1 != null || l2 != null){10 //获取节点值,如果节点为空,就是011 int x = l1 == null ? 0 : l1.val;12 int y = l2 == null ? 0 : l2.val;13 int sum = x + y +carry;14 //计算十位15 carry = sum / 10;16 //计算个位17 sum = sum % 10;18 //添加节点19 cur.next = new ListNode(sum);20 //节点往后移21 cur = cur.next;22 if (l1 != null){23 l1 = l1.next;24 }25 if (l2 != null){26 l2 = l2.next;27 }28 }29 //如果到最后十位还是1的话,得补一个节点30 if (carry == 1){31 cur.next = new ListNode(carry);32 }33 return pre.next;34 }35}给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
xxxxxxxxxx31输入: s = "abcabcbb"2输出: 33解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
xxxxxxxxxx31输入: s = "bbbbb"2输出: 13解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
xxxxxxxxxx41输入: s = "pwwkew"2输出: 33解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。4请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
Related Topics
哈希表
字符串
滑动窗口
x1class Solution {2 public static int lengthOfLongestSubstring(String s) {3 //特殊情况,字符串长度为04 if (s.length() == 0) return 0;5 //使用map记录当前滑动窗口是否存在当前遍历到的字符6 Map<Character,Integer> map = new HashMap<>();7 //转为数组8 char[] chars = s.toCharArray();9 int res = 0;10 //滑动窗口左边界11 int left = 0;12 //右边界13 int right = 0;14 while (right < chars.length){15 //不包含当前字符,我们就记录一下,右边界右移16 if (!map.containsKey(chars[right]) ){17 map.put(chars[right],1);18 right++;19 }else {20 //说明包含了,map先删除这个字符21 //左边界右移22 map.remove(chars[left]);23 left++;24 }25 //记录滑动窗口的最大值26 //因为前面right已经++了,所以这里right-left不用+127 res = Math.max(res,right-left);28 }29 return res;30 }31}32
xxxxxxxxxx231//对上一个方法进行优化,上一个方法里是left的更新比较慢,因为一旦遇到重复之后就需要更新left到重复字符的下一个位置,所以在这里对这个细节进行优化2//举个例子,假设字符串是 "abcba":当遍历到第二个 "b" 时,发现它在位置 3 和位置 1 都出现过,此时需要更新 start 的值。通过 map.get(c) 可以得到最近一次 "b" 出现的位置为 1,那么通过 Math.max(map.get(c), start) 就可以得到新的 start 值为 1+1 = 2,即重复字符的下一个位置。3class Solution {4 public static int lengthOfLongestSubstring(String s) {5 if (s.length() == 0) return 0;6 Map<Character,Integer> map = new HashMap<>();7 //左边界8 int start = 0;9 int res = 0;10 //end右边界11 for (int end = 0; end < s.length();end++){12 char c = s.charAt(end);13 if (map.containsKey(c)){14 //左边界直接跳到右边界15 start = Math.max(map.get(c),start);16 }17 res = Math.max(res,end-start+1);18 //end+1是表示记录当前字符索引的下一位,这样就能直接跳到重复字符的下一位了19 map.put(s.charAt(end),end+1);20 }21 return res;22 }23}给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
xxxxxxxxxx31输入:nums1 = [1,3], nums2 = [2]2输出:2.000003解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
xxxxxxxxxx31输入:nums1 = [1,2], nums2 = [3,4]2输出:2.500003解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
Related Topics
数组
二分查找
分治
xxxxxxxxxx291//合并数组,但只要合并到中间位置就可以了2class Solution {3 public static double findMedianSortedArrays(int[] nums1, int[] nums2) {4 int m = nums1.length, n = nums2.length;5 int l = m + n;6 int i = 0, j = 0;7 double[] res = new double[l];8 int index = 0;9 while (i < m && j < n && i + j <= l / 2){10 if (nums1[i] <= nums2[j]){11 res[index++] = nums1[i++];12 }else {13 res[index++] = nums2[j++];14 }15 }16 //有可能只是一个数组元素很少,遍历完了,但是还没遍历到合并数组的中间位置17 while (i == m && i + j <= l /2){18 res[index++] = nums2[j++];19 }20 while (j == n && i + j <= l /2){21 res[index++] = nums1[i++];22 }23 if (l %2 == 0){24 return (res[l/2] + res[l/2 - 1]) / 2;25 }else {26 return res[l/2];27 }28 }29}给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
xxxxxxxxxx31输入:s = "babad"2输出:"bab"3解释:"aba" 同样是符合题意的答案。
示例 2:
xxxxxxxxxx21输入:s = "cbbd"2输出:"bb"
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成
Related Topics
字符串
动态规划
xxxxxxxxxx481class Solution {2 public String longestPalindrome(String s) {3 int len = s.length();4 //特殊情况:如果字符串长度只有1,那一定是回文子串5 if (len < 2) return s;6 //dp表示在s[i...j]范围内是否是回文子串7 boolean[][] dp = new boolean[len][len];8 //初始化:根据dp的含义,而且我们知道单个字符肯定是回文子串9 for (int i = 0; i < len; i++) {10 dp[i][i] = true;11 }12
13 char[] chars = s.toCharArray();14
15 int begin = 0;//记录回文子串的起始位置16 int maxLen = 1;//记录回文子串的最大长度17
18 //遍历19 //用动态规划dp[i][j]是否为true,是取决于dp[i+1][j-1]的20 //所以这里我们先遍历列,再遍历行21 for (int j = 1; j < len; j++) {22 for (int i = 0; i < j; i++) {23 //两个字符不同,那肯定不是回文24 if (chars[i] != chars[j]){25 dp[i][j] = false;26 }else {27 //dp[i][j]是否为true,是取决于dp[i+1][j-1]的28 //判断一下边界条件,(j-1)-(i+1)+1 >= 2 才能用状态转移方程29 //不然的话,如果长度小于2,要么为1要么为0,再加上当前i和j的字符是相同的30 //那么dp[j][j]肯定是true了31 if (j - i < 3){32 dp[i][j] = true;33 }else {34 dp[i][j] = dp[i+1][j-1];35 }36 }37 //如果当前是回文子串,并且长度也是大于maxLen38 //那么就更新begin 和 maxLen39 if (dp[i][j] && j - i + 1 > maxLen){40 maxLen = j - i + 1;41 begin = i;42 }43 }44 }45
46 return s.substring(begin,begin+maxLen);47 }48}给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
示例 1:
xxxxxxxxxx31输入:s = "aa", p = "a"2输出:false3解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
xxxxxxxxxx31输入:s = "aa", p = "a*"2输出:true3解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
xxxxxxxxxx31输入:s = "ab", p = ".*"2输出:true3解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
提示:
1 <= s.length <= 20
1 <= p.length <= 20
s 只包含从 a-z 的小写字母。
p 只包含从 a-z 的小写字母,以及字符 . 和 *。
保证每次出现字符 * 时,前面都匹配到有效的字符
Related Topics
递归
字符串
动态规划
https://www.iamshuaidi.com/2032.html
xxxxxxxxxx301class Solution {2 public boolean isMatch(String s, String p) {3 if (s == null || p == null){4 return true;5 }6 int n = s.length();7 int m = p.length();8 boolean[][] dp = new boolean[n+1][m+1];9 dp[0][0] = true;10 for (int j = 1; j <= m ; j++) {11 if (p.charAt(j-1) == '*'){12 dp[0][j] = dp[0][j-2];13 }14 }15 for(int i = 1; i <= n; i++){16 for(int j = 1; j <= m; j++){17 if(p.charAt(j-1)=='.'||p.charAt(j-1)==s.charAt(i-1))18 dp[i][j] = dp[i-1][j-1];19 else if(p.charAt(j-1)=='*'){20 if(j!=1&&p.charAt(j-2)!='.'&&p.charAt(j-2)!=s.charAt(i-1)){21 dp[i][j] = dp[i][j-2];22 }else{23 dp[i][j] = dp[i][j-2] || dp[i][j-1]||dp[i-1][j];24 }25 }26 }27 }28 return dp[n][m];29 }30}给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
示例 1:

xxxxxxxxxx31输入:[1,8,6,2,5,4,8,3,7]2输出:493解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
xxxxxxxxxx21输入:height = [1,1]2输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
Related Topics
贪心
数组
双指针
xxxxxxxxxx271class Solution {2 public int maxArea(int[] height) {3 //考虑特殊情况4 if (height.length < 2) return 0;5 //返回的结果6 int res = 0;7 //双指针8 int left = 0, right = height.length-1;9 while (left < right){10 //h是高11 int h = Math.min(height[left],height[right]);12 //计算面积13 int area = h * (right - left);14 //更新res15 res = Math.max(res,area);16 //如果左指针对应的高度比h低的话,指针右移17 while (left < right && height[left] <= h){18 left++;19 }20 //如果右指针对应的高度比h低的话,指针左移21 while (left < right && height[right] <= h){22 right--;23 }24 }25 return res;26 }27}给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
xxxxxxxxxx81输入:nums = [-1,0,1,2,-1,-4]2输出:[[-1,-1,2],[-1,0,1]]3解释:4nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。5nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。6nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。7不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。8注意,输出的顺序和三元组的顺序并不重要。
示例 2:
xxxxxxxxxx31输入:nums = [0,1,1]2输出:[]3解释:唯一可能的三元组和不为 0 。
示例 3:
xxxxxxxxxx31输入:nums = [0,0,0]2输出:[[0,0,0]]3解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
Related Topics
数组
双指针
排序
xxxxxxxxxx481class Solution {2 public List<List<Integer>> threeSum(int[] nums) {3 List<List<Integer>> res = new LinkedList<>();4 //排序,从小到大5 Arrays.sort(nums);6 //数组长度7 int n = nums.length;8 //遍历第一个数,只需要到n-2,n-1是第三个数在遍历9 for (int i = 0; i < n - 2; i++) {10 //跳过重复数字11 if (i > 0 && nums[i] == nums[i-1]){12 continue;13 }14 //优化细节1:如果前三个数加起来都大于0了,那么就不可能存在三个数加起来能等于015 if (nums[i] + nums[i+1] + nums[i+2] > 0) {16 return res;17 }18 //优化细节1,如果最小的和最大的两个加起来小于0的话,那就没必要去遍历第二个数和第三个数了,因为这两个数比最大的两个数小19 //直接进入下一循环20 if (nums[i] + nums[n-2] + nums[n-1] < 0){21 continue;22 }23 int left = i + 1;24 int right = nums.length - 1;25 while (left < right){26 if (nums[i] + nums[left] + nums[right] == 0){27 res.add(Arrays.asList(nums[i],nums[left],nums[right]));28 //去重第二个数字29 while (left < right && nums[right] == nums[right-1]){30 right--;31 }32 //去重第三个数33 while (left < right && nums[left] == nums[left+1]){34 left++;35 }36 //移动指针37 left++;38 right--;39 }else if (nums[i] + nums[left] + nums[right] < 0){40 left++;41 }else {42 right--;43 }44 }45 }46 return res;47 }48}给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:
xxxxxxxxxx21输入:digits = "23"2输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
xxxxxxxxxx21输入:digits = ""2输出:[]
示例 3:
xxxxxxxxxx21输入:digits = "2"2输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。
Related Topics
哈希表
字符串
回溯
xxxxxxxxxx441class Solution {2 List<String> res = new LinkedList<>();3 StringBuffer sb = new StringBuffer();4 String[] numsStr = new String[]{5 "",6 "",7 "abc",8 "def",9 "ghi",10 "jkl",11 "mno",12 "pqrs",13 "tuv",14 "wxyz"15 };16 public List<String> letterCombinations(String digits) {17 if (digits.length() == 0) {18 return res;19 }20 dfs(digits,0);21 return res;22 }23
24 public void dfs(String digits, int index){25 //已经递归到这个数字对应的最后一个字母了,那就记录26 if (index == digits.length()) {27 res.add(sb.toString());28 return;29 }30 //获取这个数字对应的字母字符串31 String numChar = numsStr[digits.charAt(index)-'0'];32 //遍历字母字符33 for (int i = 0; i < numChar.length(); i++) {34 //记录该字母35 sb.append(numChar.charAt(i));36 //递归37 dfs(digits,index+1);38 //回溯39 sb.deleteCharAt(sb.length()-1);40 }41
42 }43
44}xxxxxxxxxx411//回溯的另一种写法2class Solution {3 public static final String[] MAPPING = new String[]{4 "",5 "",6 "abc",7 "def",8 "ghi",9 "jkl",10 "mno",11 "qprs",12 "tuv",13 "wxyz"14 };15
16 List<String> res = new LinkedList<>();17 char[] path, digits;18
19 public List<String> letterCombinations(String digits) {20 int n = digits.length();21 if (n == 0) return res;22 this.digits = digits.toCharArray();23 path = new char[n];24 dfs(0);25 return res;26 }27
28 public void dfs(int i){29 if (i == digits.length) {30 res.add(new String(path));31 return;32 }33 //比如这里的是digits是[2,3]34 //那么MAPPING[2]对应的就是"abc"35 for (char c : MAPPING[digits[i] -'0'].toCharArray()){36 //这里path中的元素虽然没有被删掉,但在下一层循环中会被覆盖37 path[i] = c;38 dfs(i+1);39 }40 }41}
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
xxxxxxxxxx21输入:head = [1,2,3,4,5], n = 22输出:[1,2,3,5]
示例 2:
xxxxxxxxxx21输入:head = [1], n = 12输出:[]
示例 3:
xxxxxxxxxx21输入:head = [1,2], n = 12输出:[1]
提示:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
Related Topics
链表
双指针
xxxxxxxxxx271class Solution {2 public ListNode removeNthFromEnd(ListNode head, int n) {3 //哑结点的好处在于可以不用讨论头结点被删除的情况4 ListNode dummy = new ListNode(-1,head);5 ListNode temp = dummy;6 //获取链表长度7 int len = getLength(head);8 int k = len - n ;9 for (int i = 0; i <= k; i++) {10 if (i == k){11 temp.next = temp.next.next;12 break;13 }14 temp = temp.next;15 }16 return dummy.next;17 }18
19 public int getLength(ListNode head){20 int len = 0;21 while (head != null){22 len++;23 head = head.next;24 }25 return len;26 }27}xxxxxxxxxx181class Solution {2 public ListNode removeNthFromEnd(ListNode head, int n) {3 ListNode dummy = new ListNode(-1, head);4 ListNode left = dummy;5 ListNode right =dummy;6 //让right指针先走n步7 while (n-- > 0){8 right = right.next;9 }10 //然后left和right一起走,直到right到最后一个节点,这个时候left所指向的就是待删除节点的前一个节点11 while (right.next != null){12 left = left.next;13 right = right.next;14 }15 left.next = left.next.next;16 return dummy.next;17 }18}给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
xxxxxxxxxx21输入:s = "()"2输出:true
示例 2:
xxxxxxxxxx21输入:s = "()[]{}"2输出:true
示例 3:
xxxxxxxxxx21输入:s = "(]"2输出:false
提示:
1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成
Related Topics
栈
字符串
xxxxxxxxxx241class Solution {2 public boolean isValid(String s) {3 char[] chars = s.toCharArray();4 Deque<Character> deque = new LinkedList<>();5 for (int i = 0; i < chars.length; i++) {6 char c = chars[i];7 //遇到左括号,就把右括号放入队列中8 if (c == '(') {9 deque.push(')');10 }else if (c == '[') {11 deque.push(']');12 }else if (c == '{') {13 deque.push('}');14 }else if (deque.isEmpty() || deque.peek() != c){15 //因为当前已经遍历到字符并且这个字符不是左括号,但是队列已经为空了,说明右括号多了16 //如果头元素和当前字符不一样,要么是左括号多了,要么就是左右括号不能匹配17 return false;18 }else {//如果遇到队列头元素和当前字符相同,就把头元素删掉19 deque.pop();20 }21 }22 return deque.isEmpty();23 }24}将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
xxxxxxxxxx21输入:l1 = [1,2,4], l2 = [1,3,4]2输出:[1,1,2,3,4,4]
示例 2:
xxxxxxxxxx21输入:l1 = [], l2 = []2输出:[]
示例 3:
xxxxxxxxxx21输入:l1 = [], l2 = [0]2输出:[0]
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
Related Topics
递归
链表
xxxxxxxxxx261//最简单的办法是新造一个链表,遍历这两个链表,比较两个节点的大小,用小节点的值创建新的节点,连接在新链表之后2class Solution {3 public ListNode mergeTwoLists(ListNode list1, ListNode list2) {4 if (list1 == null) return list2;5 if (list2 == null) return list1;6 ListNode dummy = new ListNode(-1);7 ListNode cur = dummy;8 while (list1 != null && list2 != null){9 if (list1.val <= list2.val) {10 cur.next = new ListNode(list1.val);11 list1 = list1.next;12 } else {13 cur.next = new ListNode(list2.val);14 list2 = list2.next;15 }16 cur = cur.next;17 }18 if (list1 != null) {19 cur.next = list1;20 }21 if (list2 != null){22 cur.next = list2;23 }24 return dummy.next;25 }26}xxxxxxxxxx261//方法二,使用原来链表的节点去造新链表2class Solution {3 public ListNode mergeTwoLists(ListNode list1, ListNode list2) {4 if (list1 == null) return list2;5 if (list2 == null) return list1;6 ListNode dummy = new ListNode(-1);7 ListNode cur = dummy;8 while (list1 != null && list2 != null){9 if (list1.val <= list2.val) {10 cur.next = list1;11 list1 = list1.next;12 } else {13 cur.next = list2;14 list2 = list2.next;15 }16 cur = cur.next;17 }18 if (list1 != null) {19 cur.next = list1;20 }21 if (list2 != null){22 cur.next = list2;23 }24 return dummy.next;25 }26}xxxxxxxxxx141//使用递归2class Solution {3 public ListNode mergeTwoLists(ListNode list1, ListNode list2) {4 if (list1 == null) return list2;5 if (list2 == null) return list1;6 if (list1.val <= list2.val) {7 list1.next = mergeTwoLists(list1.next,list2);8 return list1;9 }else {10 list2.next = mergeTwoLists(list1,list2.next);11 return list2;12 }13 }14}数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
xxxxxxxxxx21输入:n = 32输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:
xxxxxxxxxx21输入:n = 12输出:["()"]
提示:
1 <= n <= 8
Related Topics
字符串
动态规划
回溯
xxxxxxxxxx281class Solution {2 List res = new LinkedList<>(); // 保存结果的集合3 char[] path; // 保存括号组合的数组4 int n; // 括号对数5
6 public List<String> generateParenthesis(int n) {7 path = new char[2 * n]; // 初始化括号组合数组的长度8 this.n = n; // 保存括号对数9 dfs(0,0); // 开始深度优先搜索,以0作为起始位置,初始括号数为010 return res; // 返回结果集合11 }12
13 public void dfs(int i, int open) {14 if (i == 2 *n){ // 当组合的括号对数达到2n时,即所有位置都填满了括号15 res.add(new String(path)); // 将当前的括号组合转化为字符串,并添加进结果集合16 return; // 结束当前的深度优先搜索17 }18
19 if (open < n) { // 如果当前开放的左括号数小于n20 path[i] = '('; // 在当前位置添加一个左括号21 dfs(i+1,open+1); // 继续下一个位置的搜索,并更新左括号数22 }23 if (i - open < open) { // 如果当前位置之前的右括号数小于左括号数24 path[i] = ')'; // 在当前位置添加一个右括号25 dfs(i+1,open); // 继续下一个位置的搜索,并保持左括号数不变26 }27 }28}给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
xxxxxxxxxx101输入:lists = [[1,4,5],[1,3,4],[2,6]]2输出:[1,1,2,3,4,4,5,6]3解释:链表数组如下:4[51->4->5,61->3->4,72->68]9将它们合并到一个有序链表中得到。101->1->2->3->4->4->5->6
示例 2:
xxxxxxxxxx21输入:lists = []2输出:[]
示例 3:
xxxxxxxxxx21输入:lists = [[]]2输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
Related Topics
链表
分治
堆(优先队列)
归并排序
xxxxxxxxxx211class Solution {2 public ListNode mergeKLists(ListNode[] lists) {3 //优先队列,默认情况下,最小的数排在队列头部4 PriorityQueue<Integer> queue = new PriorityQueue<>();5 for (ListNode listNode : lists) {//将所有节点值塞进优先队列中,这样就得到了从小到大的排序6 while (listNode != null) {7 queue.add(listNode.val);8 listNode = listNode.next;9 }10 }11
12 ListNode dummy = new ListNode(-1);13 ListNode cur = dummy;14 while (!queue.isEmpty()) {//通过遍历队列,创建节点15 cur.next = new ListNode(queue.poll());16 cur = cur.next;17 }18 return dummy.next;19
20 }21}xxxxxxxxxx461class Solution {2 public ListNode mergeKLists(ListNode[] lists) {3 if (lists == null || lists.length == 0) {4 return null;5 }6 if (lists.length == 1) {7 return lists[0];8 }9 return mergeKListNodes(lists, 0, lists.length - 1);10 }11
12 // 合并 lists 数组中区间 [i, j] 的链表13 public ListNode mergeKListNodes(ListNode[] lists, int i, int j) {14 if (i == j) {15 return lists[i]; // 当 i 和 j 相等时,直接返回该链表16 }17 int mid = i + (j - i) / 2;18 ListNode left = mergeKListNodes(lists, i, mid); // 递归合并左侧区间 [i, mid]19 ListNode right = mergeKListNodes(lists, mid + 1, j); // 递归合并右侧区间 [mid + 1, j]20 return mergeTwoListNode(left, right); // 合并左右两个链表21 }22
23 // 合并两个有序链表24 public ListNode mergeTwoListNode(ListNode listNode1, ListNode listNode2) {25 if (listNode1 == null) {26 return listNode2;27 }28 if (listNode2 == null) {29 return listNode1;30 }31 ListNode dummy = new ListNode(-1);32 ListNode cur = dummy;33 while (listNode1 != null && listNode2 != null) {34 if (listNode1.val <= listNode2.val) {35 cur.next = listNode1; // 将当前节点连接到合并后的链表中36 listNode1 = listNode1.next; // 移动 listNode1 的指针37 } else {38 cur.next = listNode2; // 将当前节点连接到合并后的链表中39 listNode2 = listNode2.next; // 移动 listNode2 的指针40 }41 cur = cur.next; // 移动合并后的链表指针42 }43 cur.next = listNode1 == null ? listNode2 : listNode1; // 将剩余的节点连接到合并后的链表中44 return dummy.next; // 返回合并后的链表45 }46}整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
xxxxxxxxxx21输入:nums = [1,2,3]2输出:[1,3,2]
示例 2:
xxxxxxxxxx21输入:nums = [3,2,1]2输出:[1,2,3]
示例 3:
xxxxxxxxxx21输入:nums = [1,1,5]2输出:[1,5,1]
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 100
Related Topics
数组
双指针
xxxxxxxxxx241public static void nextPermutation(int[] nums) {2 int len = nums.length;3 int left = len - 2; // 从倒数第二个元素开始向左遍历4 int right = len - 1; // 从倒数第一个元素开始向左比较5 while (left >= 0) { // 遍历数组中的每一个元素6 if (nums[left] < nums[right]) { // 找到两个相邻元素,并且左边的比右边的小7 for (int i = len - 1; i >= right; i--) { // 从右向左找到第一个大于较小元素的元素8 if (nums[i] > nums[left]) {9 int temp = nums[i];10 nums[i] = nums[left];11 nums[left] = temp;12 Arrays.sort(nums, right, len); // 对较小元素右侧的元素进行排序13 break;14 }15 }16 break; // 执行了上面的代码就可以结束遍历了17 }18 left--;19 right--;20 }21 if (left < 0) { // 如果遍历到left<0,说明就找不到下一个排列,那就直接从小到大排序22 Arrays.sort(nums);23 }24}xxxxxxxxxx371class Solution {2 public void nextPermutation(int[] nums) {3 int i = nums.length - 2;4 //寻找相邻的两个数,并且左边的数小于右边的数5 while (i >= 0 && nums[i] >= nums[i+1]){//只要有一个条件不满足,就停止循环6 i--;7 }8 //只有i大于等于0,才说明找到了那两个相邻的数9 if (i >= 0) {10 //j从后往前遍历,找第一个比nums[i]大的数11 int j = nums.length - 1;12 while (j >= 0 && nums[i] >= nums[j]) {13 j--;14 }15 //交换这两个数16 swap(nums,i,j);17 }18 //将i往后的数进行从小到大排序19 reverse(nums,i+1);20 }21
22 public void swap(int[] nums,int i, int j) {23 int temp = nums[i];24 nums[i] = nums[j];25 nums[j] = temp;26 }27
28 public void reverse(int[] nums, int index){29 int left = index;30 int right = nums.length - 1;31 while (left < right) {32 swap(nums,left,right);33 left++;34 right--;35 }36 }37}给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
xxxxxxxxxx31输入:s = "(()"2输出:23解释:最长有效括号子串是 "()"
示例 2:
xxxxxxxxxx31输入:s = ")()())"2输出:43解释:最长有效括号子串是 "()()"
示例 3:
xxxxxxxxxx21输入:s = ""2输出:0
提示:
0 <= s.length <= 3 * 104
s[i] 为 '(' 或 ')'
Related Topics
栈
字符串
动态规划
xxxxxxxxxx241/**2借助一个栈(Deque)来解决有效括号子串的问题。当遇到左括号时,将其下标入栈;当遇到右括号时,弹出栈顶元素表示匹配成功。对于有效的括号子串,栈中存储的是括号的下标,而不是括号本身3如果我们不事先将 -1 放入栈中,那么当字符串 s 的第一个字符是右括号时,栈将为空,导致后续计算有效括号子串长度时出现问题。通过事先放入 -1,我们可以确保栈中始终有一个元素,从而使得后续计算有效括号子串长度的操作能够正确进行。4*/5class Solution {6 public int longestValidParentheses(String s) {7 int maxLen = 0;8 Deque<Integer> deque = new LinkedList<>(); // 使用栈来存储括号的下标9 deque.push(-1); // 在栈中事先放入-1,以处理特殊情况10 for (int i = 0; i < s.length(); i++) {11 if (s.charAt(i) == '(') { // 遇到左括号,将其下标入栈12 deque.push(i);13 } else { // 遇到右括号14 deque.pop(); // 弹出栈顶元素,表示匹配成功15 if (deque.isEmpty()) { // 栈为空,当前右括号没有匹配的左括号与之对应16 deque.push(i); // 将当前右括号的下标入栈,以便计算后续的有效括号子串长度17 } else { // 栈非空,计算当前有效括号子串的长度18 maxLen = Math.max(maxLen, i - deque.peek());19 }20 }21 }22 return maxLen;23 }24}整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
xxxxxxxxxx21输入:nums = [4,5,6,7,0,1,2], target = 02输出:4
示例 2:
xxxxxxxxxx21输入:nums = [4,5,6,7,0,1,2], target = 32输出:-1
示例 3:
xxxxxxxxxx21输入:nums = [1], target = 02输出:-1
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104
Related Topics
数组
二分查找
xxxxxxxxxx261//二分法2class Solution {3 public int search(int[] nums, int target) {4 int left = 0;5 int right = nums.length - 1;6 while (left <= right) {7 int mid = left + (right - left) / 2;8 if (nums[mid] == target) {9 return mid;10 }else if (nums[mid] < nums[right]) {//右半部分有序11 if (nums[mid] < target && target <= nums[right]) {//目标值是否在右半部分12 left = mid + 1;13 }else {14 right = mid - 1;15 }16 }else {//左半部分有序17 if (nums[left] <= target && target < nums[mid]) {//目标值是否在左半部分18 right = mid - 1;19 }else {20 left = mid + 1;21 }22 }23 }24 return -1;25 }26}给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
xxxxxxxxxx21输入:nums = [5,7,7,8,8,10], target = 82输出:[3,4]
示例 2:
xxxxxxxxxx21输入:nums = [5,7,7,8,8,10], target = 62输出:[-1,-1]
示例 3:
xxxxxxxxxx21输入:nums = [], target = 02输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
Related Topics
数组
二分查找
xxxxxxxxxx651class Solution {2 public int[] searchRange(int[] nums, int target) {3 //得到的边界是开区间的4 int leftBorder = findLeftBorder(nums,target);5 int rightBorder = findRightBorder(nums,target);6 //情况一:target小于数组的最小值或大于最大值,那么leftBorder和rightBorder就不会被更新7 if (leftBorder == -2 || rightBorder == -2) {8 return new int[] {-1,-1};9 }10 //情况二:target在数组中11 if (rightBorder - leftBorder > 1){12 return new int[]{leftBorder+1,rightBorder-1};13 }14 //情况三:target介于数组最小值和最大值之间,但不存在于数组中15 return new int[]{-1,-1};16
17 }18
19 /**20 * 寻找左边界21 * @param nums22 * @param target23 * @return24 */25 public int findLeftBorder(int[] nums, int target) {26 int left = 0;27 int right = nums.length-1;28 int leftBorder = -2;29 while (left <= right) {30 int mid = left + (right - left) /2;31 if (nums[mid] >= target) {32 right = mid -1;33 leftBorder = right;34 }else if (nums[mid] < target) {35 left = mid + 1;36 }37
38 }39 return leftBorder;40 }41
42 /**43 * 寻找右边界44 * @param nums45 * @param target46 * @return47 */48 public int findRightBorder(int[] nums,int target) {49 int left = 0;50 int right = nums.length-1;51 int rightBorder = -2;52 while (left <= right) {53 int mid = left + (right - left) /2;54 if (nums[mid] <= target) {55 left = mid +1;56 rightBorder = left;57 }else if (nums[mid] > target) {58 right = mid - 1;59 }60
61 }62 return rightBorder;63 }64
65}给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
xxxxxxxxxx61输入:candidates = [2,3,6,7], target = 72输出:[[2,2,3],[7]]3解释:42 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。57 也是一个候选, 7 = 7 。6仅有这两种组合。
示例 2:
xxxxxxxxxx21输入: candidates = [2,3,5], target = 82输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
xxxxxxxxxx21输入: candidates = [2], target = 12输出: []
提示:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40
Related Topics
数组
回溯
xxxxxxxxxx261class Solution {2 List<List<Integer>> res = new LinkedList<>();3 List<Integer> path = new LinkedList<>();4
5 public List<List<Integer>> combinationSum(int[] candidates, int target) {6 if (candidates== null || candidates.length == 0) {7 return res;8 }9 backTracking(candidates,target,0);10 return res;11 }12
13 public void backTracking(int[] candidates, int target,int index) {14 if (target == 0) {15 res.add(new ArrayList<>(path));16 return;17 }18
19 for (int i = index; i < candidates.length; i++) {20 if (target - candidates[i] < 0) continue;21 path.add(candidates[i]);22 backTracking(candidates,target-candidates[i],i);23 path.remove(path.size()-1);24 }25 }26}xxxxxxxxxx361class Solution {2 List<List<Integer>> res = new ArrayList<>();3 List<Integer> path = new ArrayList<>();4
5 public List<List<Integer>> combinationSum(int[] candidates, int target) {6 if (candidates == null || candidates.length == 0) {7 return res;8 }9
10 // 优化1:对候选数组进行排序11 //通过对候选数组进行排序,可以将较小的元素尽早放入组合中,从而在搜索过程中更早地进行剪枝操作。当目标值小于当前选择的数字时,可以提前结束搜索,因为后续的数字更大,无法满足目标值要求。12 Arrays.sort(candidates);13
14 backTracking(candidates, target, 0);15 return res;16 }17
18 public void backTracking(int[] candidates, int target, int start) {19 if (target == 0) {20 res.add(new ArrayList<>(path));21 return;22 }23
24 for (int i = start; i < candidates.length; i++) {25 // 优化2:目标值的剪枝26 if (target < candidates[i]) {27 break;28 }29
30 path.add(candidates[i]);31 backTracking(candidates, target - candidates[i], i);32 path.remove(path.size() - 1);33
34 }35 }36}给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:

xxxxxxxxxx31输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]2输出:63解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
xxxxxxxxxx21输入:height = [4,2,0,3,2,5]2输出:9
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
Related Topics
栈
数组
双指针
动态规划
单调栈
思路:https://www.bilibili.com/video/BV1Qg411q7ia/?vd_source=92cb5cb9faa01574e9b1f82bf91d080d
xxxxxxxxxx11
xxxxxxxxxx161class Solution {2 public int trap(int[] height) {3 int len = height.length;4 int pre_max = 0;5 int suf_max = 0;6 int left = 0;7 int right = len - 1;8 int res = 0;9 while (left <= right) {10 pre_max = Math.max(pre_max, height[left]);11 suf_max = Math.max(suf_max, height[right]);12 res += pre_max < suf_max ? pre_max - height[left++] : suf_max-height[right--];13 }14 return res;15 }16}