diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..59e5a926 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,6 @@ -学习笔记 \ No newline at end of file +算法训练营第一周将要在忙碌的日常工作和刷题中过去了,加入算法训练营的这一周感触颇多;算法从来都是自己的短板,看过书籍、读过博客长期下来的感觉就是算法内容-繁琐枯燥、刷题-无聊、遇到问题无人解答,最终也没坚持学下去。在算法训练营里我看到了为攻克算法难关而努力的同学,耐心解答问题的助教和讲师超哥,积极上进的学习氛围是我喜欢的同时也让我拥有了和大牛们学习的机会,让我相信再未来的几个月一定可以把算法这个难题拿下。 +算法训练营的第一周内容不算难,毕竟之前有一些数组、链表、队列和栈知识的基础。收获最多的是超哥的学习算法的方法,学算法是为了更高效的解决问题,之前的学习方式只注重了理论而忽略了实践,这一周通过使用了超哥的练习方法使自己对算法有了更深的认识,也使自己的思维神经得到了锻炼。 +解题思路收获主要有两个,一个是双指针定位数组左右边界向内收敛方式解决问题类型如:三数之和、盛最多水的容器,另外一个是单调栈的应用解决问题如:柱形图中绘制最大面积、接雨水问题。 +第一周的收获虽然有很多,但是还是有些不满意的地方:解决一个问题的最直接思路依然局限于最“暴力”的方式,稍有难度的问题如果没有引导无法确定用哪种方式解决好,比如接雨水问题,即便是学会了柱状图中绘制最大面积单调栈的解题方式,再遇到接雨水问题如果没有指引使用栈解决,自己似乎想不到使用栈来解决这个问题。可能是解决算法问题的经验太少吧,希望后续可以有改善。 + +第一周的心得体会就这些了,解决算法问题就像解密一个puzzle 过程可能令人抓狂,但在解出来的一瞬间的感觉就很棒。希望自己再后续的学习周能够收获更多的知识,和同学们一起攻克算法。 \ No newline at end of file diff --git "a/Week_01/\344\275\234\344\270\232" "b/Week_01/\344\275\234\344\270\232" new file mode 100644 index 00000000..52218f5f --- /dev/null +++ "b/Week_01/\344\275\234\344\270\232" @@ -0,0 +1,113 @@ +删除排序数组中的重复项 +class Solution { + public int removeDuplicates(int[] nums) { + if(nums.length == 0) + return 0; + int index = 0; + for(int i = 0;i < nums.length; i++){ + if(nums[i] != nums[index]){ + index ++; + nums[index] = nums[i]; + + + } + } + return index + 1; + } +} + +合并两个有序链表 +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + ListNode newNode = new ListNode(0); + ListNode cur = newNode; + while (l1 != null && l2 != null) { + if (l1.val > l2.val) { + cur.next = l2; + l2 = l2.next; + } else { + cur.next = l1; + l1 = l1.next; + } + cur = cur.next; + } + cur.next = l1 == null ? l2 : l1; + return newNode.next; + } +} + + 合并两个有序数组 + class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int i = m - 1, j = n - 1, k = m + n - 1; + while (i >= 0 && j >= 0) { + nums1[k--] = nums1[i] > nums2[j] ? nums1[i--] : nums2[j--]; + } + System.arraycopy(nums2, 0, nums1, 0, j + 1); + } +} +两数之和 +class Solution { + public int[] twoSum(int[] nums, int target) { + HashMap cache = new HashMap(); + for (int i = 0; i < nums.length; ++i) { + int cur = target - nums[i]; + if (cache.containsKey(cur)) { + return new int[]{cache.get(cur), i}; + } + cache.put(nums[i], i); + } + return null; + } +} + +移动零 +class Solution { + public void moveZeroes(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; ++i) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (i != j) { + nums[i] = 0; + } + j++; + } + } + } +} + +加一 +class Solution { + public void moveZeroes(int[] nums) { + int j = 0; + for (int i = 0; i < nums.length; ++i) { + if (nums[i] != 0) { + nums[j] = nums[i]; + if (i != j) { + nums[i] = 0; + } + j++; + } + } + } +} + +接雨水 +class Solution { + public int trap(int[] height) { + int ans = 0, current = 0; + Deque stack = new LinkedList<>(); + while (current < height.length) { + while (!stack.isEmpty() && height[current] > height[stack.peek()]) { + int top = stack.pop(); + if (stack.isEmpty()) break; + int length = current - stack.peek() - 1; + int h = Math.min(height[current], height[stack.peek()]) - height[top]; + ans += length * h; + } + stack.push(current++); + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_02/N\345\217\211\346\240\221\347\232\204\345\211\215\345\272\217\351\201\215\345\216\206.java" "b/Week_02/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 00000000..3322a62c --- /dev/null +++ "b/Week_02/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,23 @@ +/** + * 前序遍历模板 当前节点->子节点(从左到右)子树各节点规则一致 + * 迭代方式,使用队列处理 + * 由于处理顺序为从左到右,因此每层元素需要反转后加入队列然后取出队尾元素(取出队尾元素操作相当于处理了子树的根节点) + * + * + **/ +class Solution { + public List preorder(Node root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + if (root == null) + return ans; + deque.add(root); + while (!deque.isEmpty()) { + Node node = deque.pollLast(); + ans.add(node.val); + Collections.reverse(node.children); + deque.addAll(node.children); + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_02/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" "b/Week_02/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" new file mode 100644 index 00000000..f53c8dd9 --- /dev/null +++ "b/Week_02/N\345\217\211\346\240\221\347\232\204\345\261\202\345\272\217\351\201\215\345\216\206.java" @@ -0,0 +1,26 @@ +/** + * + * 迭代,使用队列存储每层节点 + * + * + * + **/ +class Solution { + public List> levelOrder(Node root) { + List> ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + if (root == null) return ans; + deque.add(root); + while (!deque.isEmpty()) { + List level = new ArrayList<>(); + int size = deque.size(); + for (int i = 0; i < size; ++i) { + Node node = deque.poll(); + level.add(node.val); + deque.addAll(node.children); + } + ans.add(level); + } + return ans; + } +} \ No newline at end of file diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..de9e7acc 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,39 @@ -学习笔记 \ No newline at end of file + +算法训练营第二周结束了,这周收获了很多新知识,主要有二叉树的遍历方法以及堆的应用。 + +## 二叉树递归遍历模板: + 前序遍历 + preorder(Node node) { + print(node.val) + preorder(node.left) + preorder(node.right) + } + 中序遍历 + inorder(Node node) { + inorder(node.left) + print(node.val) + inorder(node.right) + } + 后序遍历 + postorder(Node node) { + postorder(node.left) + postorder(node.right) + print(node.val) + } + +## 堆的总结: + + 定义:堆是一种抽象的数据结构,它能够以很快的速度查询出最大元素或者最小元素。根据构建类型不同分为大顶堆和小顶堆。 + 实现:堆的实现有多种,如:二叉堆、斐波那契堆、2-3堆等 + 二叉堆: + 二叉堆是满二叉树,实现简单性能一般,查询最大最小元素时间复杂度O(1),添加、删除时间复杂度O(logn)。 + 二叉堆数据以数组方式存储,左右子节点位置为父节点位置*2 +1 和+2 左子节点pidx*2+1,右子节点pidx*2+2,某节点父节点位置 (idx-1)/2。 + 二叉堆插入元素,先插入最后位置然后依次与父节点比较如果大于父节点则交换位置直到找到合适位置。 + 二叉堆删除元素,将末节点放到删除位置,然后依次在左右子节点找最大元素交换位置直到找到合适位置 + +对于树的遍历除了递归方式遍历,重点练习了二叉树使用迭代方式的遍历,包括:二叉树前、中、后、层序遍历,N叉树的前、后、层序遍历。感觉树的迭代方式遍历不是很好理解,练习了很多遍也是刚刚熟练些,不过对于树的结构以及遍历方式有了更深的认识。 +堆的题目主要练习了topK问题,不算太难。 +散列表平时使用的较多,理解起来没有难度。 +图的定义邻接矩阵与邻接表还算简单,遍历算法DFS、BFS练习了一下,至于图的题目 并没有花太多时间练习, 后面补回来吧。。。 + +因为是国庆周,时间还算充裕,复习了上周的一些中、困难的题目,另外找了几个链表的题目做了练习;现在leetcode做题作为了“日常”任务,只为享受解题的快乐而不是为了完成任务;继续努力吧! diff --git "a/Week_02/\344\270\221\346\225\260.java" "b/Week_02/\344\270\221\346\225\260.java" new file mode 100644 index 00000000..c0c2d28a --- /dev/null +++ "b/Week_02/\344\270\221\346\225\260.java" @@ -0,0 +1,22 @@ +/** + * + * 丑数 * 丑数 依然是一个丑数 + * 使用数组存储因子数,构建小顶堆存储丑数 将堆顶元素依次与因子数相乘后再加入堆直到找到第N个数 + * + * + **/ +class Solution { + int[] uglyNumbers = {2,3,5}; + public int nthUglyNumber(int n) { + PriorityQueue heap = new PriorityQueue<>(); + heap.offer(1L); + int count = 0; + while (true) { + long num = heap.poll(); + if (++count == n) return (int) num; + for (int uglyNumber : uglyNumbers) { + if (!heap.contains(uglyNumber * num)) heap.offer(uglyNumber * num); + } + } + } +} \ No newline at end of file diff --git "a/Week_02/\344\270\244\346\225\260\344\271\213\345\222\214.java" "b/Week_02/\344\270\244\346\225\260\344\271\213\345\222\214.java" new file mode 100644 index 00000000..d403f7f9 --- /dev/null +++ "b/Week_02/\344\270\244\346\225\260\344\271\213\345\222\214.java" @@ -0,0 +1,18 @@ +/** + * + * 遍历数组使用使用HashMap记录之前出现的数字 + * 遍历过程中使用将目标值与当前值取差值,并在之前的缓存记录中查找是否存在差值 + * + * + **/ +class Solution { + public int[] twoSum(int[] nums, int target) { + Map cache = new HashMap<>(); + for (int i = 0; i < nums.length; ++i) { + int cur = target - nums[i]; + if (cache.containsKey(cur)) return new int[]{cache.get(cur), i}; + cache.put(nums[i], i); + } + return new int[0]; + } +} \ No newline at end of file diff --git "a/Week_02/\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/Week_02/\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 00000000..612c0ce6 --- /dev/null +++ "b/Week_02/\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,22 @@ +/** + * + * 中序遍历模板 左-中-右 + * 迭代方式,处理顺序为左中右 因此将所有左节点加入栈,直到左节点为空,弹出过程中如果不处理右子树则顺序为左中左中,因此在处理完中节点时再迭代右子树即可 + * + **/ +class Solution { + public List inorderTraversal(TreeNode root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + while (root != null || !deque.isEmpty()) { + while (root != null) { + deque.push(root); + root = root.left; + } + root = deque.pop(); + ans.add(root.val); + root = root.right; + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_02/\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/Week_02/\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 00000000..adfe4cfb --- /dev/null +++ "b/Week_02/\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,22 @@ +/** + * + * 前序模板 中左右 + * 处理逻辑和中序一致,在加入栈时处理左节点即为中左中左,依次弹出再处理右子树 + * + * + **/ +class Solution { + public List preorderTraversal(TreeNode root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + while (root != null || !deque.isEmpty()) { + while (root != null) { + ans.add(root.val); + deque.push(root); + root = root.left; + } + root = deque.pop().right; + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_02/\344\275\234\344\270\232" "b/Week_02/\344\275\234\344\270\232" new file mode 100644 index 00000000..491a12bc --- /dev/null +++ "b/Week_02/\344\275\234\344\270\232" @@ -0,0 +1,156 @@ +有效的字母异位词 +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + char[] cache = new char[26]; + for (int i = 0; i < s.length(); ++i) { + cache[s.charAt(i) - 'a']++; + cache[t.charAt(i) - 'a']--; + } + return String.valueOf(cache).trim().isBlank(); + } +} +两数之和 +class Solution { + public int[] twoSum(int[] nums, int target) { + Map cache = new HashMap<>(); + for (int i = 0; i < nums.length; ++i) { + int cur = target - nums[i]; + if (cache.containsKey(cur)) return new int[]{cache.get(cur), i}; + cache.put(nums[i], i); + } + return new int[0]; + } +} +N叉树的前序遍历 +class Solution { + public List preorder(Node root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + if (root == null) + return ans; + deque.add(root); + while (!deque.isEmpty()) { + Node node = deque.pollLast(); + ans.add(node.val); + Collections.reverse(node.children); + deque.addAll(node.children); + } + return ans; + } +} +字母异位词分组 +class Solution { + public List> groupAnagrams(String[] strs) { + HashMap> ans = new HashMap<>(); + char[] cache = new char[26]; + for (String str : strs) { + Arrays.fill(cache, 'a'); + for (char c : str.toCharArray()) cache[c - 'a']++; + String key = String.valueOf(cache); + if (!ans.containsKey(key)) ans.put(key, new ArrayList<>()); + ans.get(key).add(str); + } + return new ArrayList<>(ans.values()); + } +} +二叉树的中序遍历 +class Solution { + public List inorderTraversal(TreeNode root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + while (root != null || !deque.isEmpty()) { + while (root != null) { + deque.push(root); + root = root.left; + } + root = deque.pop(); + ans.add(root.val); + root = root.right; + } + return ans; + } +} +二叉树的前序遍历 +class Solution { + public List preorderTraversal(TreeNode root) { + List ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + while (root != null || !deque.isEmpty()) { + while (root != null) { + ans.add(root.val); + deque.push(root); + root = root.left; + } + root = deque.pop().right; + } + return ans; + } +} +N叉树的层序遍历 +class Solution { + public List> levelOrder(Node root) { + List> ans = new ArrayList<>(); + Deque deque = new LinkedList<>(); + if (root == null) return ans; + deque.add(root); + while (!deque.isEmpty()) { + List level = new ArrayList<>(); + int size = deque.size(); + for (int i = 0; i < size; ++i) { + Node node = deque.poll(); + level.add(node.val); + deque.addAll(node.children); + } + ans.add(level); + } + return ans; + } +} +丑数 +class Solution { + int[] uglyNumbers = {2,3,5}; + public int nthUglyNumber(int n) { + PriorityQueue heap = new PriorityQueue<>(); + heap.offer(1L); + int count = 0; + while (true) { + long num = heap.poll(); + if (++count == n) return (int) num; + for (int uglyNumber : uglyNumbers) { + if (!heap.contains(uglyNumber * num)) heap.offer(uglyNumber * num); + } + } + } +} +前K个高频元素 +class Solution { + public int[] topKFrequent(int[] nums, int k) { + HashMap eleTimesMap = new HashMap<>(); + for(int num : nums){ + eleTimesMap.put(num, eleTimesMap.getOrDefault(num, 0) + 1); + } + PriorityQueue pq = new PriorityQueue(new Comparator(){ + public int compare(int[] a, int[] b){ + return a[1] - b[1]; + } + }); + for(Map.Entry eleTimesEntry : eleTimesMap.entrySet()){ + int ele = eleTimesEntry.getKey(), times = eleTimesEntry.getValue(); + if(pq.size() == k){ + if(pq.peek()[1] < times){ + pq.poll(); + pq.offer(new int[]{ele, times}); + } + }else{ + pq.offer(new int[]{ele, times}); + } + } + + int[] result = new int[k]; + for(int i = 0;i < k;i ++){ + result[i] = pq.poll()[0]; + } + return result; + } +} \ No newline at end of file diff --git "a/Week_02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" "b/Week_02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" new file mode 100644 index 00000000..3b3e3c2c --- /dev/null +++ "b/Week_02/\345\211\215K\344\270\252\351\253\230\351\242\221\345\205\203\347\264\240.java" @@ -0,0 +1,37 @@ +/** + * + * 使用HashMap统计每个数字出现的次数 + * 构建小顶堆 将数字加入堆,如果超出K则移除堆顶元素 最后剩余的数字即为高频元素 + * + * + **/ +class Solution { + public int[] topKFrequent(int[] nums, int k) { + HashMap eleTimesMap = new HashMap<>(); + for(int num : nums){ + eleTimesMap.put(num, eleTimesMap.getOrDefault(num, 0) + 1); + } + PriorityQueue pq = new PriorityQueue(new Comparator(){ + public int compare(int[] a, int[] b){ + return a[1] - b[1]; + } + }); + for(Map.Entry eleTimesEntry : eleTimesMap.entrySet()){ + int ele = eleTimesEntry.getKey(), times = eleTimesEntry.getValue(); + if(pq.size() == k){ + if(pq.peek()[1] < times){ + pq.poll(); + pq.offer(new int[]{ele, times}); + } + }else{ + pq.offer(new int[]{ele, times}); + } + } + + int[] result = new int[k]; + for(int i = 0;i < k;i ++){ + result[i] = pq.poll()[0]; + } + return result; + } +} \ No newline at end of file diff --git "a/Week_02/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204.java" "b/Week_02/\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 00000000..ff4849c2 --- /dev/null +++ "b/Week_02/\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,22 @@ +/** + * + * 每个异位词处理方式同题目有效字母异位词 + * 将每个异位词根据出现字母次数构建Key + * 使用HashMap存储相同key的异位词 + * + * + **/ +class Solution { + public List> groupAnagrams(String[] strs) { + HashMap> ans = new HashMap<>(); + char[] cache = new char[26]; + for (String str : strs) { + Arrays.fill(cache, 'a'); + for (char c : str.toCharArray()) cache[c - 'a']++; + String key = String.valueOf(cache); + if (!ans.containsKey(key)) ans.put(key, new ArrayList<>()); + ans.get(key).add(str); + } + return new ArrayList<>(ans.values()); + } +} \ No newline at end of file diff --git "a/Week_02/\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/Week_02/\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 00000000..5c8000ed --- /dev/null +++ "b/Week_02/\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,17 @@ +/** + * + * 采用计数规则 s字符串出现的字母+1在t字符串出现的同样字母-1 + * 使用长度26的字符类型数组记录字母出现的情况 + * + **/ +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + char[] cache = new char[26]; + for (int i = 0; i < s.length(); ++i) { + cache[s.charAt(i) - 'a']++; + cache[t.charAt(i) - 'a']--; + } + return String.valueOf(cache).trim().isBlank(); + } +} \ No newline at end of file diff --git "a/Week_03/#\345\255\246\345\217\267: G20200343140122" "b/Week_03/#\345\255\246\345\217\267: G20200343140122" new file mode 100644 index 00000000..7c1c2bdd --- /dev/null +++ "b/Week_03/#\345\255\246\345\217\267: G20200343140122" @@ -0,0 +1,6 @@ +#学号: G20200343140122 +#姓名: 张介英 +#班级: 17期 +#语言: Java +#作业链接: https://github.com/325G/algorithm017/tree/master/Week_03 +#总结链接: https://github.com/325G/algorithm017/tree/master/Week_03/README.md \ No newline at end of file diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..b1198abb 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,37 @@ -学习笔记 \ No newline at end of file +本周学习内容为范型递归、分治思想、回溯思想。
+ 范型递归-通过方法通过不断调用自身达到解决问题的方式; + + +## 递归的三个思维要点: + + 1.不要人肉递归。避免用大脑做递归,最好使用纸笔对递归对状态画出递归状态树,这样能够更好的检验程序写的是否合适,对于边界条件的处理也更加明确。 + 2.找到最近最简方法,拆解为重复问题(重复子问题)。先将问题拆解为简单的递归方式,然后再逐渐添加边界条件判断。 + 3.数学归纳法思维。 + +## 递归模版: + + public void rec(int level, int param){ + // 判断终止条件 + if(level > MAX_LEVEL) + return; + // 执行逻辑 + process(level, param); + // 下探到下一层
+ rec(level + 1, param); + // 清理当前层 + + } + + +## 分治思想: + + 将原问题拆解为多个结构和原问题相似的小规模问题,通过递归的方式解决这些小问题然后进行结果合并,最终将问题解决。 + 问题拆解的足够小,一般可以达到直接求解的地步,在递归模版中的终止条件处可以直接返回最小问题解。 + 拆解问题不要忘记将问题的解合并。 + + +## 回溯思想: + + + 回溯通过不断尝试问题的解得到最终的题解。 + 回溯剪枝方式:如果得不到最优解则不继续尝试;如果加入的条件不满足题解则不继续尝试。 diff --git "a/Week_03/\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/Week_03/\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 00000000..b189e0b2 --- /dev/null +++ "b/Week_03/\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,24 @@ +/** + * 递归方式查找 + * 某节点的左右子树如果包含目标节点,则当前节点为公共祖先节点;因为要求解最近的公共祖先因此节点要尽可能靠下 + * 另外一种祖先节点为某节点的值为任意目标节点的值,并且另外一个节点存在与该节点的左子树或者右子树,则该节点为公共祖先节点 + * + */ +class Solution { + TreeNode ans = null; + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + dfs(root, p, q); + return ans; + } + public boolean dfs(TreeNode node, TreeNode p, TreeNode q) { + if (node == null) + return false; + boolean l = dfs(node.left, p, q); + boolean r = dfs(node.right, p, q); + if (l && r || (node.val == p.val || node.val == q.val) && (l || r)) { + ans = node; + return true; + } + return l || r || node.val == p.val || node.val == q.val; + } +} \ No newline at end of file diff --git "a/Week_03/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" "b/Week_03/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" new file mode 100644 index 00000000..dd042171 --- /dev/null +++ "b/Week_03/\344\273\216\345\211\215\345\272\217\344\270\216\344\270\255\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227\346\236\204\351\200\240\344\272\214\345\217\211\346\240\221.java" @@ -0,0 +1,30 @@ +/** + * + * 1. 前序数组中第一个节点为二叉树根节点 + * 2. 构建Map 存储中序数组值和数组下标 + * 3. 通过前序数组找到二叉树根节点值,然后在中序数组Map中找到二叉树根节点下标;因为中序遍历是左子树、根节点、右子树方式排列 因此左子树长度也可以计算出 + * 4. 递归函数设计 参数:前序数组的左右界限,中序数组的左右界限,返回值树节点 + * 5. 左树计算:前序数组第一个节点为根 因此左界限+1开始,右界限为左界限+左树长度;中序数组根节点位置通过中序Map可以计算得到,左界限为参数左界限,右界限为根节点下表-1 + * 6. 右树计算:前序数组左节点左界限+1+左树长度,右界限为参数右界限;中序数组左界限为根节点下标+1右界限为参数右界限 + * + **/ +class Solution { + HashMap inorderCache = new HashMap<>(); + public TreeNode buildTree(int[] preorder, int[] inorder) { + int n = inorder.length; + for (int i =0 ;i < n; ++i) { + inorderCache.put(inorder[i], i); + } + return dfs(preorder, inorder, 0, n - 1, 0, n - 1); + } + public TreeNode dfs(int[] preorder, int[] inorder, int preorderLeftIdx, int preorderRightIdx, int inorderLeftIdx, int inorderRightIdx) { + if (preorderLeftIdx > preorderRightIdx) return null; + int rootVal = preorder[preorderLeftIdx]; + int inorderMidIdx = inorderCache.get(rootVal); + TreeNode tree = new TreeNode(rootVal); + int leftTreeSize = inorderMidIdx - inorderLeftIdx; + tree.left = dfs(preorder, inorder, preorderLeftIdx + 1, preorderLeftIdx + leftTreeSize, inorderLeftIdx, inorderMidIdx - 1); + tree.right = dfs(preorder, inorder, preorderLeftIdx + leftTreeSize + 1, preorderRightIdx, inorderMidIdx + 1, inorderRightIdx); + return tree; + } +} \ No newline at end of file diff --git "a/Week_03/\345\205\250\346\216\222\345\210\227.java" "b/Week_03/\345\205\250\346\216\222\345\210\227.java" new file mode 100644 index 00000000..223ac55c --- /dev/null +++ "b/Week_03/\345\205\250\346\216\222\345\210\227.java" @@ -0,0 +1,28 @@ +/** + * + * 换个角度即初始化一个和待排序组合数组一样长度的数组 每个格子的数字从待组合的数组中选一个,且选过的数字不能在选择; + * 使用boolean类型的数组记录已经选过的元素下标,然后依次尝试填入的数字 + * + * + **/ +class Solution { + public List> permute(int[] nums) { + List> ans = new ArrayList<>(); + dfs(nums, 0, ans, new ArrayList<>(), new boolean[nums.length]); + return ans; + } + public void dfs(int[] nums, int idx, List> ans, List temp, boolean[] valid) { + if (idx == nums.length) { + ans.add(new ArrayList<>(temp)); + return; + } + for (int i = 0; i < nums.length; ++i) { + if (valid[i]) continue; + temp.add(nums[i]); + valid[i] = true; + dfs (nums, idx + 1, ans, temp, valid); + temp.remove(idx); + valid[i] = false; + } + } +} \ No newline at end of file diff --git "a/Week_03/\345\205\250\346\216\222\345\210\227II.java" "b/Week_03/\345\205\250\346\216\222\345\210\227II.java" new file mode 100644 index 00000000..634e1e2c --- /dev/null +++ "b/Week_03/\345\205\250\346\216\222\345\210\227II.java" @@ -0,0 +1,34 @@ +/** + * + * 和全排列的思想类似,需要注意的是相同数字相同位置只能排列一次 如 1(idx1)、2、 1(idx2) 如果再出现1(idx2)、2、1(idx1) 算做一种 + * 剪枝去重的方式 为对原排列数据做排序使相同的数字在一起,对于相同的数字,后面索引的数字如果选择择前面的必须要选择,如: 1(idx1)、2、 1(idx2) + * 如果idx2位置的1做选择加入答案则idx1位置的1必须已经被选择 + * + **/ +class Solution { + List> ans = new ArrayList<>(); + List temp = new ArrayList<>(); + boolean[] valid; + public List> permuteUnique(int[] nums) { + valid = new boolean[nums.length]; + Arrays.sort(nums); + dfs(0, nums); + return ans; + } + public void dfs(int idx, int[] nums){ + if (idx == nums.length) { + ans.add(new ArrayList<>(temp)); + return; + } + for (int i = 0;i < nums.length; ++i){ + if (valid[i] || (i > 0 && nums[i] == nums[i - 1] && !valid[i - 1])) + continue; + valid[i] = true; + temp.add(nums[i]); + dfs(idx + 1, nums); + valid[i] = false; + temp.remove(idx); + + } + } +} \ No newline at end of file diff --git "a/Week_03/\347\273\204\345\220\210.java" "b/Week_03/\347\273\204\345\220\210.java" new file mode 100644 index 00000000..801bbc8e --- /dev/null +++ "b/Week_03/\347\273\204\345\220\210.java" @@ -0,0 +1,24 @@ +/** + * + * 采用回溯思想,每次对元素进行选择或者不选择抉择 + * 剪枝:如果已选择元素与待选择元素无法凑成目标个数则不继续抉择 + * + **/ +class Solution { + public List> combine(int n, int k) { + List> ans = new ArrayList<>(); + dfs(n, k, 1, ans, new ArrayList<>()); + return ans; + } + public void dfs(int n, int k, int idx, List> ans, List temp) { + if (temp.size() + (n - idx + 1) < k) return; + if (temp.size() == k) { + ans.add(new ArrayList<>(temp)); + return; + } + dfs(n, k , idx + 1, ans, temp); + temp.add(idx); + dfs(n, k, idx + 1, ans, temp); + temp.remove(temp.size() - 1); + } +} \ No newline at end of file diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..74586151 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,51 @@ -学习笔记 \ No newline at end of file +##### 深度优先搜索和广度优先搜索 + - 每个节点访问一次 + - 每个节点仅访问一次 +##### 深度优先搜索模板 + Set visited = new HashSet<>(); + + public void dfs(Node node, int target, Set visited) { + if (visited.contains(node) + return; + if (node.val == target) + return; + visited.add(node); + process(node); + for (Node next : node.children) { + dfs(next, target, visited); + } + } + ##### 广度优先搜索模板 + public void bfs(Node node, int target, Set visited) { + Deque deque = new LinkedList<>(); + stack.offer(node); + while (!stack.isEmpty()) { + Node cur = stack.poll(); + visited.add(cur); + process(cur); + for (Node next : gen_relation_nodes(cur)) + deque.offer(next); + } + } + ##### 贪心算法 + 贪心算法是一种在每一步选择中都采取在当前状态下最好或者最优的选择,从而希 + 望导致结果是全局最好的或最优的算法。 + 与动态规划不同点:贪心选择不能回退,动态规划可以保存中间运行结果可以回退。 + 使用贪心算法关键点:1.贪心是可以得到全局最优解的 2.贪心角度可以有多种,如 + 从前往后、从后往前、局部切入 + + ##### 二分查找前提条件 + 1.目标函数单调性 + 2.存在边界 + 3.能够通过索引访问 + ##### 二分查找代码模板 + int left = 0, right = array.length() - 1; + while (left <= right) { + int mid = (right - left) /2 + left; + if (array[mid] == target) + break or return; + else if (array[mid] < target) + left = mid + 1; + else + right = mid - 1; + } diff --git "a/Week_04/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" "b/Week_04/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" new file mode 100644 index 00000000..dae00403 --- /dev/null +++ "b/Week_04/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.java" @@ -0,0 +1,16 @@ +/* + * 贪心算法 + * 比较当天与前一天的价格如果有收益就出售,累积计算总利润 + * + **/ +class Solution { + public int maxProfit(int[] prices) { + int ans = 0, i = 0; + for (int j = 1; j < prices.length; ++j) { + if (prices[j] > prices[i]) + ans += prices[j] - prices[i]; + ++i; + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_04/\345\210\206\345\217\221\351\245\274\345\271\262.java" "b/Week_04/\345\210\206\345\217\221\351\245\274\345\271\262.java" new file mode 100644 index 00000000..a2713e6b --- /dev/null +++ "b/Week_04/\345\210\206\345\217\221\351\245\274\345\271\262.java" @@ -0,0 +1,23 @@ +/** + * 贪心算法 + * 升序排序小孩与饼干 优先将小饼干送给胃口小的小朋友 + * 遍历一次饼干即可知道能送出几块 + **/ +class Solution { + public int findContentChildren(int[] g, int[] s) { + int ans = 0; + Arrays.sort(g); + Arrays.sort(s); + int j = 0; + for (int i = 0; i < s.length; ++i) { + if (j == g.length) + return g.length; + if (s[i] >= g[j]){ + ans++; + j++; + } + + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231.java" "b/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231.java" new file mode 100644 index 00000000..7a440427 --- /dev/null +++ "b/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231.java" @@ -0,0 +1,56 @@ +/** + * + * 双向BFS搜索 + * 使用两个集合表示搜索的两端,每次从较少元素集合开始搜索,如果搜索的下层节点在另外一个集合中存在则为最短路径;中途需要使用另外一个集合记录已经搜索过的节点; + * wordList转换为Set集合加速判断 + * 由于每次只能变换一个字母,所以将当前字母每一位从a-z一次尝试从wordList中判断 如果存在变换后的单词则为当前单词的变换单词(需要从已访问集合中判断一次,防止重复访问) + **/ + +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + Set wordSet = new HashSet<>(wordList); + if (!wordSet.contains(endWord)) + return 0; + Set visited = new HashSet<>(); + visited.add(beginWord); + Set beginVisited = new HashSet<>(); + beginVisited.add(beginWord); + Set endVisited = new HashSet<>(); + endVisited.add(endWord); + + int step = 1; + while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { + if (beginVisited.size() > endVisited.size()) { + Set temp = new HashSet<>(beginVisited); + beginVisited = endVisited; + endVisited = temp; + } + Set nextVisited = new HashSet<>(); + for (String word : beginVisited) { + char[] wordChar = word.toCharArray(); + for (int i = 0; i < word.length(); ++i) { + char originChar = wordChar[i]; + for (char c = 'a'; c <= 'z'; ++c) { + if (originChar == c) + continue; + wordChar[i] = c; + String nextWord = String.valueOf(wordChar); + if (wordSet.contains(nextWord)) { + if (endVisited.contains(nextWord)) { + return step + 1; + } + if (!visited.contains(nextWord)) { + visited.add(nextWord); + nextVisited.add(nextWord); + } + } + } + wordChar[i] = originChar; + } + } + step++; + beginVisited = nextVisited; + } + return 0; + } +} \ No newline at end of file diff --git "a/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231II.java" "b/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231II.java" new file mode 100644 index 00000000..c37a23fe --- /dev/null +++ "b/Week_04/\345\215\225\350\257\215\346\216\245\351\276\231II.java" @@ -0,0 +1,94 @@ +/** + * + * 双向BFS + DFS + * 双向BFS思路与单词接龙方式一样,不同之处是需要将所有相邻的节点找出并记录 + * 然后使用DFS 回溯方式将路径全部找出 + * + **/ +class Solution { + public List> findLadders(String beginWord, String endWord, List wordList) { + List> ans = new ArrayList<>(); + Set wordSet = new HashSet<>(wordList); + if (!wordSet.contains(endWord)) + return ans; + Map> allComboDict = new HashMap<>(); + boolean found = bfs(beginWord, endWord, wordSet, allComboDict); + if (!found) + return ans; + dfs(beginWord, endWord, allComboDict, ans, new ArrayList<>(){{add(beginWord);}}); + return ans; + + } + public boolean bfs(String beginWord, String endWord, Set wordSet, Map> allComboDict) { + Set visited = new HashSet<>(); + visited.add(beginWord); + visited.add(endWord); + Set beginVisited = new HashSet<>(); + beginVisited.add(beginWord); + Set endVisited = new HashSet<>(); + endVisited.add(endWord); + + boolean forword = true; + boolean found = false; + while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { + if (beginVisited.size() > endVisited.size()) { + Set temp = new HashSet<>(beginVisited); + beginVisited = endVisited; + endVisited = temp; + forword = !forword; + } + Set nextVisited = new HashSet<>(); + for (String word : beginVisited) { + char[] wordChar = word.toCharArray(); + for (int i = 0; i < word.length(); ++i) { + char originChar = wordChar[i]; + for (char c = 'a'; c <= 'z'; ++c) { + if (originChar == c) + continue; + wordChar[i] = c; + String nextWord = String.valueOf(wordChar); + if (wordSet.contains(nextWord)) { + if (endVisited.contains(nextWord)) { + found = true; + addDict(word, nextWord, allComboDict, forword); + } + if (!visited.contains(nextWord)) { + nextVisited.add(nextWord); + addDict(word, nextWord, allComboDict, forword); + } + } + } + wordChar[i] = originChar; + } + } + beginVisited = nextVisited; + visited.addAll(nextVisited); + if (found) + break; + } + return found; + } + public void addDict(String fromWord, String toWord, Map> allComboDict, boolean forword) { + if (!forword) { + String temp = fromWord; + fromWord = toWord; + toWord = temp; + } + allComboDict.computeIfAbsent(fromWord, x -> new HashSet<>()); + allComboDict.get(fromWord).add(toWord); + } + public void dfs(String beginWord, String endWord, Map> allComboDict, List> ans, List temp) { + if (beginWord.equals(endWord)) { + ans.add(new ArrayList<>(temp)); + return; + } + if (!allComboDict.containsKey(beginWord)) + return; + for (String nextWord : allComboDict.get(beginWord)) { + temp.add(nextWord); + dfs(nextWord, endWord, allComboDict, ans, temp); + temp.remove(temp.size() - 1); + } + } + +} \ No newline at end of file diff --git "a/Week_04/\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/Week_04/\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 00000000..b11c746c --- /dev/null +++ "b/Week_04/\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,30 @@ +/** + * 从00位置开始遍历,当遇到陆地(1)时,陆地数加一并把陆地标记改为水(0)然后使用深度优先搜索继续查找相连陆地 + * + * + **/ +class Solution { + public int numIslands(char[][] grid) { + int ans = 0; + if (grid == null || grid.length == 0) + return ans; + for (int i = 0; i < grid.length; ++i) { + for (int j = 0; j < grid[0].length; ++j) { + if (grid[i][j] == '1') { + ans++; + dfs(i, j, grid); + } + } + } + return ans; + } + public void dfs(int line, int column, char[][] grid) { + if (line < 0 || line >= grid.length || column < 0 || column >= grid[0].length || grid[line][column] == '0') + return; + grid[line][column] = '0'; + dfs(line + 1, column, grid); + dfs(line - 1, column, grid); + dfs(line, column + 1, grid); + dfs(line, column - 1, grid); + } +} \ No newline at end of file diff --git "a/Week_04/\346\211\253\351\233\267\346\270\270\346\210\217.java" "b/Week_04/\346\211\253\351\233\267\346\270\270\346\210\217.java" new file mode 100644 index 00000000..ff4a5c39 --- /dev/null +++ "b/Week_04/\346\211\253\351\233\267\346\270\270\346\210\217.java" @@ -0,0 +1,52 @@ +/** + * + * 使用两个数组代表下次搜索方位 + * 如果挖出的位置为地雷则改变值为X,结束 + * 否则对八个方位进行一次检索,如果存在地雷则修改挖出位为地雷数 + * 如果周围没有地雷则修改为B标记然后对8个方位依次进行深度优先搜索 + * + **/ +class Solution { + int[] x = {-1, 1, 0, 0, 1, -1, 1, -1}; + int[] y = {0, 0, 1, -1, 1, -1, -1, 1}; + public char[][] updateBoard(char[][] board, int[] click) { + int i = click[0]; + int j = click[1]; + if (board[i][j] == 'M') { + board[i][j] = 'X'; + + } else { + dfs(i ,j, board); + } + + return board; + + } + public void dfs(int i, int j, char[][] board) { + //if (board[i][j] == 'E') { + int num = 0; + for (int k = 0; k < 8; ++k) { + + int curX = i + x[k]; + int curY = j + y[k]; + if (curX < 0 || curX >= board.length || curY < 0 || curY >= board[0].length) + continue; + if (board[curX][curY] == 'M') + num ++; + } + if (num > 0) { + board[i][j] = (char) (num + '0'); + return; + } else { + board[i][j] = 'B'; + for (int k = 0; k < 8; ++k) { + int curX = i + x[k]; + int curY = j + y[k]; + if (curX < 0 || curX >= board.length || curY < 0 || curY >= board[0].length || board[curX][curY] != 'E') + continue; + dfs(curX, curY, board); + } + } + //} + } + } \ No newline at end of file diff --git "a/Week_04/\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.java" "b/Week_04/\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.java" new file mode 100644 index 00000000..c092e323 --- /dev/null +++ "b/Week_04/\346\220\234\347\264\242\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204.java" @@ -0,0 +1,34 @@ +/** + * + * 二分法 + * 取中点 判断递增序列在左边还是在右边 (根据递增性能够判断目标数是否在该序列中) + * 根据判断递增序列中是否存在目标数 选择检索起始位置和终止位置 + * + * + **/ +class Solution { + public int search(int[] nums, int target) { + if (nums.length == 0) + return -1; + int lo = 0, hi = nums.length - 1; + while (lo <= hi) { + int mid = (hi - lo) / 2 + lo; + if (nums[mid] == target) + return mid; + if (nums[mid] >= nums[lo]) { + if (target >= nums[lo] && target < nums[mid]) { + hi = mid - 1; + } else { + lo = mid + 1; + } + } else { + if (target > nums[mid] && target <= nums[hi]) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + } + return -1; + } +} \ No newline at end of file diff --git "a/Week_04/\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" "b/Week_04/\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" new file mode 100644 index 00000000..02cbc227 --- /dev/null +++ "b/Week_04/\346\237\240\346\252\254\346\260\264\346\211\276\351\233\266.java" @@ -0,0 +1,27 @@ +/** + * + * 贪心算法 + * 优先使用大额的币种 每次找零如果硬币数量小于0 则说明无法找零 + * + **/ +class Solution { + public boolean lemonadeChange(int[] bills) { + int ten = 0, five = 0; + for (int bill : bills) { + if (bill == 5) { + five++; + } else if (bill == 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (ten < 0 || five < 0) + return false; + } + return true; + } +} \ No newline at end of file diff --git "a/Week_04/\346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272.java" "b/Week_04/\346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272.java" new file mode 100644 index 00000000..ee723b37 --- /dev/null +++ "b/Week_04/\346\250\241\346\213\237\350\241\214\350\265\260\346\234\272\345\231\250\344\272\272.java" @@ -0,0 +1,39 @@ +/** + * 使用两个数组表示x轴y轴方向 + * 创建Set集合存储障碍物位置 + * 遇到转向指令根据x轴y轴数组 确定前进方向 + * 前进时每次前进一格然后计算欧式距离的平方,如果前进的一格为障碍物则原地不动并执行下条指令 + * 欧式距离的平方 = x^2 + y^2 + **/ +class Solution { + public int robotSim(int[] commands, int[][] obstacles) { + int[] dy = {1, 0, -1, 0}; + int[] dx = {0, 1, 0, -1}; + int direction = 0; + int ans = 0, lx = 0, ly = 0; + Set locationSet = new HashSet<>(); + for (int[] obstacle : obstacles) { + locationSet.add(((long)(obstacle[0] + 30000) << 16) + (long)(obstacle[1] + 30000)); + } + for (int command : commands) { + if (command == -1) { + direction = (direction + 1) % 4; + } else if (command == -2) { + direction = (direction + 3) % 4; + } else { + for (int i = 0; i < command; ++i) { + int nextY = dy[direction] + ly; + int nextX = dx[direction] + lx; + Long location = ((long)(nextX + 30000) << 16) + (long)(nextY + 30000); + if (locationSet.contains(location)) + break; + ly = nextY; + lx = nextX; + ans = Math.max(ans, ly * ly + lx * lx); + } + } + + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217.java" "b/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217.java" new file mode 100644 index 00000000..203c842f --- /dev/null +++ "b/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217.java" @@ -0,0 +1,17 @@ +/** + * + * 从后向前贪心算法 + * 起始目标点选在最后,依次检索能够跳跃过目标点的位置,如果能够到达起始位置则可以从起始跳跃到最后 + * 从最后一个位置起向前检索,如果存在一个位置可以跳跃到最后则选取该点为目标点,继续向前检索能够到达该点的位置。 + * + **/ +class Solution { + public boolean canJump(int[] nums) { + int last = nums.length - 1; + for (int i = nums.length - 1; i >= 0; --i) { + if (nums[i] + i >= last) + last = i; + } + return last == 0; + } +} \ No newline at end of file diff --git "a/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217II.java" "b/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217II.java" new file mode 100644 index 00000000..12ae1f4a --- /dev/null +++ "b/Week_04/\350\267\263\350\267\203\346\270\270\346\210\217II.java" @@ -0,0 +1,20 @@ +/** + * + * 前提一定可以跳跃到最后,因此跳跃段为起始到终点前一个位置 + * 相当于对跳跃分段 每次段最大即可获取到最小次数 + * 起始段为起始位置跳跃最远位置,再第一段中寻找最远跳跃位置作为第二段终点 依次进行直到跳跃到最后 + * + **/ +class Solution { + public int jump(int[] nums) { + int end = 0, ans = 0, maxPos = 0; + for (int i = 0; i < nums.length - 1; ++i) { + maxPos = Math.max(maxPos, nums[i] + i); + if (i == end) { + end = maxPos; + ans++; + } + } + return ans; + } +} \ No newline at end of file diff --git a/Week_06/README.md b/Week_06/README.md index 50de3041..dfce58c7 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,17 @@ -学习笔记 \ No newline at end of file +动态规划 +##### 动态规划情况 + 1.存在最优子结构 + 问题的最优解可以通过子问题的最优解推导出 + 2.无后效性 + 某状态确定后,不会受到后续状态影响 + 3.具有重复子问题 + 问题可以分解为多个子问题求解,通过求解子问题选择最优情况得到问题本身解 + +##### 解题步骤 + 1.寻找重复子问题,通过自底向上方式推导 + 2.确定dp数组,即问题的状态 + 3.dp方程 通过子问题推导的过程 + +##### 解题思路 + 数组类题目:可以定义二维、三维数组作为状态,初始化数组起始状态,然后从1..n逐步推导各种状态最后得到答案 + 字符串类题目:从空字符串开始考虑,逐步添加字母得到中间状态 diff --git "a/Week_06/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" "b/Week_06/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" new file mode 100644 index 00000000..f30633d2 --- /dev/null +++ "b/Week_06/\346\234\200\345\244\247\346\255\243\346\226\271\345\275\242.java" @@ -0,0 +1,33 @@ +/** + * + * 子问题- m-n位置可以看做左上方 左方 上方构成的正方形额外添加的边,那么m-n位置可构成的正方形为 上方 左方 左上方可构成最大正方形最小的一个边长加一 + * dp数组 int[][] m n位置可以构成的最大正方形 + * dp方程 dp[m][n] = 1, m == 1 || n == 1; dp[m][n] = min(dp[m - 1][n - 1], dp[m - 1][n], dp[m][n - 1]) + 1; + * + * + **/ +class Solution { + public int maximalSquare(char[][] matrix) { + if (matrix.length == 0 || matrix[0].length == 0) + return 0; + + int m = matrix.length; + + int n = matrix[0].length; + int[][] dp = new int[m][n]; + int maxSide = 0; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (matrix[i][j] == '1') { + if (i == 0 || j == 0) { + dp[i][j] = 1; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1; + } + } + maxSide = Math.max(dp[i][j], maxSide); + } + } + return maxSide * maxSide; + } +} \ No newline at end of file diff --git "a/Week_06/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" "b/Week_06/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" new file mode 100644 index 00000000..9de04822 --- /dev/null +++ "b/Week_06/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214.java" @@ -0,0 +1,31 @@ +/** + * + * 子问题- 某网格只能由上方或者左方移动过来,从终点开始考虑,路径最小值取左方格与上方格较小的一个,并将最小值方格作为终点形成子问题 + * dp数组- int[][] 代表m n位置方格作为终点的最小值 + * dp方程- dp[0][n] = dp[0][n - 1] + grid[0][n] m = 0; dp[m][0] = dp[m - 1][0] + grid[m][0] n = 0; dp[m][n] = min(dp[m - 1][n], dp[m][n - 1]) m,n >= 1; + * + * + **/ +class Solution { + public int minPathSum(int[][] grid) { + int m = grid.length; + if (m == 0) + return 0; + int n = grid[0].length; + + 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 i = 1; i < n; ++i) { + dp[0][i] = dp[0][i - 1] + grid[0][i]; + } + 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]; + } +} \ No newline at end of file diff --git "a/Week_07/N\347\232\207\345\220\216.java" "b/Week_07/N\347\232\207\345\220\216.java" new file mode 100644 index 00000000..18db9abf --- /dev/null +++ "b/Week_07/N\347\232\207\345\220\216.java" @@ -0,0 +1,37 @@ +class Solution { + public List> solveNQueens(int n) { + List> ans = new ArrayList<>(); + dfs(n, 0, new int[n], ans, new HashSet<>(), new HashSet<>(), new HashSet<>()); + return ans; + } + public void dfs(int n, int row, int[] queens, List> ans, Set column, Set diags1, Set diags2) { + if (row == n) { + ans.add(genAns(queens)); + return; + } + for (int i = 0; i < n; ++i) { + int diag1 = row + i, diag2 = row - i; + if (column.contains(i) || diags1.contains(diag1) || diags2.contains(diag2)) + continue; + column.add(i); + queens[row] = i; + diags1.add(diag1); + diags2.add(diag2); + dfs(n, row + 1, queens, ans, column, diags1, diags2); + column.remove(i); + queens[row] = -1; + diags1.remove(diag1); + diags2.remove(diag2); + } + } + public List genAns(int[] queens) { + List result = new ArrayList<>(); + for (int i = 0; i < queens.length; ++i) { + char[] row = new char[queens.length]; + Arrays.fill(row, '.'); + row[queens[i]] = 'Q'; + result.add(String.valueOf(row)); + } + return result; + } +} \ No newline at end of file diff --git a/Week_07/README.md b/Week_07/README.md index 50de3041..f359c492 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,89 @@ -学习笔记 \ No newline at end of file +##### Trie + 字典树,即 Trie 树,又称单词查找树或键树,是一种树形结构。典型应用是用于统计和排序大量的字符串(但不仅限于 字符串),所以经常被搜索引擎系统用于文本词频统计。 + 优点:最大限度地减少无谓的字符串比较,查询效率比哈希表高。 + 基本性质: + 1. 结点本身不存完整单词; + 2. 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的 字符串; + 3. 每个结点的所有子结点路径代表的字符都不相同。 +##### Trie 代码模版 + class Trie { + boolean end; + Trie[] next = new Trie[26]; + public Trie() { + } + public void insert(String word) { + if (word == null || word.length() == 0) + return; + Trie trie = this; + for (char c : word.toCharArray()) { + if (trie.next[c - 'a'] == null) + trie.next[c - 'a'] = new Trie(); + trie = trie.next[c - 'a']; + } + trie.end = true; + } + public boolean search(String word) { + Trie node = findPrefix(word); + return node != null && node.end; + } + public boolean startsWith(String prefix) { + return findPrefix(prefix) != null; + } + private Trie findPrefix(String word) { + if (word == null || word.length() == 0) + return null; + Trie node = this; + for (char c : word.toCharArray()) { + node = node.next[c - 'a']; + if (node == null) + return null; + } + return node; + } + } + +##### 并查集 + 解决组团 配对问题 + 基本操作: + makeSet(s):建立一个新的并查集,其中包含 s 个单元素集合。 + unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并。 + find(x):找到元素 x 所在的集合的代表,该操作也可以用于判断两个元 素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。 +##### 并查集代码模版 + class UnionFind { + private int count = 0; + private int[] parent; + public UnionFind(int n) { + + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + + } + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + + return p; + } + + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootP] = rootQ; + count--; + } + + } +##### 红黑树 + 红黑树是一种近似平衡的二叉搜索树(Binary Search Tree),它能够确保任何一 个结 点的左右子树的高度差小于两倍。具体来说,红黑树是满足如下条件的二叉 搜索树: + + • 每个结点要么是红色,要么是黑色 + • 根结点是黑色 + • 每个叶结点(NIL结点,空结点)是黑色的。 + • 不能有相邻接的两个红色结点 + • 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。 diff --git "a/Week_07/\345\215\225\350\257\215\346\216\245\351\276\231.java" "b/Week_07/\345\215\225\350\257\215\346\216\245\351\276\231.java" new file mode 100644 index 00000000..0724609b --- /dev/null +++ "b/Week_07/\345\215\225\350\257\215\346\216\245\351\276\231.java" @@ -0,0 +1,52 @@ +/** + * + * 双向BFS + * + **/ +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + Set wordSet = new HashSet<>(wordList); + if (!wordSet.contains(endWord)) + return 0; + Set visited = new HashSet<>(); + visited.add(beginWord); + Set beginVisited = new HashSet<>(); + beginVisited.add(beginWord); + Set endVisited = new HashSet<>(); + endVisited.add(endWord); + int step = 1; + while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { + if (beginVisited.size() > endVisited.size()) { + Set temp = new HashSet<>(beginVisited); + beginVisited = endVisited; + endVisited = temp; + } + Set nextVisited = new HashSet<>(); + for (String word : beginVisited) { + char[] wordChar = word.toCharArray(); + for (int i = 0; i < word.length(); ++i) { + char originChar = wordChar[i]; + for (char c = 'a'; c <= 'z'; ++c) { + if (originChar == c) + continue; + wordChar[i] = c; + String nextWord = String.valueOf(wordChar); + if (wordSet.contains(nextWord)) { + if (endVisited.contains(nextWord)) + return step + 1; + if (!visited.contains(nextWord)) { + visited.add(nextWord); + nextVisited.add(nextWord); + } + } + } + wordChar[i] = originChar; + } + + } + beginVisited = nextVisited; + step++; + } + return 0; + } +} \ No newline at end of file diff --git "a/Week_07/\345\215\225\350\257\215\346\220\234\347\264\242II.java" "b/Week_07/\345\215\225\350\257\215\346\220\234\347\264\242II.java" new file mode 100644 index 00000000..edac11bf --- /dev/null +++ "b/Week_07/\345\215\225\350\257\215\346\220\234\347\264\242II.java" @@ -0,0 +1,50 @@ +class Solution { + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + public List findWords(char[][] board, String[] words) { + Trie trie = new Trie(); + for (String word : words) + trie.insert(word); + List ans = new ArrayList<>(); + for (int i = 0; i < board.length; ++i) { + for (int j = 0; j < board[0].length; ++j) { + dfs(board, i, j, trie.root , ans); + } + } + return ans; + } + public void dfs(char[][] board, int i, int j, TrieNode node, List ans) { + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length) + return; + char c = board[i][j]; + if (c == '#' || node.next[c - 'a'] == null) + return; + node = node.next[c - 'a']; + if (node.word != null) { + ans.add(node.word); + node.word = null; + return; + } + board[i][j] = '#'; + for (int k = 0; k < 4; ++k) { + dfs(board, dx[k] + i, dy[k] + j, node, ans); + } + board[i][j] = c; + } + class Trie { + TrieNode root = new TrieNode(); + public void insert(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + if (node.next[c - 'a'] == null) + node.next[c - 'a'] = new TrieNode(); + node = node.next[c - 'a']; + } + node.word = word; + } + } + class TrieNode { + TrieNode[] next = new TrieNode[26]; + String word = null; + } +} \ No newline at end of file diff --git "a/Week_07/\345\256\236\347\216\260Trie.java" "b/Week_07/\345\256\236\347\216\260Trie.java" new file mode 100644 index 00000000..ca1e3fa3 --- /dev/null +++ "b/Week_07/\345\256\236\347\216\260Trie.java" @@ -0,0 +1,53 @@ +class Trie { + boolean end; + Trie[] next = new Trie[26]; + /** Initialize your data structure here. */ + public Trie() { + + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + if (word == null || word.length() == 0) + return; + Trie trie = this; + for (char c : word.toCharArray()) { + if (trie.next[c - 'a'] == null) + trie.next[c - 'a'] = new Trie(); + trie = trie.next[c - 'a']; + } + trie.end = true; + + + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + Trie node = findPrefix(word); + return node != null && node.end; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + return findPrefix(prefix) != null; + } + private Trie findPrefix(String word) { + if (word == null || word.length() == 0) + return null; + Trie node = this; + for (char c : word.toCharArray()) { + node = node.next[c - 'a']; + if (node == null) + return null; + } + return node; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ \ No newline at end of file diff --git "a/Week_07/\345\262\233\345\261\277\346\225\260\351\207\217.java" "b/Week_07/\345\262\233\345\261\277\346\225\260\351\207\217.java" new file mode 100644 index 00000000..a662a85c --- /dev/null +++ "b/Week_07/\345\262\233\345\261\277\346\225\260\351\207\217.java" @@ -0,0 +1,59 @@ +/** + * + * 创建并查集 + * 初始化每个网格为一个岛屿 然后合并每个网格陆地区域 + * + **/ +class Solution { + public int numIslands(char[][] grid) { + int m = grid.length; + int n = grid[0].length; + UnionFind uf = new UnionFind(m * n); + int w = 0; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == '1') { + if (i + 1 < m && grid[i + 1][j] == '1') + uf.union(i * n + j, (i + 1) * n + j); + if (j + 1 < n && grid[i][j + 1] == '1') + uf.union(i * n + j, i * n + j + 1); + } else { + w++; + } + } + } + return uf.count() - w; + } + class UnionFind { + int[] parent; + int count; + public int count() { + return count; + } + public UnionFind(int n) { + parent = new int[n]; + count = n; + for (int i = 0; i < n; ++i) { + parent[i] = i; + } + + + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parent[rootP] = rootQ; + count--; + } + } + public int find(int k) { + while (k != parent[k]) { + parent[k] = parent[parent[k]]; + k = parent[k]; + + } + return k; + } + } +} \ No newline at end of file diff --git "a/Week_07/\346\213\254\345\217\267\347\224\237\346\210\220.java" "b/Week_07/\346\213\254\345\217\267\347\224\237\346\210\220.java" new file mode 100644 index 00000000..4a9787fc --- /dev/null +++ "b/Week_07/\346\213\254\345\217\267\347\224\237\346\210\220.java" @@ -0,0 +1,23 @@ +/** + * 递归 剪枝 左括号不能超过总数对数 右括号不能超过左括号 + * + **/ +class Solution { + public List generateParenthesis(int n) { + List ans = new ArrayList<>(); + if (n == 0) + return ans; + dfs(0 , 0, n , ans, ""); + return ans; + } + public void dfs(int left, int right, int n, List ans, String temp) { + if (left == n && right == n) { + ans.add(temp); + return; + } + if (left < n) + dfs(left + 1, right, n, ans, temp + "("); + if (right < left) + dfs(left, right + 1, n, ans, temp + ")"); + } +} \ No newline at end of file diff --git "a/Week_07/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" "b/Week_07/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" new file mode 100644 index 00000000..964b6a0d --- /dev/null +++ "b/Week_07/\346\234\200\345\260\217\345\237\272\345\233\240\345\217\230\345\214\226.java" @@ -0,0 +1,48 @@ +class Solution { + public int minMutation(String start, String end, String[] bank) { + char[] letters = {'A', 'C', 'G', 'T'}; + Set wordSet = new HashSet<>(Arrays.asList(bank)); + if (!wordSet.contains(end)) + return -1; + Set visited = new HashSet<>(); + visited.add(start); + Set beginVisited = new HashSet<>(); + beginVisited.add(start); + Set endVisited = new HashSet<>(); + endVisited.add(end); + + int step = 0; + while (!beginVisited.isEmpty() && !endVisited.isEmpty()) { + if (beginVisited.size() > endVisited.size()) { + Set temp = new HashSet<>(beginVisited); + beginVisited = endVisited; + endVisited = temp; + } + Set nextVisited = new HashSet<>(); + for (String word : beginVisited) { + char[] wordChar = word.toCharArray(); + for (int i = 0; i < word.length(); ++i) { + char originChar = wordChar[i]; + for (char c : letters) { + if (originChar == c) + continue; + wordChar[i] = c; + String nextWord = String.valueOf(wordChar); + if (wordSet.contains(nextWord)) { + if (endVisited.contains(nextWord)) + return step + 1; + if (!visited.contains(nextWord)) { + nextVisited.add(nextWord); + visited.add(nextWord); + } + } + } + wordChar[i] = originChar; + } + } + beginVisited = nextVisited; + step++; + } + return -1; + } +} \ No newline at end of file diff --git "a/Week_07/\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.java" "b/Week_07/\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.java" new file mode 100644 index 00000000..5703526a --- /dev/null +++ "b/Week_07/\346\234\211\346\225\210\347\232\204\346\225\260\347\213\254.java" @@ -0,0 +1,18 @@ +class Solution { + public boolean isValidSudoku(char[][] board) { + boolean[][] line = new boolean[9][9]; + boolean[][] column = new boolean[9][9]; + boolean[][][] block = new boolean[3][3][9]; + for (int i = 0;i < 9; ++i) { + for (int j = 0; j < 9; ++j) { + if (board[i][j] != '.') { + int num = board[i][j] - '0' - 1; + if (line[i][num] || column[j][num] || block[i / 3][j / 3][num]) + return false; + line[i][num] = column[j][num] = block[i / 3][j / 3][num] = true; + } + } + } + return true; + } +} \ No newline at end of file diff --git "a/Week_07/\346\234\213\345\217\213\345\234\210.java" "b/Week_07/\346\234\213\345\217\213\345\234\210.java" new file mode 100644 index 00000000..4ce0c698 --- /dev/null +++ "b/Week_07/\346\234\213\345\217\213\345\234\210.java" @@ -0,0 +1,48 @@ +/** + * 使用并查集 + * 初始化将自己列为一个圈 然后对每个人的朋友进行合并 最后统计公共群体数量 + * + **/ +class Solution { + public int findCircleNum(int[][] M) { + if (M == null || M.length == 0) + return 0; + UnionFind uf = new UnionFind(M.length); + for (int i = 0; i < M.length; ++i) { + for (int j = 0; j < M[0].length; ++j) { + if (M[i][j] == 1) + uf.union(i, j); + } + } + return uf.count(); + } + class UnionFind { + int count; + int[] parent; + public UnionFind(int n) { + count = n; + parent = new int[n]; + for (int i = 0; i < n; ++i) { + parent[i] = i; + } + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parent[rootQ] = rootP; + count--; + } + } + public int count() { + return count; + } + public int find(int k) { + while (k != parent[k]) { + parent[k] = parent[parent[k]]; + k = parent[k]; + } + return k; + } + } +} \ No newline at end of file diff --git "a/Week_07/\347\210\254\346\245\274\346\242\257.java" "b/Week_07/\347\210\254\346\245\274\346\242\257.java" new file mode 100644 index 00000000..915860d3 --- /dev/null +++ "b/Week_07/\347\210\254\346\245\274\346\242\257.java" @@ -0,0 +1,20 @@ +/** + * 递归备忘录方式剪枝重复计算 + * + **/ +class Solution { + int[] cache; + public int climbStairs(int n) { + + cache = new int[n + 1]; + cache[0] = 1; + cache[1] = 1; + return dfs(n); + } + public int dfs(int n) { + if (cache[n] != 0) + return cache[n]; + cache[n] = dfs(n - 1) + dfs(n - 2); + return cache[n]; + } +} \ No newline at end of file diff --git "a/Week_07/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" "b/Week_07/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" new file mode 100644 index 00000000..01d42f31 --- /dev/null +++ "b/Week_07/\350\242\253\345\233\264\347\273\225\347\232\204\345\214\272\345\237\237.java" @@ -0,0 +1,62 @@ +/** + * 创建并查集 + * 额外创建边缘网格 并将边缘的网格与额外的边缘网格相连 最后统计与边缘网格相连的区域不做修改 + * + * + **/ +class Solution { + public void solve(char[][] board) { + int m = board.length; + if (m == 0) + return; + int n = board[0].length; + int side = m * n; + UnionFind uf = new UnionFind(m * n + 1); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (board[i][j] == 'O') { + if (i == 0 ||i == m - 1 || j == 0 || j == n - 1) + uf.union(i * n + j, side); + if (i + 1 < m && board[i + 1][j] == 'O') + uf.union(i * n + j, (i + 1) * n + j); + if (j + 1 < n && board[i][j + 1] == 'O') + uf.union(i * n + j, (i * n + j + 1)); + } + } + } + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (board[i][j] == 'O' && !uf.isConn(i * n + j, side)) + board[i][j] = 'X'; + } + } + } + class UnionFind { + int[] parent; + public boolean isConn(int p, int q) { + return find(p) == find(q); + } + public UnionFind(int n) { + parent = new int[n]; + for (int i = 0; i < n; ++i) { + parent[i] = i; + } + } + public int find(int k) { + while (k != parent[k]) { + parent[k] = parent[parent[k]]; + k = parent[k]; + } + return k; + } + public void union(int p, int q) { + int rootP = find(p); + int rootQ = find(q); + if (rootP != rootQ) { + parent[rootQ] = rootP; + } + } + + + } +} \ No newline at end of file diff --git "a/Week_07/\350\247\243\346\225\260\347\213\254.java" "b/Week_07/\350\247\243\346\225\260\347\213\254.java" new file mode 100644 index 00000000..e2cfa99f --- /dev/null +++ "b/Week_07/\350\247\243\346\225\260\347\213\254.java" @@ -0,0 +1,37 @@ +class Solution { + boolean[][] line = new boolean[9][9]; + boolean[][] column = new boolean[9][9]; + boolean[][][] block = new boolean[3][3][9]; + List spaces = new ArrayList<>(); + boolean valid = false; + public void solveSudoku(char[][] board) { + for (int i = 0;i < 9; ++i) { + for (int j = 0;j < 9; ++j) { + if (board[i][j] == '.') { + spaces.add(new int[]{i, j}); + }else{ + int num = board[i][j] - '0' - 1; + line[i][num] = column[j][num] = block[i / 3][j / 3][num] = true; + } + } + } + dfs(0, board); + } + + public void dfs(int idx, char[][] board){ + if (idx == spaces.size()) { + valid = true; + return; + } + int[] space = spaces.get(idx); + int i = space[0], j = space[1]; + for (int num = 0;num < 9 && !valid; ++num) { + if(!line[i][num] && !column[j][num] && !block[i /3][j / 3][num]){ + line[i][num] = column[j][num] = block[i / 3][j / 3][num] = true; + board[i][j] = (char)(num + '0' + 1); + dfs(idx + 1, board); + line[i][num] = column[j][num] = block[i / 3][j / 3][num] = false; + } + } + } +} \ No newline at end of file diff --git "a/Week_08/2 \347\232\204\345\271\202.java" "b/Week_08/2 \347\232\204\345\271\202.java" new file mode 100644 index 00000000..e38d0d54 --- /dev/null +++ "b/Week_08/2 \347\232\204\345\271\202.java" @@ -0,0 +1,11 @@ +/** + * 一个数如果是2的n次方 则二进制位中有且仅有一个1 + * + **/ +class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) + return false; + return (n & (n - 1)) == 0; + } +} \ No newline at end of file diff --git "a/Week_08/LRU \347\274\223\345\255\230\346\234\272\345\210\266.java" "b/Week_08/LRU \347\274\223\345\255\230\346\234\272\345\210\266.java" new file mode 100644 index 00000000..2cd5a0fe --- /dev/null +++ "b/Week_08/LRU \347\274\223\345\255\230\346\234\272\345\210\266.java" @@ -0,0 +1,78 @@ +class LRUCache { + class DLinkedNode { + int key; + int value; + DLinkedNode prev; + DLinkedNode next; + public DLinkedNode(){} + public DLinkedNode(int key, int value) { + this.key = key; + this.value = value; + } + } + private Map cache = new HashMap<>(); + private int size; + private int capacity; + private DLinkedNode head, tail; + public LRUCache(int capacity) { + size = 0; + this.capacity = capacity; + head = new DLinkedNode(); + tail = new DLinkedNode(); + head.next = tail; + tail.prev = head; + } + + public int get(int key) { + DLinkedNode node = cache.get(key); + if (node == null) + return -1; + moveToHead(node); + return node.value; + } + + public void put(int key, int value) { + DLinkedNode node = cache.get(key); + if (node == null) { + node = new DLinkedNode(key, value); + cache.put(key, node); + addToHead(node); + size++; + if (size > capacity) { + DLinkedNode tail = removeTail(); + cache.remove(tail.key); + size--; + } + } else { + node.value = value; + moveToHead(node); + } + } + private void moveToHead(DLinkedNode node) { + removeNode(node); + addToHead(node); + } + private void addToHead(DLinkedNode node) { + node.prev = head; + node.next = head.next; + head.next.prev = node; + head.next = node; + } + private void removeNode(DLinkedNode node) { + node.prev.next = node.next; + node.next.prev = node.prev; + } + private DLinkedNode removeTail() { + DLinkedNode node = tail.prev; + removeNode(node); + return node; + } + +} + +/** + * 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); + */ \ No newline at end of file diff --git "a/Week_08/N \347\232\207\345\220\216.java" "b/Week_08/N \347\232\207\345\220\216.java" new file mode 100644 index 00000000..1f8d5962 --- /dev/null +++ "b/Week_08/N \347\232\207\345\220\216.java" @@ -0,0 +1,40 @@ +/** + * 1 << n -1 将n个皇后二进制都设置为1 如8皇后则为 000...11111111 + * cols pie na 1的位置为标记为占用位, cols | pie | na 按位或运算获取所有占用位,取反后得到所有1标记为空余位,通过按位与(1 << n) - 1 + * 得到空余位置二进制表示方式如:0000... 11001110 目的为除n位置的高位都设置为0. + * position = aliPosition & (-aliPosition)得到最低位为1的表示方式代表可以放置皇后位置,aliPosition & (aliPosition - + * 1)去除最低位1(因为该位置已经标记放置皇后) + * cols | position, (pie | position) << 1, (na | position) >> 1 分别做与运算 代表三个记录位已经放置皇后 pie na位斜位需要左右移1 + **/ +class Solution { + public List> solveNQueens(int n) { + List> ans = new ArrayList<>(); + dfs(n, 0, 0, 0, 0, ans, new int[n]); + return ans; + } + + public void dfs(int n, int row, int cols, int pie ,int na, List> ans, int[] queens) { + if (row == n) { + ans.add(genAns(queens)); + return; + } + int aliPosition = ((1 << n) - 1) & (~(cols | pie | na)); + while (aliPosition != 0) { + int position = aliPosition & (-aliPosition); + aliPosition &= aliPosition - 1; + queens[row] = Integer.bitCount(position - 1); + dfs(n, row + 1, cols | position, (pie | position) << 1, (na | position) >> 1, ans, queens); + //queens[row] = -1; + } + } + public List genAns(int[] queens) { + List result = new ArrayList<>(); + for (int i = 0; i < queens.length; ++i) { + char[] row = new char[queens.length]; + Arrays.fill(row, '.'); + row[queens[i]] = 'Q'; + result.add(String.valueOf(row)); + } + return result; + } +} \ No newline at end of file diff --git "a/Week_08/N\347\232\207\345\220\216II.java" "b/Week_08/N\347\232\207\345\220\216II.java" new file mode 100644 index 00000000..696daf49 --- /dev/null +++ "b/Week_08/N\347\232\207\345\220\216II.java" @@ -0,0 +1,17 @@ +class Solution { + public int totalNQueens(int n) { + return dfs(n, 0, 0, 0, 0); + } + public int dfs(int n, int row, int cols, int pie, int na) { + if (n == row) + return 1; + int aliPosition = ((1 << n) - 1) & (~(cols | pie | na)); + int count = 0; + while (aliPosition != 0) { + int position = aliPosition & (-aliPosition); + aliPosition &= (aliPosition - 1); + count += dfs(n, row + 1, cols | position, (pie | position) << 1, (na | position) >> 1); + } + return count; + } +} \ No newline at end of file diff --git a/Week_08/README.md b/Week_08/README.md index 50de3041..05e7a7b4 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,104 @@ -学习笔记 \ No newline at end of file +##### 位运算 + 常用运算符:左移 << + 右移 >> + 按位与 & + 按位或 | + 按位异或 ^ + 按位取反 ~ + 高频位运算: X &(X - 1) 清零最低位的1 + X & -X 得到最低位的1 + X >> 1 等效 x/2 +##### 布隆过滤器 + 一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。 + + 优点是空间效率和查询时间都远远超过一般的算法, + 缺点是有一定的误识别率和删除困难。 + 应用,如:redis缓存、垃圾邮件评论过滤、爬虫URL判断重复等。 + + +##### 冒泡排序 + + // 冒泡排序,a表示数组,n表示数组大小 + public void bubbleSort(int[] a, int n) { + if (n <= 1) return; + + for (int i = 0; i < n; ++i) { + // 提前退出冒泡循环的标志位 + boolean flag = false; + for (int j = 0; j < n - i - 1; ++j) { + if (a[j] > a[j+1]) { // 交换 + int tmp = a[j]; + a[j] = a[j+1]; + a[j+1] = tmp; + flag = true; // 表示有数据交换 + } + } + if (!flag) break; // 没有数据交换,提前退出 + } + } +##### 插入排序 + + // 插入排序,a表示数组,n表示数组大小 + public void insertionSort(int[] a, int n) { + if (n <= 1) return; + + for (int i = 1; i < n; ++i) { + int value = a[i]; + int j = i - 1; + // 查找插入的位置 + for (; j >= 0; --j) { + if (a[j] > value) { + a[j+1] = a[j]; // 数据移动 + } else { + break; + } + } + a[j+1] = value; // 插入数据 + } + } +##### 选择排序 + public int[] selectionSort(int[] arr) { + int len = arr.length; + int minIndex, temp; + for (int i = 0; i < len - 1; i++) { + minIndex = i; + for (int j = i + 1; j < len; j++) { + if (arr[j] < arr[minIndex]) { // 寻找最小的数 + minIndex = j; // 将最小数的索引保存 + } + } + temp = arr[i]; + arr[i] = arr[minIndex]; + arr[minIndex] = temp; + } + return arr; + } + +##### 快速排序 + public static void quickSort(int[] array, int begin, int end) { if (end <= begin) return; + int pivot = partition(array, begin, end); + quickSort(array, begin, pivot - 1); + quickSort(array, pivot + 1, end); + } + + static int partition(int[] a, int begin, int end) { // pivot: 标杆位置,counter: 小于pivot的元素的个数int pivot = end, counter = begin; + for (int i = begin; i < end; i++) { + if (a[i] < a[pivot]) { + int temp = a[counter]; a[counter] = a[i]; a[i] = temp; counter++; + } + } + int temp = a[pivot]; a[pivot] = a[counter]; a[counter] = temp; + return counter; + } +##### 归并排序 + public static void merge(int[] arr, int left, int mid, int right) { int[] temp = new int[right - left + 1]; // 中间数组 + int i = left, j = mid + 1, k = 0; + while (i <= mid && j <= right) + temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; + while (i <= mid) + temp[k++] = arr[i++]; + + while (j <= right) temp[k++] = arr[j++]; + System.arraycopy(a, start1, b, start2, length); + + } diff --git "a/Week_08/\344\275\215 1 \347\232\204\344\270\252\346\225\260.java" "b/Week_08/\344\275\215 1 \347\232\204\344\270\252\346\225\260.java" new file mode 100644 index 00000000..484188c9 --- /dev/null +++ "b/Week_08/\344\275\215 1 \347\232\204\344\270\252\346\225\260.java" @@ -0,0 +1,15 @@ +/** + * n & (n - 1) 去掉二进制最低位1 统计去掉的个数 + * + **/ +public class Solution { + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int ans = 0; + while (n != 0) { + ans++; + n &= n - 1; + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_08/\345\220\210\345\271\266\345\214\272\351\227\264.java" "b/Week_08/\345\220\210\345\271\266\345\214\272\351\227\264.java" new file mode 100644 index 00000000..f0b5ec9c --- /dev/null +++ "b/Week_08/\345\220\210\345\271\266\345\214\272\351\227\264.java" @@ -0,0 +1,21 @@ +/** + * 以左区间为主进行排序,然后依次检查后序区间的右区间与最后一个检查过的的左区间比较,如果超过最后一个检查的区间的左区间 作为新区间,否则合并 + * + **/ +class Solution { + public int[][] merge(int[][] intervals) { + if (intervals.length == 0) + return new int[0][2]; + Arrays.sort(intervals, (o1, o2) -> o1[0] - o2[0]); + List ans = new ArrayList<>(); + for (int i = 0;i < intervals.length; ++i) { + int left = intervals[i][0], right = intervals[i][1]; + if (ans.size() == 0 || ans.get(ans.size() - 1)[1] < left) { + ans.add(new int[]{left, right}); + } else { + ans.get(ans.size() - 1)[1] = Math.max(right, ans.get(ans.size() - 1)[1]); + } + } + return ans.toArray(new int[ans.size()][]); + } +} \ No newline at end of file diff --git "a/Week_08/\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.java" "b/Week_08/\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.java" new file mode 100644 index 00000000..37a1410e --- /dev/null +++ "b/Week_08/\346\225\260\347\273\204\347\232\204\347\233\270\345\257\271\346\216\222\345\272\217.java" @@ -0,0 +1,33 @@ +/** + * + * 计数排序 因为数字不超过1000因此不会有较多内存消耗 + * 临时数组记录arr1中每个数字出现的次数,遍历arr2 将数字从临时数组中取出,已取出数字位个数标记为0 + * 将剩余数字从临时数组中取出 + * + **/ +class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int max = Integer.MIN_VALUE; + for (int num1 : arr1) { + max = Math.max(max, num1); + } + int[] temp = new int[max + 1]; + for (int num1 : arr1) { + temp[num1]++; + } + int[] ans = new int[arr1.length]; + int idx = 0; + for (int num2 : arr2) { + for (int i = 0; i < temp[num2]; ++i) { + ans[idx++] = num2; + } + temp[num2] = 0; + } + for (int i = 0; i < temp.length; ++i) { + for (int j = 0; j < temp[i]; ++j) { + ans[idx++] = i; + } + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_08/\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/Week_08/\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 00000000..45eb36b6 --- /dev/null +++ "b/Week_08/\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,16 @@ +/** + * 计数方式统计两个字符串的字符出现次数 s出现一次加1 t出现一次减1 26个字母数量为0则为异位词 + * + **/ +class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) + return false; + char[] cache = new char[26]; + for (int i = 0; i < s.length(); ++i) { + cache[s.charAt(i) - 'a']++; + cache[t.charAt(i) - 'a']--; + } + return String.valueOf(cache).trim().isBlank(); + } +} \ No newline at end of file diff --git "a/Week_08/\347\277\273\350\275\254\345\257\271.java" "b/Week_08/\347\277\273\350\275\254\345\257\271.java" new file mode 100644 index 00000000..9ef4ef1e --- /dev/null +++ "b/Week_08/\347\277\273\350\275\254\345\257\271.java" @@ -0,0 +1,33 @@ +/** + * 归并排序, 排序过程中统计翻转对数量 + * 合并排序时 左区间和右区间已经有序(从小到大),翻转对下标从左区间起始位置开始,如果该位置数字小于或等于2倍右区间比较数字,则移动一位;因为左区间从小到大有序 + * 因此当当前左区间数字大于右区间二倍当前数字,那么左区间后序的数字mid - i + 1个也会大于当前右区间2倍当前数字;同理右区间2倍当前数字如果大于当前左区间数字那么 + * 右区间当前2倍数字也大于左区间起始位置到当前位置的数字; + * + * + **/ +class Solution { + public int reversePairs(int[] nums) { + return mergeSort(nums, 0, nums.length - 1); + } + public int mergeSort(int[] nums, int lo, int hi) { + if (lo >= hi) + return 0; + int mid = (hi - lo) / 2 + lo; + int count = mergeSort(nums, lo, mid) + mergeSort(nums, mid + 1, hi); + int[] temp = new int[hi - lo + 1]; + int i = lo, t = lo, c = 0; + for (int j = mid + 1; j <= hi; ++j, ++c) { + while (i <= mid && nums[i] <= 2 * (long)nums[j]) + ++i; + while (t <= mid && nums[t] < nums[j]) + temp[c++] = nums[t++]; + temp[c] = nums[j]; + count += mid - i + 1; + } + while (t <= mid) + temp[c++] = nums[t++]; + System.arraycopy(temp, 0, nums, lo, hi - lo + 1); + return count; + } +} \ No newline at end of file diff --git "a/Week_08/\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.java" "b/Week_08/\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.java" new file mode 100644 index 00000000..37727602 --- /dev/null +++ "b/Week_08/\351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.java" @@ -0,0 +1,17 @@ +/** + * + * 答案数字初始化为0每次左移一位 然后加上原数最后一位 通过n & 1得到最后一位二进制位 + * 原数每次右移一位 即每次去掉最低位 + * + **/ +public class Solution { + // you need treat n as an unsigned value + public int reverseBits(int n) { + int ans = 0; + for (int i = 0; i < 32; ++i) { + ans = (ans << 1) + (n & 1); + n >>= 1; + } + return ans; + } +} \ No newline at end of file diff --git a/Week_09/README.md b/Week_09/README.md index 50de3041..6ee44b24 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,20 @@ -学习笔记 \ No newline at end of file +##### 常见动态规划问题递推公式 + 爬楼梯- f(n) = f(n - 1) + f(n - 2) , f(1) = 1, f(0) = 0 + 不同路径- f(x, y) = f(x-1, y) + f(x, y-1) + 打家劫舍- dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) + 最小路径和- dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + A[i][j] + 股票买卖- dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i]) + dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i]) +##### 高级字符串算法题目(DP结合) + 一般会创建一个二维数组,根据题目要求确定每个维度的含义。 + +##### 字符串匹配算法 + 1.暴力法 BF 时间复杂度O(mn) + 2.RK算法,以hash函数快速匹配字串 + 3.KMP算法 +##### RK算法思想 + 1.假设子串的长度为M(pat),目标字符串的长度为N(txt) + 2.计算子串的hash值hash_pat + 3.计算目标字符串txt中每个长度为M的字串的hash值 + 4.比较hash值:如果hash值不同,字符串必然不匹配;如果hash值相同,还需要使用朴素算法再次判断。 + diff --git "a/Week_09/\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.java" "b/Week_09/\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 00000000..ef4ccec0 --- /dev/null +++ "b/Week_09/\344\270\215\345\220\214\347\232\204\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,20 @@ +class Solution { + public int numDistinct(String s, String t) { + int m = t.length(); + int n = s.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 0; i < n; ++i) { + dp[0][i] = 1; + } + for (int i = 1; i <= m; ++i) { + for (int j = 1; j <= n; ++j) { + if (t.charAt(i - 1) == s.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + return dp[m][n]; + } +} \ No newline at end of file diff --git "a/Week_09/\344\273\205\344\273\205\345\217\215\350\275\254\345\255\227\346\257\215.java" "b/Week_09/\344\273\205\344\273\205\345\217\215\350\275\254\345\255\227\346\257\215.java" new file mode 100644 index 00000000..8a993b82 --- /dev/null +++ "b/Week_09/\344\273\205\344\273\205\345\217\215\350\275\254\345\255\227\346\257\215.java" @@ -0,0 +1,22 @@ +class Solution { + public String reverseOnlyLetters(String S) { + char[] arr = S.toCharArray(); + char[] ans = new char[arr.length]; + int idx = 0; + for (int i = arr.length - 1; i >= 0; --i) { + while (idx < arr.length && !Character.isLetter(arr[idx])) { + ans[idx] = arr[idx]; + idx++; + + } + if(Character.isLetter(arr[i])) { + ans[idx++] = arr[i]; + } + } + while (idx < arr.length) { + ans[idx] = arr[idx]; + idx++; + } + return String.valueOf(ans); + } +} \ No newline at end of file diff --git "a/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.java" "b/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.java" new file mode 100644 index 00000000..80060039 --- /dev/null +++ "b/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262 II.java" @@ -0,0 +1,14 @@ +class Solution { + public String reverseStr(String s, int k) { + char[] c = s.toCharArray(); + for (int i = 0; i < c.length; i += 2 * k) { + int begin = i, end = Math.min(c.length - 1, i + k - 1); + while (begin < end) { + char temp = c[begin]; + c[begin++] = c[end]; + c[end--] = temp; + } + } + return String.valueOf(c); + } +} \ No newline at end of file diff --git "a/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III.java" "b/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III.java" new file mode 100644 index 00000000..8852cca8 --- /dev/null +++ "b/Week_09/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\215\225\350\257\215 III.java" @@ -0,0 +1,16 @@ +class Solution { + public String reverseWords(String s) { + String[] arr = s.split(" +"); + for (int i = 0; i < arr.length; ++i) { + char[] crr = arr[i].toCharArray(); + int begin = 0, end = crr.length - 1; + while (begin < end) { + char c = crr[begin]; + crr[begin++] = crr[end]; + crr[end--] = c; + } + arr[i] = String.valueOf(crr); + } + return String.join(" ", arr); + } +} \ No newline at end of file diff --git "a/Week_09/\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.java" "b/Week_09/\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.java" new file mode 100644 index 00000000..4a4577bd --- /dev/null +++ "b/Week_09/\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\347\254\254\344\270\200\344\270\252\345\224\257\344\270\200\345\255\227\347\254\246.java" @@ -0,0 +1,17 @@ +/** + * 先统计每个字母出现次数 然后在一次遍历找到第一个出现次数为1的字符 + * + **/ +class Solution { + public int firstUniqChar(String s) { + int[] cache = new int[26]; + for (char c : s.toCharArray()) { + cache[c - 'a']++; + } + for (int i = 0; i < s.length(); ++i) { + if (cache[s.charAt(i) - 'a'] == 1) + return i; + } + return -1; + } +} \ No newline at end of file diff --git "a/Week_09/\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\225\264\346\225\260 (atoi).java" "b/Week_09/\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\225\264\346\225\260 (atoi).java" new file mode 100644 index 00000000..ffed9305 --- /dev/null +++ "b/Week_09/\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\225\264\346\225\260 (atoi).java" @@ -0,0 +1,24 @@ +class Solution { + public int myAtoi(String s) { + int ans = 0, begin = 0, symbol = 0; + while (begin < s.length() && s.charAt(begin) == ' ') + begin++; + for (int i = begin; i < s.length(); ++i) { + char c = s.charAt(i); + if ((c == '+' || c == '-') && symbol == 0) { + symbol = (c == '+' ? 1 : -1); + } else if (Character.isDigit(c)) { + if (symbol == 0) + symbol = 1; + if (ans > (Integer.MAX_VALUE - (c - '0')) / 10) + return symbol == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + ans = ans * 10 + (c - '0'); + } else if (ans == 0) { + return 0; + } else { + break; + } + } + return ans * symbol; + } +} \ No newline at end of file diff --git "a/Week_09/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" "b/Week_09/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" new file mode 100644 index 00000000..7695239a --- /dev/null +++ "b/Week_09/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215.java" @@ -0,0 +1,30 @@ +class Solution { + public List findAnagrams(String s, String p) { + List ans = new ArrayList<>(); + if (p.length() > s.length()) + return ans; + int[] c1 = new int[26]; + int[] c2 = new int[26]; + for (int i = 0; i < p.length(); ++i) { + c1[s.charAt(i) - 'a']++; + c2[p.charAt(i) - 'a']++; + } + if (valid(c1, c2)) + ans.add(0); + int begin = 0, end = p.length() - 1; + while (end < s.length() - 1) { + c1[s.charAt(begin++) - 'a']--; + c1[s.charAt(++end) - 'a']++; + if (valid(c1, c2)) + ans.add(begin); + } + return ans; + } + private boolean valid(int[] c1, int[] c2) { + for (int i = 0; i < 26; ++i) { + if (c1[i] != c2[i]) + return false; + } + return true; + } +} \ No newline at end of file diff --git "a/Week_09/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" "b/Week_09/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" new file mode 100644 index 00000000..6c6c1e33 --- /dev/null +++ "b/Week_09/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227.java" @@ -0,0 +1,15 @@ +class Solution { + public int lengthOfLIS(int[] nums) { + int[] dp = new int[nums.length]; + Arrays.fill(dp, 1); + int ans = 0; + for (int i = 0; i < nums.length; ++i) { + for (int j = 0; j < i; ++j) { + if (nums[i] > nums[j]) + dp[i] = Math.max(dp[i], dp[j] + 1); + } + ans = Math.max(dp[i], ans); + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_09/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" "b/Week_09/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" new file mode 100644 index 00000000..be93b63a --- /dev/null +++ "b/Week_09/\346\234\200\351\225\277\345\233\236\346\226\207\345\255\220\344\270\262.java" @@ -0,0 +1,15 @@ +class Solution { + public String longestPalindrome(String s) { + String ans = ""; + int n = s.length(); + boolean[][] dp = new boolean[n][n]; + for (int i = n - 1; i >= 0; --i) { + for (int j = i; j < n; ++j) { + dp[i][j] = (s.charAt(i) == s.charAt(j)) && (j - i < 2 || dp[i + 1][j - 1]); + if (dp[i][j] && j - i + 1 > ans.length()) + ans = s.substring(i, j + 1); + } + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" "b/Week_09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" new file mode 100644 index 00000000..6983453c --- /dev/null +++ "b/Week_09/\346\234\200\351\225\277\346\234\211\346\225\210\346\213\254\345\217\267.java" @@ -0,0 +1,18 @@ +class Solution { + public int longestValidParentheses(String s) { + int n = s.length(); + int[] dp = new int[n]; + int ans = 0; + for (int i = 1; i < n; ++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 ; + } + ans = Math.max(dp[i], ans); + } + } + return ans; + } +} \ No newline at end of file diff --git "a/Week_09/\347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.java" "b/Week_09/\347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.java" new file mode 100644 index 00000000..56b3ab7a --- /dev/null +++ "b/Week_09/\347\277\273\350\275\254\345\255\227\347\254\246\344\270\262\351\207\214\347\232\204\345\215\225\350\257\215.java" @@ -0,0 +1,7 @@ +class Solution { + public String reverseWords(String s) { + String[] c = s.trim().split(" +"); + Collections.reverse(Arrays.asList(c)); + return String.join(" ", c); + } +} \ No newline at end of file diff --git "a/Week_09/\350\247\243\347\240\201\346\226\271\346\263\225.java" "b/Week_09/\350\247\243\347\240\201\346\226\271\346\263\225.java" new file mode 100644 index 00000000..895ad0c3 --- /dev/null +++ "b/Week_09/\350\247\243\347\240\201\346\226\271\346\263\225.java" @@ -0,0 +1,18 @@ +class Solution { + public int numDecodings(String s) { + if (s.length() == 0 || s.charAt(0) == '0') + return 0; + int n = s.length(); + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = 1; + for (int i = 1; i < n; ++i) { + if (s.charAt(i) != '0') + dp[i + 1] = dp[i]; + int num = 10 * (s.charAt(i - 1) - '0') + (s.charAt(i) - '0'); + if (num >= 10 && num <= 26) + dp[i + 1] += dp[i - 1]; + } + return dp[n]; + } +} \ No newline at end of file diff --git "a/Week_09/\351\252\214\350\257\201\345\233\236\346\226\207\345\255\227\347\254\246\344\270\262 \342\205\241.java" "b/Week_09/\351\252\214\350\257\201\345\233\236\346\226\207\345\255\227\347\254\246\344\270\262 \342\205\241.java" new file mode 100644 index 00000000..05d67b37 --- /dev/null +++ "b/Week_09/\351\252\214\350\257\201\345\233\236\346\226\207\345\255\227\347\254\246\344\270\262 \342\205\241.java" @@ -0,0 +1,22 @@ +class Solution { + public boolean validPalindrome(String s) { + char[] c = s.toCharArray(); + int i = 0, j = c.length - 1; + while (i < j) { + if (c[i] != c[j]) { + return valid(Arrays.copyOfRange(c, i + 1, j + 1)) || valid(Arrays.copyOfRange(c, i, j)); + } + i++; + j--; + } + return true; + } + public boolean valid(char[] c) { + int i = 0, j = c.length - 1; + while (i < j) { + if (c[i++] != c[j--]) + return false; + } + return true; + } +} \ No newline at end of file diff --git a/Week_10/README.md b/Week_10/README.md index 50de3041..2323277a 100644 --- a/Week_10/README.md +++ b/Week_10/README.md @@ -1 +1,13 @@ -学习笔记 \ No newline at end of file + 我是算法训练营第17期的学员,虽有6年的编程经验,但由于上学时并没有好好学习数据结构课程,参加工作后日常开发也不会用到过多算法知识,因此”算法“这个知识点一直处于一知半解的程度。在算法训练营经过2个月的训练,对于常用的算法有个更深层次的认知,更重要的是掌握了leetcode近300个题目的解法,这让我有了一个小小的成就感。2个月时间是短暂的,但是收获的知识却是巨大的,以下是参加训练营后的一些心得体会。 +### 算法之路 + 对于学习这件事情,我并不排斥,从参加工作每天都会看一点有关技术的文章或者文字片段,学了很多也忘了很多,技术书籍经过几年积累也有近30本其中就包括算法的书籍《算法图解》、《数据结构与算法分析》、《算法导论》、《算法4》。自己也知道算法和计算机基础理论这类知识一样属于程序员修炼的内功,对于深入学习其他知识如Redis有着很重要的作用,但是最后这些算法书籍的阅读基本都止步于30页左右知识点大概是树、图这些,本来算法知识就差加上书中有些知识点描述晦涩难懂,枯燥的学习一段时间后就统统放弃了,直到遇到算法训练营又有了攻克算法的想法。 +### 算法训练营 + 极客时间是我经常登录的一个APP,忘记了什么时候出现了一个算法训练营的广告,第0期标价2000多,线上教育?学什么?不知道。后来推出了一个9块9的试看专栏,价格还可以买来看了一下感觉和书里说的也没差别,再然后学了半截又被我扔到一边。偶然的一次机会看到了超哥的直播大概是14期左右,讲到了算法学习的方法论、学员的收获,我认真的听完了超哥的讲述觉得挺有意思,后面超哥的直播基本都会去看看,然后心里冒出一个念头,学!查看教学大纲目录、翻出书籍、打开leetcode准备了3期报名了第17期,现在看来当时的选择是正确的。 +### 时间 + 算法训练营的授课时间只有两个月期间要学习大量的知识,做大量的练习,毕竟像我这种”大龄程序员“对于时间还是有些紧迫,早上要做早餐、上班要处理工作事情、在家要陪孩子玩耍,空闲时间只有上下班地铁,午休、晚上10点-12点的时间,还好坚持了过来,虽然劳累但是收获也是很多的。 +### 学习方法 + 学习过程中严格遵循了超哥的五毒神掌刷题法,实践证明确实很好用。除此之外对于每个题目的解法要做到理解透彻,记住题解的框架比记住一个个字母数字要重要的多,对于同类型的题目最好时常归类总结毕竟解法都有想通的地方。多反复练习,虽然粗暴但是有效,有难度题解默写完毕后可以间隔10分钟、半小时、一小时再默写一遍直到对解题的脉路熟练为止,不要怕过程繁琐。对于题解中的边界条件、判断分支多思考一下为什么要这样做,换一下写法或者不加判断行不行,多去动手操作,这样更有助于加深对题目的理解和题解的记忆。 +### 收获 + 除了学到的算法知识,更重要的是学到了学习方法,通过超哥的”无毒神掌“刷题法,使我对学习知识的方法有了一些改进,勤于动手往往更重要,对于学过的知识要时刻整理记录便于日后查看加深记忆。现在学习其他知识我也是遵守这样的法则,而且学习效果比之前要好的多。 + 至此算法训练营的课程已经结束,现在每天刷两个题目一来是巩固算法知识,另外就是锻炼自己的思维能力,毕竟解题过程还是挺有意思的。 + 最后感谢算法训练营、感谢超哥、感谢班班,学习之路继续加油!