From f5a1d35d7cad02f1e44476cdcc20e7192d6277bb Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Sun, 5 Mar 2023 14:35:25 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hot100/T19_LongestValidParentheses.java | 175 ++++++++++++++++++ .../markilue/leecode/hot100/T20_Search.java | 135 ++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T19_LongestValidParentheses.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T20_Search.java diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T19_LongestValidParentheses.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T19_LongestValidParentheses.java new file mode 100644 index 0000000..567344f --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T19_LongestValidParentheses.java @@ -0,0 +1,175 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.Stack; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-05 10:59 + *@Description: + * TODO 力扣32题 最长有效括号: + * 给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 + *@Version: 1.0 + */ +public class T19_LongestValidParentheses { + + @Test + public void test() { +// String s = "(())"; +// String s = "()()"; +// String s = "(()())"; +// String s = ")(((((()())()()))()(()))("; + String s = ")())"; +// String s = "()(()"; + System.out.println(longestValidParentheses3(s)); + } + + + /** + * 思路:其实就是将有效括号 进一步变为了寻找最长,那么可以使用动态规划法 + * TODO DP五部曲: + * 1.dp定义: dp[i][j]表示 以i结尾以j开头的子串 是不是有效括号 + * 2.dp状态转移方程: + * 1. s[i]匹配得上s[j] + * dp[i][j]=dp[i-1][j+1] + * 3.dp初始化: 为了初始化方便 s[i] s[j]变为s[i-1] dp[i][j]=true + * 4.dp遍历顺序:从上到下 + * 5.dp举例推导: + * 暂时有问题 + * @param s + * @return + */ + public int longestValidParentheses(String s) { + + char[] chars = s.toCharArray(); + + boolean[][] dp = new boolean[chars.length][chars.length]; + int maxLength = 0; + for (int i = 0; i < chars.length; i++) { + for (int j = 0; j < i; j++) { + if (isMatch(chars, i, j)) { + if (i == j + 1) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i - 1][j + 1]; + } + } + + //判断他前后是不是也能匹配上()()的情况 + if (dp[i][j]) { + int start = j; + for (int k = j - 1; k >= 0; k--) { + if (dp[j - 1][k]) { + dp[i][k] = true;//修复,对应的位置 + start = k; + break; + } + }//找起始位置 + if (maxLength < i - start + 1) maxLength = i - start + 1; + } + } + } + + return maxLength; + + } + + public boolean isMatch(char[] chars, int i, int j) { + return chars[i] == ')' && chars[j] == '('; + } + + + /** + * 仍然使用stack记录法 + * 暂时有问题 + * @param s + * @return + */ + public int longestValidParentheses1(String s) { + + char[] chars = s.toCharArray(); + + int maxLength = 0; + int curLength = 0; + + Stack stack = new Stack<>(); + + for (char aChar : chars) { + if (aChar == '(') { + stack.push(aChar); + } else { + if (!stack.isEmpty()) { + stack.pop(); + curLength += 2; + if (maxLength < curLength) maxLength = curLength; + } else { + stack.clear(); + curLength = 0; + } + } + } + + + return maxLength; + + } + + + /** + * 官方动态规划法: + * TODO + * 1.s[i]=‘)’ 且 s[i−1]=‘(’s[i - 1] = \text{‘(’}s[i−1]=‘(’,也就是字符串形如 “……()”“……()”“……()”,我们可以推出: + * dp[i]=dp[i−2]+2 + * 2.s[i]=‘)’s[i] = \text{‘)’}s[i]=‘)’ 且 s[i−1]=‘)’s[i - 1] = \text{‘)’}s[i−1]=‘)’,也就是字符串形如 “……))”“……))”“……))”,我们可以推出: 如果 s[i−dp[i−1]−1]=‘(’s[i - \textit{dp}[i - 1] - 1] = \text{‘(’}s[i−dp[i−1]−1]=‘(’,那么 + * dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2 + * @param s + * @return + */ + public int longestValidParentheses2(String s) { + int maxans = 0; + int[] dp = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; + } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') { + dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2; + } + maxans = Math.max(maxans, dp[i]); + } + } + return maxans; + } + + + /** + * 官方栈法:存入栈的是位置 + * @param s + * @return + */ + public int longestValidParentheses3(String s) { + int maxans = 0; + Deque stack = new LinkedList(); + stack.push(-1); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push(i); + } else { + stack.pop(); + if (stack.isEmpty()) { + stack.push(i); + } else { + maxans = Math.max(maxans, i - stack.peek());//只peek不pop是关键 + } + } + } + return maxans; + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T20_Search.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T20_Search.java new file mode 100644 index 0000000..58db739 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T20_Search.java @@ -0,0 +1,135 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-05 13:45 + *@Description: + * TODO 力扣33题 搜索旋转排序数组: + * 整数数组 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) 的算法解决此问题。 + *@Version: 1.0 + */ +public class T20_Search { + + @Test + public void test() { + int[] nums = {4, 5, 6, 7, 0, 1, 2}; + int target = 3; + System.out.println(search(nums, target)); + } + + @Test + public void test1() { + int[] nums = {1, 3}; + int target = 3; + System.out.println(search(nums, target)); + } + + @Test + public void test2() { + int[] nums = {5,1, 3}; + int target = 1; + System.out.println(search(nums, target)); + } + + /** + * 思路: 难度自然在于 设计一个时间复杂度为O(log n)的算法 + * 时间复杂度logn则必然是考虑二分搜索 因为旋转后的数一定是小于第1个数 + * 1.先找具体旋转的位置(二分法) + * 2.再分为两个区具体去找 + * 速度击败100% 内存击败42.71% + * @param nums + * @param target + * @return + */ + public int search(int[] nums, int target) { + if (nums.length == 1) { + return nums[0] == target ? 0 : -1; + } + + int k = searchK(nums, 0, nums.length - 1, nums[0]); + if (target < nums[0] || k == 0) { + if (target < nums[k]) return -1; + return binarySearchTrue(nums, k, nums.length - 1, target); + } else { + return binarySearchTrue(nums, 0, k - 1, target); + } + + } + + public int searchK(int[] nums, int start, int end, int target) { + if (start == end) { + if (nums[start] < target) return start; + return 0; + } + int mid = start + ((end - start) >> 1); + if (nums[mid] < target) { + return searchK(nums, start, mid, target); + } else { + return searchK(nums, mid + 1, end, target); + } + } + + public int binarySearchTrue(int[] nums, int start, int end, int target) { + if (start > end) { + return -1; + } + int mid = start + ((end - start) >> 1); + if (nums[mid] < target) { + return binarySearchTrue(nums, mid + 1, end, target); + } else if (nums[mid] > target) { + return binarySearchTrue(nums, start, mid - 1, target); + } else { + return mid; + } + + } + + + /** + * 官方二分查找:将两次二分合并在一起;就是增加了一层判断:nums[0]和nums[mid]得到关系 + * 速度击败100% 内存击败37.71% + * @param nums + * @param target + * @return + */ + public int search1(int[] nums, int target) { + int n = nums.length; + if (n == 0) { + return -1; + } + if (n == 1) { + return nums[0] == target ? 0 : -1; + } + int l = 0, r = n - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[0] <= nums[mid]) { + if (nums[0] <= target && target < nums[mid]) { + r = mid - 1; + } else { + l = mid + 1; + } + } else { + if (nums[mid] < target && target <= nums[n - 1]) { + l = mid + 1; + } else { + r = mid - 1; + } + } + } + return -1; + } + +}