diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 50de30414..202662fd3 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -1 +1,7 @@ -学习笔记 \ No newline at end of file +学习笔记 +1、长短指针有利于做数组中数据的移动交换,而不需要再额外开辟空间来存储,可以将空间复杂度降为O(1) +2、对于进位的操作,可以使用取模的方式,判断进位后的数值是多少 +3、正确使用Hash表,可减少时间复杂度,以空间换时间 +4、对于有序数组或遍历时,看情况可使用左右夹逼的方式减少遍历次数 +5、程序算法需要的是找出重复子问题,然后再对重复的子问题做处理,性能的提升在原基础上再进行优化 +6、对于链接的交替操作,可以使用【currentNode = currentNode.next】将下一个节点往前移位,利于节省空间,可将空间复杂度降为O(1) diff --git "a/Week01/\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/Week01/\344\270\244\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 000000000..a3d9d841c --- /dev/null +++ "b/Week01/\344\270\244\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,34 @@ +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 +// +// +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + + +import java.util.HashMap; +import java.util.Map; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for(int i=0,len = nums.length;i= 0; i--) { + digits[i]++; + digits[i] = digits[i] % 10; + if (digits[i] != 0) return digits; + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" "b/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" new file mode 100644 index 000000000..29676e5c1 --- /dev/null +++ "b/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204.java" @@ -0,0 +1,38 @@ +//给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。 +// +// +// +// 说明: +// +// +// 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。 +// 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。 +// +// +// +// +// 示例: +// +// 输入: +//nums1 = [1,2,3,0,0,0], m = 3 +//nums2 = [2,5,6], n = 3 +// +//输出: [1,2,2,3,5,6] +// Related Topics 数组 双指针 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int p1 = m - 1; + int p2 = n - 1; + int p = m + n -1; + + while (p1 >= 0 && p2 >= 0){ + nums1[p--] = nums1[p1] < nums2[p2] ? nums2[p2--] : nums1[p1--]; + } + System.arraycopy(nums2, 0,nums1, 0,p2+1); + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" "b/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" new file mode 100644 index 000000000..936d30f7b --- /dev/null +++ "b/Week01/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250.java" @@ -0,0 +1,42 @@ +//将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 +// +// +// +// 示例: +// +// 输入:1->2->4, 1->3->4 +//输出:1->1->2->3->4->4 +// +// Related Topics 链表 + + +//leetcode submit region begin(Prohibit modification and deletion) +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode preHead = new ListNode(-1); + ListNode pre = preHead; + while(l1 != null && l2 != null){ + if(l1.val <= l2.val){ + pre.next = l1; + l1 = l1.next; + } else { + pre.next = l2; + l2 = l2.next; + } + pre = pre.next; + } + pre.next = l1==null?l2:l1; + return preHead.next; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\345\233\236\346\226\207\346\225\260.java" "b/Week01/\345\233\236\346\226\207\346\225\260.java" new file mode 100644 index 000000000..973f309f3 --- /dev/null +++ "b/Week01/\345\233\236\346\226\207\346\225\260.java" @@ -0,0 +1,44 @@ +//判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。 +// +// 示例 1: +// +// 输入: 121 +//输出: true +// +// +// 示例 2: +// +// 输入: -121 +//输出: false +//解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。 +// +// +// 示例 3: +// +// 输入: 10 +//输出: false +//解释: 从右向左读, 为 01 。因此它不是一个回文数。 +// +// +// 进阶: +// +// 你能不将整数转为字符串来解决这个问题吗? +// Related Topics 数学 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isPalindrome(int x) { + if (x < 0 || (x % 10 == 0 && x != 0)){ + return false; + } + + int revertNumber = 0; + while(x > revertNumber){ + revertNumber = revertNumber * 10 + x % 10; + x /= 10; + } + return x == revertNumber || x == revertNumber / 10; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\346\216\245\351\233\250\346\260\264.java" "b/Week01/\346\216\245\351\233\250\346\260\264.java" new file mode 100644 index 000000000..f6031779d --- /dev/null +++ "b/Week01/\346\216\245\351\233\250\346\260\264.java" @@ -0,0 +1,38 @@ +//给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 +// +// +// +// 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Mar +//cos 贡献此图。 +// +// 示例: +// +// 输入: [0,1,0,2,1,0,1,3,2,1,2,1] +//输出: 6 +// Related Topics 栈 数组 双指针 +// 👍 1496 👎 0 + + +import com.sun.source.tree.AssignmentTree; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int trap(int[] height) { + //双指针解法 + int sum = 0; + int max_left = 0; + int[] max_right = new int[height.length]; + for (int i = height.length - 2; i >= 0; i--) { + max_right[i] = Math.max(max_right[i + 1], height[i + 1]); + } + for (int i = 1; i < height.length - 1; i++) { + max_left = Math.max(max_left, height[i - 1]); + int min = Math.min(max_left, max_right[i]); + if (min > height[i]) { + sum = sum + (min - height[i]); + } + } + return sum; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\346\227\213\350\275\254\346\225\260\347\273\204.java" "b/Week01/\346\227\213\350\275\254\346\225\260\347\273\204.java" new file mode 100644 index 000000000..66a356558 --- /dev/null +++ "b/Week01/\346\227\213\350\275\254\346\225\260\347\273\204.java" @@ -0,0 +1,51 @@ +//给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。 +// +// 示例 1: +// +// 输入: [1,2,3,4,5,6,7] 和 k = 3 +//输出: [5,6,7,1,2,3,4] +//解释: +//向右旋转 1 步: [7,1,2,3,4,5,6] +//向右旋转 2 步: [6,7,1,2,3,4,5] +//向右旋转 3 步: [5,6,7,1,2,3,4] +// +// +// 示例 2: +// +// 输入: [-1,-100,3,99] 和 k = 2 +//输出: [3,99,-1,-100] +//解释: +//向右旋转 1 步: [99,-1,-100,3] +//向右旋转 2 步: [3,99,-1,-100] +// +// 说明: +// +// +// 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。 +// 要求使用空间复杂度为 O(1) 的 原地 算法。 +// +// Related Topics 数组 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public void rotate(int[] nums, int k) { +// 方法一:暴力解法 +// for (int i = 0; i < k; i++) { +// for(int j = nums.length-1;j>0;j--) { +// int t = nums[j]; +// nums[j] = nums[j-1]; +// nums[j-1] = t; +// } +// } +// 方法二:额外数组 + int[] aa = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + aa[(i + k) % nums.length] = nums[i]; + } + for (int i = 0; i < aa.length; i++) { + nums[i] = aa[i]; + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week01/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250.java" "b/Week01/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250.java" new file mode 100644 index 000000000..ffeeef07f --- /dev/null +++ "b/Week01/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250.java" @@ -0,0 +1,40 @@ +//给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, +//ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 +// +// 说明:你不能倾斜容器,且 n 的值至少为 2。 +// +// +// +// +// +// 图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。 +// +// +// +// 示例: +// +// 输入:[1,8,6,2,5,4,8,3,7] +//输出:49 +// Related Topics 数组 双指针 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int maxArea(int[] height) { + int max = 0; + // for(int i=0;i=其所有子节点值 +(1)索引为i的左孩子索引 (2*i+1) +(2)索引为i的右孩子索引 (2*i+2) +(3)索引为i的父节点索引 floor(i-1)/2) + +**树的遍历方式** +1、前序遍历:先根、后左、最后右 +2、中序遍历:先左、后根、最后右 +3、后序遍历:先左、后右、最后根 \ No newline at end of file diff --git "a/Week02/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/Week02/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 000000000..00afba8ec --- /dev/null +++ "b/Week02/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,60 @@ +//给定一个 N 叉树,返回其节点值的前序遍历。 +// +// 例如,给定一个 3叉树 : +// +// +// +// +// +// +// +// 返回其前序遍历: [1,3,5,6,2,4]。 +// +// +// +// 说明: 递归法很简单,你可以使用迭代法完成此题吗? Related Topics 树 + + +//leetcode submit region begin(Prohibit modification and deletion) +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } +}; +*/ + +import java.util.ArrayList; +import java.util.LinkedList; + +class Solution { + public List preorder(Node root) { + LinkedList stack = new LinkedList<>(); + LinkedList result = new LinkedList<>(); + if(root == null){ + return result; + } + stack.add(root); + while(!stack.isEmpty()){ + Node node = stack.pollLast(); + result.add(node.val); + Collections.reverse(node.children); + for(Node item : node.children){ + stack.add(item); + } + } + return result; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\344\270\221\346\225\260.java" "b/Week02/\344\270\221\346\225\260.java" new file mode 100644 index 000000000..855f900f7 --- /dev/null +++ "b/Week02/\344\270\221\346\225\260.java" @@ -0,0 +1,64 @@ +//编写一个程序判断给定的数是否为丑数。 +// +// 丑数就是只包含质因数 2, 3, 5 的正整数。 +// +// 示例 1: +// +// 输入: 6 +//输出: true +//解释: 6 = 2 × 3 +// +// 示例 2: +// +// 输入: 8 +//输出: true +//解释: 8 = 2 × 2 × 2 +// +// +// 示例 3: +// +// 输入: 14 +//输出: false +//解释: 14 不是丑数,因为它包含了另外一个质因数 7。 +// +// 说明: +// +// +// 1 是丑数。 +// 输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]。 +// +// Related Topics 数学 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isUgly(int num) { + if(num == 0) return false; + while(num != 1){ + if(num % 2 == 0){ + num /= 2; + continue; + } + if(num % 3 ==0){ + num /= 3; + continue; + } + if(num % 5 == 0){ + num /= 5; + continue; + } + return false; + } + return true; + + } + +// 解法二: +// private boolean isUgly(int num){ +// for (int i=2; i<6 && num>0; i++) +// while (num % i == 0) +// num /= i; +// return num == 1; +// } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/Week02/\344\270\244\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 000000000..d02ca01e5 --- /dev/null +++ "b/Week02/\344\270\244\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,34 @@ +//给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 +// +// 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 +// +// +// +// 示例: +// +// 给定 nums = [2, 7, 11, 15], target = 9 +// +//因为 nums[0] + nums[1] = 2 + 7 = 9 +//所以返回 [0, 1] +// +// Related Topics 数组 哈希表 + + +import java.util.HashMap; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int complement = target - nums[i]; + + if(map.containsKey(complement)) { + return new int[]{map.get(complement),i}; + } + map.put(nums[i],i); + } + return null; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" "b/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 000000000..6ed8569c6 --- /dev/null +++ "b/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\344\270\255\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,49 @@ +//给定一个二叉树,返回它的中序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,3,2] +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 哈希表 + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.LinkedList; +import java.util.Stack; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List inorderTraversal(TreeNode root) { + List result = new LinkedList<>(); + Stack stack = new Stack<>(); + TreeNode current = root; + while(current != null || !stack.isEmpty()){ + while(current != null){ + stack.push(current); + current = current.left; + } + current = stack.pop(); + result.add(current.val); + current = current.right; + } + return result; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 000000000..f4de73bf3 --- /dev/null +++ "b/Week02/\344\272\214\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,55 @@ +//给定一个二叉树,返回它的 前序 遍历。 +// +// 示例: +// +// 输入: [1,null,2,3] +// 1 +// \ +// 2 +// / +// 3 +// +//输出: [1,2,3] +// +// +// 进阶: 递归算法很简单,你可以通过迭代算法完成吗? +// Related Topics 栈 树 + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List preorderTraversal(TreeNode root) { + List result = new LinkedList<>(); + LinkedList stack = new LinkedList<>(); + if(root == null){ + return result; + } + stack.add(root); + while(!stack.isEmpty()){ + TreeNode node = stack.pollLast(); + result.add(node.val); + if(node.right != null){ + stack.add(node.right); + } + if(node.left != null){ + stack.add(node.left); + } + + } + return result; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" "b/Week02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" new file mode 100644 index 000000000..effb02f6e --- /dev/null +++ "b/Week02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" @@ -0,0 +1,54 @@ +//给定一个非空的整数数组,返回其中出现频率前 k 高的元素。 +// +// +// +// 示例 1: +// +// 输入: nums = [1,1,1,2,2,3], k = 2 +//输出: [1,2] +// +// +// 示例 2: +// +// 输入: nums = [1], k = 1 +//输出: [1] +// +// +// +// 提示: +// +// +// 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。 +// 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。 +// 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。 +// 你可以按任意顺序返回答案。 +// +// Related Topics 堆 哈希表 + + +import java.util.*; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int[] topKFrequent(int[] nums, int k) { + final Map countMap = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + countMap.put(nums[i],countMap.getOrDefault(nums[i],0) + 1); + } + PriorityQueue queue = new PriorityQueue<>((n1,n2)-> countMap.get(n1) - countMap.get(n2)); + for(int key : countMap.keySet()){ + queue.add(key); + if(queue.size() > k){ + queue.poll(); + } + } + int[] result = new int[k]; + int i = k; + while(!queue.isEmpty()){ + result[--k] = queue.poll(); + } + + return result; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.java" "b/Week02/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.java" new file mode 100644 index 000000000..24432afa2 --- /dev/null +++ "b/Week02/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.java" @@ -0,0 +1,38 @@ +//给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。 +// +// 示例: +// +// 输入: ["eat", "tea", "tan", "ate", "nat", "bat"] +//输出: +//[ +// ["ate","eat","tea"], +// ["nat","tan"], +// ["bat"] +//] +// +// 说明: +// +// +// 所有输入均为小写字母。 +// 不考虑答案输出的顺序。 +// +// Related Topics 哈希表 字符串 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + for(int i=0; i < strs.length; i++){ + char[] ca = strs[i].toCharArray(); + Arrays.sort(ca); + String s = String.valueOf(ca); + if(!map.containsKey(s)){ + map.put(s,new ArrayList<>()); + } + map.get(s).add(strs[i]); + } + return new ArrayList<>(map.values()); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week02/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" "b/Week02/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" new file mode 100644 index 000000000..195f49613 --- /dev/null +++ "b/Week02/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" @@ -0,0 +1,42 @@ +//给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 +// +// 示例 1: +// +// 输入: s = "anagram", t = "nagaram" +//输出: true +// +// +// 示例 2: +// +// 输入: s = "rat", t = "car" +//输出: false +// +// 说明: +//你可以假设字符串只包含小写字母。 +// +// 进阶: +//如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况? +// Related Topics 排序 哈希表 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean isAnagram(String s, String t) { + if(s.length() != t.length()){ + return false; + } + + int[] counter = new int[26]; + for (int i = 0; i < s.length(); i++) { + counter[s.charAt(i) - 'a']++; + } + for (int i = 0; i < t.length(); i++) { + counter[t.charAt(i) - 'a']--; + if(counter[t.charAt(i) - 'a'] < 0){ + return false; + } + } + return true; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 50de30414..9709fa207 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1 +1,20 @@ -学习笔记 \ No newline at end of file +### 递归 +递归其实也是一种循环,没有好坏之分,递归所说的性能问题,绝大多数都是程序上重复递归的问题 + +#####递归代码模板: +```java +public void recur( int level,int param){ + if(level > max_level){ + return ; + } + process(level,param); + recur(level : level + 1,newParam); +} +``` + +分治算法是一种处理问题的思想,递归是一种编程技巧,分治是通过递归实现的。 + +#### 递归可分为三个步骤: +分解:将原问题分解成多个子问题; +解决:递归求解子问题 +合并:合并子问题结果 diff --git "a/Week03/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" "b/Week03/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" new file mode 100644 index 000000000..24bdd5dd2 --- /dev/null +++ "b/Week03/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.java" @@ -0,0 +1,82 @@ +//给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 +// +// 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大( +//一个节点也可以是它自己的祖先)。” +// +// 例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4] +// +// +// +// +// +// 示例 1: +// +// 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +//输出: 3 +//解释: 节点 5 和节点 1 的最近公共祖先是节点 3。 +// +// +// 示例 2: +// +// 输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +//输出: 5 +//解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。 +// +// +// +// +// 说明: +// +// +// 所有节点的值都是唯一的。 +// p、q 为不同节点且均存在于给定的二叉树中。 +// +// Related Topics 树 + + +//leetcode submit region begin(Prohibit modification and deletion) + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + Map parent = new HashMap<>(); + Set visited = new HashSet<>(); + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + dfs(root); + while(p != null){ + visited.add(p.val); + p = parent.get(p.val); + } + while(q != null){ + if(visited.contains(q.val)){ + return q; + } + q = parent.get(q.val); + } + return null; + } + + private void dfs(TreeNode root){ + if(root.left != null){ + parent.put(root.left.val,root); + dfs(root.left); + } + if(root.right != null){ + parent.put(root.right.val,root); + dfs(root.right); + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week03/\345\205\250\346\216\222\345\210\227.java" "b/Week03/\345\205\250\346\216\222\345\210\227.java" new file mode 100644 index 000000000..fc57e2a93 --- /dev/null +++ "b/Week03/\345\205\250\346\216\222\345\210\227.java" @@ -0,0 +1,48 @@ +//给定一个 没有重复 数字的序列,返回其所有可能的全排列。 +// +// 示例: +// +// 输入: [1,2,3] +//输出: +//[ +// [1,2,3], +// [1,3,2], +// [2,1,3], +// [2,3,1], +// [3,1,2], +// [3,2,1] +//] +// Related Topics 回溯算法 + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public List> permute(int[] nums) { + List> result = new LinkedList<>(); + ArrayList output = new ArrayList<>(); + for (int num : nums){ + output.add(num); + } + int n = nums.length; + backtrack(n,output,result,0); + return result; + } + + public void backtrack(int n,ArrayList output,List> result,int first){ + if(first == n){ + result.add(new ArrayList(output)); + } + for (int i = first; i < n; i++) { + Collections.swap(output,first,i); + backtrack(n,output,result,first+1); + Collections.swap(output,first,i); + + } + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week03/\347\273\204\345\220\210.java" "b/Week03/\347\273\204\345\220\210.java" new file mode 100644 index 000000000..8fd2a07a1 --- /dev/null +++ "b/Week03/\347\273\204\345\220\210.java" @@ -0,0 +1,43 @@ +//给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 +// +// 示例: +// +// 输入: n = 4, k = 2 +//输出: +//[ +// [2,4], +// [3,4], +// [2,3], +// [1,2], +// [1,3], +// [1,4], +//] +// Related Topics 回溯算法 + + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public List> combine(int n, int k) { + LinkedList nums = new LinkedList<>(); + for (int i = 1; i < k + 1; i++) { + nums.add(i); + } + nums.add(n+1); + List> output = new ArrayList<>(); + int j = 0; + while(j < k){ + output.add(new LinkedList(nums.subList(0,k))); + j = 0; + while((j stack = new Stack<>(); + stack.add(treeNode); + while (!stack.isEmpty()) { + TreeNode cur = stack.pop(); + // 处理节点信息 + if(cur.right != null){ + stack.add(cur.right); + } + if(cur.left != null){ + stack.add(cur.left); + } + } +} +``` + +###深度优先搜索(递归) +```java +public void depthFirstSearch(TreeNode treeNode){ + if(treeNode == null){ + return; + } + // 处理节点信息 + for(TreeNode ch:treeNode.chs){ + depthFirstSearch(ch); + } +} +``` + +###广度优先搜索(非递归) +```java +public void broadFirstSearch(TreeNode treeNode){ + if(treeNode == null){ + return; + } + LinkedList queue = new LinkedList<>(); + queue.add(treeNode); + while (!queue.isEmpty()) { + TreeNode cur = queue.removeFirst(); + for(TreeNode ch:cur.chs){ + queue.add(ch); + } + } +} +``` + +###贪心算法: + +贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的解法。 + + +###二分查找 + +1、目标函数单调性(单调递增或单调递减) +2、存在上下界(bounded) +3、能够通过索引访问(index accessible) + +```java +int left = 0,right = 0, len = array.length - 1; +while( left <= right){ + mid = left + (right - left) /2; + if(array[mid] == target){ + return result; + }else if(array[mid] < target){ + left = mid + 1; + }else { + right = mid - 1; + } +} +return result; +``` + + diff --git "a/Week04/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/Week04/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 000000000..6ee2d456f --- /dev/null +++ "b/Week04/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,69 @@ +//给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 +// +// 岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。 +// +// 此外,你可以假设该网格的四条边均被水包围。 +// +// +// +// 示例 1: +// +// 输入: +//[ +//['1','1','1','1','0'], +//['1','1','0','1','0'], +//['1','1','0','0','0'], +//['0','0','0','0','0'] +//] +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//[ +//['1','1','0','0','0'], +//['1','1','0','0','0'], +//['0','0','1','0','0'], +//['0','0','0','1','1'] +//] +//输出: 3 +//解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int numIslands(char[][] grid) { + if(grid == null || grid.length == 0){ + return 0; + } + + int row = grid.length, col = grid[0].length; + int numIslands = 0; + for(int r = 0;r < row; ++r){ + for(int c = 0;c < col; ++c){ + if(grid[r][c] == '1'){ + ++numIslands; + dfs(grid,r,c); + } + } + } + return numIslands; + } + + private void dfs(char[][] grid,int r,int c){ + int row = grid.length, col = grid[0].length; + if(r < 0 || c < 0 || r >= row || c >= col || grid[r][c] == '0'){ + return; + } + + grid[r][c] = '0'; + dfs(grid,r - 1, c); + dfs(grid,r + 1, c); + dfs(grid,r, c - 1); + dfs(grid,r, c + 1); + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week04/[455]\345\210\206\345\217\221\351\245\274\345\271\262.java" "b/Week04/[455]\345\210\206\345\217\221\351\245\274\345\271\262.java" new file mode 100644 index 000000000..61314240c --- /dev/null +++ "b/Week04/[455]\345\210\206\345\217\221\351\245\274\345\271\262.java" @@ -0,0 +1,61 @@ +//假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干 +//的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满 +//足越多数量的孩子,并输出这个最大数值。 +// +// 注意: +// +// 你可以假设胃口值为正。 +//一个小朋友最多只能拥有一块饼干。 +// +// 示例 1: +// +// +//输入: [1,2,3], [1,1] +// +//输出: 1 +// +//解释: +//你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 +//虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 +//所以你应该输出1。 +// +// +// 示例 2: +// +// +//输入: [1,2], [1,2,3] +// +//输出: 2 +// +//解释: +//你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 +//你拥有的饼干数量和尺寸都足以让所有孩子满足。 +//所以你应该输出2. +// +// Related Topics 贪心算法 + + +import java.util.Arrays; + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int findContentChildren(int[] g, int[] s) { + //首先判断小孩或饼干为null时,没有能满足的 + if(g == null || s == null) return 0; + //先将小孩及饼干排序 + Arrays.sort(g); + Arrays.sort(s); + //使用双指针,优先满足胃口值比较小的小孩,因为胃口值小的小孩,容易满足 + //每满足一个小孩,小孩索引+1,饼干索引+1,如果小孩不满足,饼干索引+1 + //最后返回结果小孩索引即为满足的小孩数量 + int gindex = 0, sindex = 0; + while(gindex < g.length && sindex < s.length){ + if(g[gindex] <= s[sindex]){ + gindex++; + } + sindex++; + } + return gindex; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week04/[860]\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" "b/Week04/[860]\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" new file mode 100644 index 000000000..186739dd4 --- /dev/null +++ "b/Week04/[860]\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" @@ -0,0 +1,91 @@ +//在柠檬水摊上,每一杯柠檬水的售价为 5 美元。 +// +// 顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。 +// +// 每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。 +// +// 注意,一开始你手头没有任何零钱。 +// +// 如果你能给每位顾客正确找零,返回 true ,否则返回 false 。 +// +// 示例 1: +// +// 输入:[5,5,5,10,20] +//输出:true +//解释: +//前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 +//第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 +//第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。 +//由于所有客户都得到了正确的找零,所以我们输出 true。 +// +// +// 示例 2: +// +// 输入:[5,5,10] +//输出:true +// +// +// 示例 3: +// +// 输入:[10,10] +//输出:false +// +// +// 示例 4: +// +// 输入:[5,5,10,10,20] +//输出:false +//解释: +//前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。 +//对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。 +//对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。 +//由于不是每位顾客都得到了正确的找零,所以答案是 false。 +// +// +// +// +// 提示: +// +// +// 0 <= bills.length <= 10000 +// bills[i] 不是 5 就是 10 或是 20 +// +// Related Topics 贪心算法 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public boolean lemonadeChange(int[] bills) { + //f:5元数量;t:10元数量 + int f = 0, t = 0; + for(int bill : bills){ + if(bill == 5){ + //如果是收到5元,则5元的+1 + f++; + }else if(bill == 10){ + //如果收到的是10元,先判断是否有5元数量 + if(f == 0){ + //没有5元数量,则不能找零,返回false + return false; + } + //如果有5元数量,则5元数量-1,10元数量+1 + f--; + t++; + }else{ + //如果收到的是20元 + if(f > 0 && t > 0){ + //5元跟10元的数量都有,则各-1 + f--; + t--; + }else if(f >= 3){ + //5元的超过3张 + f -= 3; + }else { + return false; + } + } + } + return true; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 50de30414..d9771c554 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1 +1,12 @@ -学习笔记 \ No newline at end of file +#####动态规划: +###### 人肉递归效率低下,找到最近最简方法,拆解重复子问题,要有数学归纳思维 + +关键点: +######动态规划和递归、分治无本质区别,关键看有无最优子结构 + +#####分治、回溯、递归、动态规划的共性: +######重复子问题 + +##### 解题流程: +######1、代码模板 +######2、DP 三部曲:分解子问题;DP 状态数组;DP 方程 \ No newline at end of file diff --git "a/Week06/[221]\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" "b/Week06/[221]\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" new file mode 100644 index 000000000..51d819ff5 --- /dev/null +++ "b/Week06/[221]\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" @@ -0,0 +1,48 @@ +//在一个由 0 和 1 组成的二维矩阵内,找到只包含 1 的最大正方形,并返回其面积。 +// +// 示例: +// +// 输入: +// +//1 0 1 0 0 +//1 0 1 1 1 +//1 1 1 1 1 +//1 0 0 1 0 +// +//输出: 4 +// Related Topics 动态规划 +// 👍 486 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int maximalSquare(char[][] matrix) { + int n = matrix.length; + if(n == 0) return 0; + int m = matrix[0].length; + if(m == 0) return 0; + int[][] dp = new int[n][m]; + int result = 0; + //初始化base case + for(int i = 0 ;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; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week06/[647]\345\233\236\346\226\207\345\255\220\344\270\262.java" "b/Week06/[647]\345\233\236\346\226\207\345\255\220\344\270\262.java" new file mode 100644 index 000000000..e4bad3926 --- /dev/null +++ "b/Week06/[647]\345\233\236\346\226\207\345\255\220\344\270\262.java" @@ -0,0 +1,47 @@ +//给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 +// +// 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。 +// +// 示例 1: +// +// +//输入: "abc" +//输出: 3 +//解释: 三个回文子串: "a", "b", "c". +// +// +// 示例 2: +// +// +//输入: "aaa" +//输出: 6 +//说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa". +// +// +// 注意: +// +// +// 输入的字符串长度不会超过1000。 +// +// Related Topics 字符串 动态规划 +// 👍 282 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int countSubstrings(String s) { + char[] chars = s.toCharArray(); + int result = 0; + boolean[][] flag = new boolean[chars.length][chars.length]; + for (int j = 0; j < chars.length; j++) { + for (int i = j; i >= 0; i--) { // i j 的值设定保证了只使用了 flag[][]的上三角,没有出现重复 + if (chars[i] == chars[j] && (j - i < 2 || flag[i + 1][j - 1])) { + flag[i][j] = true; + result++; + } + } + } + return result; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week06/[64]\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/Week06/[64]\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 000000000..82a9efd2b --- /dev/null +++ "b/Week06/[64]\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,47 @@ +//给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 +// +// 说明:每次只能向下或者向右移动一步。 +// +// 示例: +// +// 输入: +//[ +//  [1,3,1], +// [1,5,1], +// [4,2,1] +//] +//输出: 7 +//解释: 因为路径 1→3→1→1→1 的总和最小。 +// +// Related Topics 数组 动态规划 +// 👍 524 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int minPathSum(int[][] grid) { + int m = grid.length; + int n = grid[0].length; + if (m == 0 || n == 0) { + return 0; + } + + int[][] dp = new int[m][n]; + dp[0][0] = grid[0][0]; + + for (int i = 1; i < m; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + for (int j = 1; j < n; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } + } + return dp[m - 1][n - 1]; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week07/NOTE.md b/Week07/NOTE.md index 50de30414..bc1edc7ea 100644 --- a/Week07/NOTE.md +++ b/Week07/NOTE.md @@ -1 +1,45 @@ -学习笔记 \ No newline at end of file +##### 字典树: +基本性质: + 1、节点本身不存完事单词 + 2、从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串 + 3、每个节点的所有子节点路径代表的字符都不相同 + + 核心思想: + trie树的核心思想是用空间换时间 + + ##### 并查集: + 适用场景: + 1、组团 + 2、配对 + + 基本操作: + 1、makeSet(s):建立一个新的并查集,其中包含s个单元素集合 + 2、unionSet(x,y):把元素x和元素y所在的集合合并,要求x和y所在的集合不相交,如果相交则不合并 + 3、find(x):找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。 + + ##### 剪枝: + 1.朴素搜索 + 2.优化方式:不重复、剪枝(生成括号问题) + 3.搜索方向:尝试优先(DFS)、广度优先(BFS) + 双向搜索、启发搜索 + +##### 回溯法: +通常用最简单的递归实现: +* 找到一个可能存在的正确答案 +* 在尝试了所有可能的分步方法后宣告该问题没有答案 +最坏的情况下,回溯法会导致一次复杂度为指数时间的计算 + +##### 启发式搜索: +启发式函数:一种告知搜索方向的方法。它提供了一种明智的方法来猜测哪个邻居节点会导向一个目标 + +##### 红黑树: +保证性能的关键: +* 保证二维维度 ——> 左中子树节点平衡 +* Balanced + +红黑树是一种近似平衡的二叉搜索树,它能够确保斜体一个节点的左右子树的高度差小于两倍 +* 每个节点要么是红色,要么是黑色 +* 根节点是黑色 +* 每个叶子节点(NIL节点、空节点)是黑色的 +* 不能有相邻接的两个红色的节点 +* 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 \ No newline at end of file diff --git "a/Week07/[127]\345\215\225\350\257\215\346\216\245\351\276\231.java" "b/Week07/[127]\345\215\225\350\257\215\346\216\245\351\276\231.java" new file mode 100644 index 000000000..974d3d3a3 --- /dev/null +++ "b/Week07/[127]\345\215\225\350\257\215\346\216\245\351\276\231.java" @@ -0,0 +1,110 @@ +//给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则: +// +// +// +// 每次转换只能改变一个字母。 +// 转换过程中的中间单词必须是字典中的单词。 +// +// +// 说明: +// +// +// 如果不存在这样的转换序列,返回 0。 +// 所有单词具有相同的长度。 +// 所有单词只由小写字母组成。 +// 字典中不存在重复的单词。 +// 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。 +// +// +// 示例 1: +// +// 输入: +//beginWord = "hit", +//endWord = "cog", +//wordList = ["hot","dot","dog","lot","log","cog"] +// +//输出: 5 +// +//解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", +// 返回它的长度 5。 +// +// +// 示例 2: +// +// 输入: +//beginWord = "hit" +//endWord = "cog" +//wordList = ["hot","dot","dog","lot","log"] +// +//输出: 0 +// +//解释: endWord "cog" 不在字典中,所以无法进行转换。 +// Related Topics 广度优先搜索 +// 👍 391 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (wordList == null || !wordList.contains(endWord)) return 0; + //将beginWord加入list + wordList.add(beginWord); + //begin找end + Queue queue1 = new LinkedList<>(); + Set visited1 = new HashSet<>(); + queue1.add(beginWord); + visited1.add(beginWord); + //end找begin + Queue queue2 = new LinkedList<>(); + Set visited2 = new HashSet<>(); + queue2.add(endWord); + visited2.add(endWord); + int depth = 0; + Set allWord = new HashSet<>(wordList); + while (!queue1.isEmpty() && !queue2.isEmpty()) { + //将节点更少的作为 1 + if (queue1.size() > queue2.size()) { + Queue temp = queue1; + queue1 = queue2; + queue2 = temp; + Set set = visited1; + visited1 = visited2; + visited2 = set; + } + //开始遍历 + depth++; + int size = queue1.size(); + while (size-- > 0) { + String poll = queue1.poll(); + char[] chars = poll.toCharArray(); + //遍历poll的每个字符 + for (int i = 0; i < chars.length; i++) { + //保存第i个字符,判断结束后需要还原 + char temp = chars[i]; + //将poll的每个字符逐个替换为其他字符 + for (char c = 'a'; c <= 'z'; c++) { + chars[i] = c; + //判断替换后的单词 + String newString = new String(chars); + if (visited1.contains(newString)) { + continue; + } + if (visited2.contains(newString)) { + return depth + 1; + } + //如果替换后的单词,存在字典中,则入队并标记访问 + if (allWord.contains(newString)) { + queue1.add(newString); + visited1.add(newString); + } + } + //还原第i个字符 + chars[i] = temp; + } + } + } + return 0; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week07/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/Week07/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 000000000..492fa9c66 --- /dev/null +++ "b/Week07/[200]\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,70 @@ +//给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。 +// +// 岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。 +// +// 此外,你可以假设该网格的四条边均被水包围。 +// +// +// +// 示例 1: +// +// 输入: +//[ +//['1','1','1','1','0'], +//['1','1','0','1','0'], +//['1','1','0','0','0'], +//['0','0','0','0','0'] +//] +//输出: 1 +// +// +// 示例 2: +// +// 输入: +//[ +//['1','1','0','0','0'], +//['1','1','0','0','0'], +//['0','0','1','0','0'], +//['0','0','0','1','1'] +//] +//输出: 3 +//解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。 +// +// Related Topics 深度优先搜索 广度优先搜索 并查集 +// 👍 675 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + void dfs (char[][] grid, int r, int c){ + int nr = grid.length; + int nc = grid[0].length; + if(r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0'){ + return; + } + grid[r][c] = '0'; +//这个地方是递归,在你寻找一个1的时候就开始深度遍历,直到遇见0,才结束 + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } + public int numIslands(char[][] grid) { + if(grid == null || grid.length == 0){ + return 0; + } + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + for(int r = 0; r < nr; ++r){ + for(int c = 0; c < nc; ++c){ + if (grid[r][c] == '1'){ + ++num_islands; + dfs(grid, r, c); + } + } + } + return num_islands; + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week07/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" "b/Week07/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" new file mode 100644 index 000000000..7de89c4bc --- /dev/null +++ "b/Week07/[22]\346\213\254\345\217\267\347\224\237\346\210\220.java" @@ -0,0 +1,44 @@ +//数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 +// +// +// +// 示例: +// +// 输入:n = 3 +//输出:[ +// "((()))", +// "(()())", +// "(())()", +// "()(())", +// "()()()" +// ] +// +// Related Topics 字符串 回溯算法 +// 👍 1193 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public List generateParenthesis(int n) { + // 参数介绍:n是 剩余左括号,剩余右括号 + List res = new ArrayList<>(); + _generatebrackets(res,"" , n , n); + return res; + } + + private void _generatebrackets(List res, String s, int left , int right) { + //终止条件 + if (left == 0 && right ==0) { + res.add(s); + return; + } + if ( left > 0) + _generatebrackets(res,s+"(" , left -1 , right); + + // 匹配右边括号的时候总是比左边括号数多 + if( right > 0 && right > left) + _generatebrackets(res,s+")" , left , right - 1); + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week08/NOTE.md b/Week08/NOTE.md index 50de30414..c9fc0f78c 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -1 +1,45 @@ -学习笔记 \ No newline at end of file +#####位运算: +* 将X最右边的n位清零: x & (~0<>n)&1 +* 获取x的第n位幂值:x & (1< (x&1) == 1 +x % 2 == 0 --> (x&1) == 0 + +* x >> 1 --> x / 2 即: x = x / 2; --> x = x >> 1; +* mid = (left + right) / 2 --> mid = (left + right) >> 1 +* x = x&(x - 1) 清零最低位置的1 +* x & -x ==> 得到最低位的1 +* x & ~x == 0 + +#####布隆过滤器和LRU缓存 +* 排序算法 +* 初级排序 +* 选择排序 (Selection Sort):每次找最小值,然后放到待排序数组的起始位置 + +* 插入排序 (Insertion Sort):从前到后逐步构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置插入 + +* 冒泡排序 (Bubble Sort):嵌套循环,每次查看相邻的元素如果逆序,则交换 + +* 高级排序-O(NlogN) +* 快速排序 (quick Sort):数组取标杆pivot,将小元素放pivot左边,大元素放右侧,然后依次对右边和右边的子数组继续排序,已达到整个序列有序 + +* 归并排序(Merge Sort) 分治: + * 把长度为你n的输入序列分成两个长度为n/2de子序列 + * 对这两个子序列采用归并排序 + * 将两个排序好的子序列合并成一个最终的排序序列 +* 堆排序(Heep Sort) :堆插入OlngN 取最大小值O(1)_ + * 数组元素依次建立小顶堆 + * 依次去对顶元素,并删除 + * 归并和快排具有相似性,但是步骤顺序相反 +* 归并: 先排序左右子数组,然后合并两个有序子数组 +* 快排:先调配出左右子数组,然后对于左右子数组进行排序 +* 特殊排序 +* 计算排序 +* 桶排序 +* 基数排序 \ No newline at end of file diff --git "a/Week08/[146]LRU\347\274\223\345\255\230\346\234\272\345\210\266.java" "b/Week08/[146]LRU\347\274\223\345\255\230\346\234\272\345\210\266.java" new file mode 100644 index 000000000..582b56a33 --- /dev/null +++ "b/Week08/[146]LRU\347\274\223\345\255\230\346\234\272\345\210\266.java" @@ -0,0 +1,217 @@ +//运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 +// +// 获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。 +//写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在 +//写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 +// +// +// +// 进阶: +// +// 你是否可以在 O(1) 时间复杂度内完成这两种操作? +// +// +// +// 示例: +// +// LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); +// +//cache.put(1, 1); +//cache.put(2, 2); +//cache.get(1); // 返回 1 +//cache.put(3, 3); // 该操作会使得关键字 2 作废 +//cache.get(2); // 返回 -1 (未找到) +//cache.put(4, 4); // 该操作会使得关键字 1 作废 +//cache.get(1); // 返回 -1 (未找到) +//cache.get(3); // 返回 3 +//cache.get(4); // 返回 4 +// +// Related Topics 设计 +// 👍 786 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class LRUCache { + + static class Node{ + private int key; + private int value; + + private Node pre; + private Node next; + + // 用来记录插入顺序 + private Node preI; + private Node nextI; + + public Node(int key, int value) { + this.key = key; + this.value = value; + this.pre = this.next = null; + this.preI = this.nextI = null; + } + } + + private final int capacity; + private final int length; + private int size; + + private Node [] table; + private Node head; + private Node tail; + + static final int tableSizeFor(int cap) { + int n = cap - 1; + n |= n >>> 1; + n |= n >>> 2; + n |= n >>> 4; + n |= n >>> 8; + n |= n >>> 16; + return n + 1; + } + + // hashmap里的 hash 方法, 原对象的hashCode的高16位 与 低16位异或 + static final int hash(Object key) { + int h = 0; + return (key == null) ? 0 : ((h = key.hashCode()) ^ h >>> 16); + } + + public LRUCache(int capacity) { + this.capacity = capacity; + this.size = 0; + this.head = this.tail = null; + // 数组长度赋值为两倍长度,因为没有写扩容方法,有个样例结点太多,导致冲突太多 + this.length = tableSizeFor(2 * capacity); + table = new Node[length]; + } + + public int get(int key) { + + int h = hash(key); + Node p = table[h & length - 1]; + while (p != null) { + if (p.key == key) break; // 在 hashcode 相等的情况下, 比较键是否相等(equals) + p = p.next; + } + if (p == null) return -1; + adjust(p); // 在hash表里面的位置不变,但是在先进先出的链表里的位置改变 + return p.value; + } + + public void put(int key, int value) { + + int h = hash(key); + int index = h & length - 1; + Node p = table[index]; + + while (p != null) { + if (p.key == key) break; + p = p.next; + } + if (p != null) { + p.value = value; + adjust(p); + return; + } + if (size >= capacity) removeHead(); // 不仅仅在链表中删除,也要在hash表中删除。 + + Node t = new Node(key, value); + + // 在 哈希表 中添加 + if(table[index] == null) { + table[index] = t; + } + else { + Node e; + p = table[index]; + while ((e = p.next) != null) p = p.next; + t.pre = p; + p.next = t; + } + + // 在 链表 中添加 + if (tail == null) { + head = tail = t; + } + else { + t.preI = tail; + tail = tail.nextI = t; + } + ++size; + } + + // 调整 链表 的次序 + public void adjust(Node p) { + if (p == tail) return; + else if (p == head) { + Node t = head; + head = head.nextI; + head.preI = null; + + t.preI = tail; + t.nextI = null; + tail = tail.nextI = t; + + return; + } + else { + + p.preI.nextI = p.nextI; + p.nextI.preI = p.preI; + + p.preI = tail; + p.nextI = null; + tail = tail.nextI = p; + return; + } + } + + public void removeHead() { + + if (head == null) return; + + int h = hash(head.key); + int index = (h & length - 1); + + // 在 哈希表中删除 + if (head.pre == null) { + table[index] = head.next; + head.pre = head.next = head; // 指向自己,垃圾回收 + } + else if (head.next == null) { + head.pre.next = null; + head.pre = head.next = head; + } + else { + head.pre.next = head.next; + head.next.pre = head.pre; + + head.pre= head.next = head; + } + + // 在链表中删除 + if (head == tail) { + + Node t = head; + head = tail = null; + + t.preI = t.nextI = t; + } + else { + Node t = head; + head = head.nextI; + head.preI = null; + t.preI = t.nextI = null; + + } + --size; + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week08/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" "b/Week08/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" new file mode 100644 index 000000000..3399aa0ea --- /dev/null +++ "b/Week08/[191]\344\275\2151\347\232\204\344\270\252\346\225\260.java" @@ -0,0 +1,56 @@ +//编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。 +// +// +// +// 示例 1: +// +// 输入:00000000000000000000000000001011 +//输出:3 +//解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。 +// +// +// 示例 2: +// +// 输入:00000000000000000000000010000000 +//输出:1 +//解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。 +// +// +// 示例 3: +// +// 输入:11111111111111111111111111111101 +//输出:31 +//解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。 +// +// +// +// 提示: +// +// +// 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的 +//还是无符号的,其内部的二进制表示形式都是相同的。 +// 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。 +// +// +// +// +// 进阶: +//如果多次调用这个函数,你将如何优化你的算法? +// Related Topics 位运算 +// 👍 191 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int sum = 0; + while (n != 0) { + sum++; + n &= (n - 1); + } + return sum; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git a/Week09/NOTE.md b/Week09/NOTE.md index 50de30414..4dc64fa45 100644 --- a/Week09/NOTE.md +++ b/Week09/NOTE.md @@ -1 +1,25 @@ -学习笔记 \ No newline at end of file +学习笔记 + +####高级动态规划 +* 动态规划:分治 + 最优子结构 +* 顺推形式:动态递推 +* 不同点:动态规划有最优子结构,可以在中途淘汰次优解 + +####高级动态规划复杂度的来源 +状态拥有更多维度(二维、三维、或者更多、甚至需要压缩) +状态方程更加复杂 +字符串匹配算法 +暴力法,时间复杂度 O(m*n) + +#### Rabin-Karp 算法 +* 假设子串的长度为 M (pat),目标字符串的长度为 N (txt) +* 计算子串的 hash 值 hash_pat +* 计算目标字符串txt中每个长度为 M 的子串的 hash 值(共需要计算 N-M+1 次) +* 比较 hash 值: 如果 hash 值不同,字符串必然不匹配; 如果 hash 值相同, 还需要使用朴素算法再次判断 +#### KMP 算法 +此算法通过运用对这个词在不匹配时本身就包含足够的信息来确定下一个匹配将在哪里开始的发现,从而避免重新检查先前匹配的字符。 + +假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置 +* 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j]),都令 i++,j++,继续匹配下一个字符; +* 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P 相对于文本串 S 向右移动了 j - next [j] 位 +* 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的 next 值,即移动的实际位数为:j - next[j],且此值大于等于1 \ No newline at end of file diff --git "a/Week09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\2622.java" "b/Week09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\2622.java" new file mode 100644 index 000000000..477bc3165 --- /dev/null +++ "b/Week09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\2622.java" @@ -0,0 +1,50 @@ +//给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转。 +// +// +// 如果剩余字符少于 k 个,则将剩余字符全部反转。 +// 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。 +// +// +// +// +// 示例: +// +// 输入: s = "abcdefg", k = 2 +//输出: "bacdfeg" +// +// +// +// +// 提示: +// +// +// 该字符串只包含小写英文字母。 +// 给定字符串的长度和 k 在 [1, 10000] 范围内。 +// +// Related Topics 字符串 +// 👍 84 👎 0 + + +//leetcode submit region begin(Prohibit modification and deletion) +class Solution { + public String reverseStr(String s, int k) { + char[] ch = s.toCharArray(); + int n = ch.length; + // 每2k个元素为一组进行反转 + for (int i = 0; i < n; i += 2 * k){ + int left = i; + int right = (i + k - 1 < n) ? i + k -1 : n - 1; //判断下标是否越界 + while (left <= right){ + char temp = ch[left]; + ch[left] = ch[right]; + ch[right] = temp; + left++; + right--; + } + } + String str = new String(ch); + return str; + + } +} +//leetcode submit region end(Prohibit modification and deletion) diff --git "a/Week09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" "b/Week09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" new file mode 100644 index 000000000..c128be027 --- /dev/null +++ "b/Week09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" @@ -0,0 +1,20 @@ +public class Solution { + public int longestValidParentheses(String s) { + int maxans = 0; + Stack stack = new Stack<>(); + stack.push(-1); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + stack.push(i); + } else { + stack.pop(); + if (stack.empty()) { + stack.push(i); + } else { + maxans = Math.max(maxans, i - stack.peek()); + } + } + } + return maxans; + } +} \ No newline at end of file diff --git "a/Week09/\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" "b/Week09/\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" new file mode 100644 index 000000000..57370b077 --- /dev/null +++ "b/Week09/\351\200\232\351\205\215\347\254\246\345\214\271\351\205\215.java" @@ -0,0 +1,52 @@ +class Solution { + public boolean isMatch(String s, String p) { + int sRight = s.length(), pRight = p.length(); + while (sRight > 0 && pRight > 0 && p.charAt(pRight - 1) != '*') { + if (charMatch(s.charAt(sRight - 1), p.charAt(pRight - 1))) { + --sRight; + --pRight; + } else { + return false; + } + } + + if (pRight == 0) { + return sRight == 0; + } + + int sIndex = 0, pIndex = 0; + int sRecord = -1, pRecord = -1; + + while (sIndex < sRight && pIndex < pRight) { + if (p.charAt(pIndex) == '*') { + ++pIndex; + sRecord = sIndex; + pRecord = pIndex; + } else if (charMatch(s.charAt(sIndex), p.charAt(pIndex))) { + ++sIndex; + ++pIndex; + } else if (sRecord != -1 && sRecord + 1 < sRight) { + ++sRecord; + sIndex = sRecord; + pIndex = pRecord; + } else { + return false; + } + } + + return allStars(p, pIndex, pRight); + } + + public boolean allStars(String str, int left, int right) { + for (int i = left; i < right; ++i) { + if (str.charAt(i) != '*') { + return false; + } + } + return true; + } + + public boolean charMatch(char u, char v) { + return u == v || v == '?'; + } +} diff --git a/Week10/NOTE.md b/Week10/NOTE.md new file mode 100644 index 000000000..a9b726dfc --- /dev/null +++ b/Week10/NOTE.md @@ -0,0 +1,18 @@ +通过十周的学习,整个算法相比学习之前提升了一个档次,明白了很多之前不太了解的数据结构以及解法,如树的层序遍历、贪心算法、动态规划、深度优先和广度优先等。 +覃超老师的讲解也特别的详细,很多地方不明白的,经过老师的讲解也是一点就通,相比自学时候要好很多,另外覃超老师所说的五毒神掌,通过刷题,刷五遍,让自己对每一题的印象更加深刻 + +#### 解题方法论: +* 五毒神掌 +* 自顶向下 +* 熟记解题模板 +* 不要死磕,看中英文精选评论,写简洁代码 + +#### 算法的思想: +* 分治 +* 贪心 +* 回溯 +* 动态规划 + +另外助教老师每周也会检查我们的作业及总结,然后挑选出精华来给我们学习,还有分享成果,每周都会有不同的收获,有什么问题老师也会耐心的讲解,为我们前进的道路铺开了路 + +每天都让自己进步一点点,十周的学习,完成算法的基本入门,后续还需要自己不断刷题加强对算法的理解,最后感谢老师与助教以及班主任在这段时间的帮助