From f610191d00d21a6b9cd312548ea641e26f4ff11b Mon Sep 17 00:00:00 2001 From: langming Date: Sun, 27 Sep 2020 22:52:59 +0800 Subject: [PATCH 01/12] week1 --- Week_01/Algorithm.java | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Week_01/Algorithm.java diff --git a/Week_01/Algorithm.java b/Week_01/Algorithm.java new file mode 100644 index 00000000..00bf3070 --- /dev/null +++ b/Week_01/Algorithm.java @@ -0,0 +1,60 @@ + +class Solution { + public int climbStairs(int n) { + if(n <=2){ + return n; + } + int f1=1; + int f2=2; + int f3=3; + for(int i=3;i<=n;i++){ + f3= f1+f2; + f1= f2; + f2= f3; + } + return f3; + } +} + + +class Solution { + public int[] plusOne(int[] digits) { + for(int i=digits.length -1 ;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; + } +} + +class Solution { + public int maxArea(int[] height) { + int i=0, j=height.length-1, res=0; + while(i Date: Sun, 27 Sep 2020 23:43:15 +0800 Subject: [PATCH 02/12] =?UTF-8?q?=E6=80=BB=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Week_01/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..96273972 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,12 @@ -学习笔记 \ No newline at end of file +## 算法知识总结 + +- 前几节,感觉学到了不错的学习方法,触类旁通,可以用在其他技能学习。包括:遵循记忆周期的五遍刷题法,刻意练习,主动反馈等 + +- 在学习时间复杂度和空间复杂度一节前,也学习过复杂度,但是知识不够系统,结合课程中的实例又学习了一遍,印象更加深刻了 +- 数组,链表之前也比较熟悉,跳表之前没有接触过,学习掌握了跳表的原理和实际应用 +- 栈和队列视频看了一遍,视频中的作业还没来得及做,接下来要补下。之前也有用队列实现树的层次遍历,用来实现社交关系节点的可视化功能 +- 之前刷题很少,觉得比较难。这周连刷四题,结合超哥的刷题思路,看高分题解,收获感觉还是比较大的。(应该连刷7题的,木有坚持住,后面再接再励,加油!!!) + - 爬梯子题目中找重复子问题 + - 移动零题目中借鉴快排思路,以0为中间点,把不等于0的放左边,等于0的放右边 + - 盛水最多的容器题目中,**既有巧妙的解题思路**,比如:两边向中间收敛,水槽的短板取决于最短的那一块,长板向内移动,即使碰到更长的长板,也不会影响水槽短板的长度,相反如果短板向内移动,碰到更短的短板,则水槽的短板会变短。**也有比较严谨的证明**:每次向内移动短板,所有的消去状态都不会导致丢失面积最大值 。 + From c36ae88e0d97895ee937d83d585f0e731ffa368c Mon Sep 17 00:00:00 2001 From: itchanges Date: Mon, 28 Sep 2020 00:00:03 +0800 Subject: [PATCH 03/12] Update README.md --- Week_01/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Week_01/README.md b/Week_01/README.md index 96273972..bab43c44 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -3,7 +3,7 @@ - 前几节,感觉学到了不错的学习方法,触类旁通,可以用在其他技能学习。包括:遵循记忆周期的五遍刷题法,刻意练习,主动反馈等 - 在学习时间复杂度和空间复杂度一节前,也学习过复杂度,但是知识不够系统,结合课程中的实例又学习了一遍,印象更加深刻了 -- 数组,链表之前也比较熟悉,跳表之前没有接触过,学习掌握了跳表的原理和实际应用 +- 数组,链表之前也比较熟悉,跳表之前没有接触过,学习了跳表的原理,有序链表上加多级索引,时间复杂度logN,了解了跳表的实际应用场景 - 栈和队列视频看了一遍,视频中的作业还没来得及做,接下来要补下。之前也有用队列实现树的层次遍历,用来实现社交关系节点的可视化功能 - 之前刷题很少,觉得比较难。这周连刷四题,结合超哥的刷题思路,看高分题解,收获感觉还是比较大的。(应该连刷7题的,木有坚持住,后面再接再励,加油!!!) - 爬梯子题目中找重复子问题 From 74a3823e57d430c322b6ed0930ee02b913630486 Mon Sep 17 00:00:00 2001 From: itchanges Date: Mon, 28 Sep 2020 00:07:45 +0800 Subject: [PATCH 04/12] Update README.md --- Week_01/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Week_01/README.md b/Week_01/README.md index bab43c44..f2640556 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -3,10 +3,9 @@ - 前几节,感觉学到了不错的学习方法,触类旁通,可以用在其他技能学习。包括:遵循记忆周期的五遍刷题法,刻意练习,主动反馈等 - 在学习时间复杂度和空间复杂度一节前,也学习过复杂度,但是知识不够系统,结合课程中的实例又学习了一遍,印象更加深刻了 -- 数组,链表之前也比较熟悉,跳表之前没有接触过,学习了跳表的原理,有序链表上加多级索引,时间复杂度logN,了解了跳表的实际应用场景 +- 数组,链表之前也比较熟悉,跳表没什么印象,学习了跳表的原理,有序链表上加多级索引,时间复杂度logN,了解了跳表的实际应用场景 - 栈和队列视频看了一遍,视频中的作业还没来得及做,接下来要补下。之前也有用队列实现树的层次遍历,用来实现社交关系节点的可视化功能 - 之前刷题很少,觉得比较难。这周连刷四题,结合超哥的刷题思路,看高分题解,收获感觉还是比较大的。(应该连刷7题的,木有坚持住,后面再接再励,加油!!!) - 爬梯子题目中找重复子问题 - 移动零题目中借鉴快排思路,以0为中间点,把不等于0的放左边,等于0的放右边 - 盛水最多的容器题目中,**既有巧妙的解题思路**,比如:两边向中间收敛,水槽的短板取决于最短的那一块,长板向内移动,即使碰到更长的长板,也不会影响水槽短板的长度,相反如果短板向内移动,碰到更短的短板,则水槽的短板会变短。**也有比较严谨的证明**:每次向内移动短板,所有的消去状态都不会导致丢失面积最大值 。 - From 303291e8bbf2a7e324aa7b6d951d88ad0e82345d Mon Sep 17 00:00:00 2001 From: langming Date: Wed, 14 Oct 2020 23:16:56 +0800 Subject: [PATCH 05/12] t --- Week_02/Algorithm.java | 106 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Week_02/Algorithm.java diff --git a/Week_02/Algorithm.java b/Week_02/Algorithm.java new file mode 100644 index 00000000..81fe201a --- /dev/null +++ b/Week_02/Algorithm.java @@ -0,0 +1,106 @@ +package com.ggj.center.member.core.api; + +public class Algorithm { + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + //递归到p,q或叶子节点终止 + if (root == null || root.val == p.val || root.val == q.val) { + return root; + } + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + //暗含了left,right都为null的情况 + if (left == null) { + return right; + } + // + if (right == null) { + return left; + } + return root; + } + + public int[] maxSlidingWindow(int[] nums, int k) { + //这个判断不能少,不然提交leetcode可能会报错 + if (nums == null || nums.length == 0 || k == 0) { + return new int[0]; + } + + Deque deque = new LinkedList(); + int[] res = new int[nums.length - k + 1]; + for (int i = 0; i < nums.length; i++) { + while (!deque.isEmpty() && deque.peekLast() < nums[i]) { + deque.removeLast(); + } + deque.addLast(nums[i]); + + if (i >= k - 1) { + res[i - k + 1] = deque.peekFirst(); + //队列的第一个元素可能在上次窗口移动时已经被干掉了,所以要判断下 + //把移除窗口最左侧元素放到代码最后,这样就可以把index为2和index>2两种情况合并成一种情况处理 + if (deque.peekFirst() == nums[i - k + 1]) { + deque.removeFirst(); + } + } + } + return res; + } + + public int[] twoSum(int[] nums, int target) { + Map map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + return new int[]{i, map.get(target - nums[i])}; + } + map.put(nums[i], i); + } + return new int[]{}; + } + + public List> threeSum(int[] nums) { + List> res = new ArrayList>(); + //基础校验,小于3 return + if (nums == null || nums.length < 3) { + return res; + } + //排序 + Arrays.sort(nums); + for (int i = 0; i < nums.length; i++) { + //如果nums[i]大于0,return + if (nums[i] > 0) { + return res; + } + //如果碰到重复,continue + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + int left = i + 1; + int right = nums.length - 1; + while (left < right) { + //如果三数之和等于0,设置res,L++, R--, 判断重复 + int sum = nums[i] + nums[left] + nums[right]; + if (sum == 0) { + res.add(Arrays.asList(nums[i], nums[left], nums[right])); + } + if (sum <= 0) { + left++; + //如果跟上一个数相同,继续向前一步 + //这里left < right容易忽视 + while (left < right && nums[left] == nums[left - 1]) { + left++; + } + } + if (sum >= 0) { + right--; + //这里判断下,避免right索引超界 + while (left < right && right < nums.length - 1 && nums[right] == nums[right + 1]) { + right--; + } + } + } + } + return res; + + } + +} From 114a6a1b5614b43a42f582f4e56ee194aa6686ec Mon Sep 17 00:00:00 2001 From: itchanges Date: Wed, 14 Oct 2020 23:43:04 +0800 Subject: [PATCH 06/12] Update README.md --- Week_02/README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..f7abcd6f 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,12 @@ -学习笔记 \ No newline at end of file +## 算法知识总结 + +- 国庆假期在外面玩,基本没学习。国庆节后回来,已经完全没了状态,作业也是到10.14才补上。 +- 学习视频 + - hashmap做为常考点,比较熟悉。hashset是用hashmap实现的,这点之前没注意到 + - 熟悉了二叉搜索树(查询,添加,删除都是logN),二叉堆(堆顶查询O(1), 添加,删除logN)。类似这些数据结构,学习了容易忘,这次系统学习后希望不会忘 + - 图这块讲的比较少,其实这块还是比较感兴趣。之前有了解过图数据库,但是不够深入 +- 刷题 + - 二叉树的最近公共祖先: 巩固了下二叉树的后续遍历 + - 滑动窗口的最大值: 学习了单调队列这种数据结构,感觉比较巧妙 + - 两数之和:借助hashmap 解决问题 + - 三数之和: 这个算法看的时间挺长的,解法也比较巧妙。如果面试碰到,感觉不太好讲 From 1f95b4e742691b653bd46278647c832d8c784eea Mon Sep 17 00:00:00 2001 From: itchanges Date: Sun, 18 Oct 2020 23:46:50 +0800 Subject: [PATCH 07/12] Create Algorithm.java --- Week_03/Algorithm.java | 1 + 1 file changed, 1 insertion(+) create mode 100644 Week_03/Algorithm.java diff --git a/Week_03/Algorithm.java b/Week_03/Algorithm.java new file mode 100644 index 00000000..e64735c4 --- /dev/null +++ b/Week_03/Algorithm.java @@ -0,0 +1 @@ +Algorithm From de7757d6b46220bf7cb254a1c45eac6a4075b605 Mon Sep 17 00:00:00 2001 From: itchanges Date: Wed, 21 Oct 2020 22:14:03 +0800 Subject: [PATCH 08/12] Update Algorithm.java --- Week_03/Algorithm.java | 69 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/Week_03/Algorithm.java b/Week_03/Algorithm.java index e64735c4..153f17b5 100644 --- a/Week_03/Algorithm.java +++ b/Week_03/Algorithm.java @@ -1 +1,68 @@ -Algorithm + +//组合 +public List> combine(int n, int k){ + List> res = new ArrayList(); + if(k<=0 || n paths = new ArrayDeque(); + dfs(n,k,1,paths,res); + return res; + } + public void dfs(int n, int k, int begin,Deque paths, List> res){ + // 递归终止条件:path.size = k + if(paths.size() == k ){ + res.add(new ArrayList(paths)); + return; + } + for(int i=begin;i<=n;i++){ + paths.addLast(i); + //因为是组合,每下探一层i+1,避免有重复元素 + dfs(n,k,i+1,paths,res); + //递归恢复后,重置到递归前的状态,继续for循环 + paths.removeLast(); + } + + } + + + + +//全排列 + public List> permute(int[] nums) { + //res初始化 + List> res = new ArrayList(); + //校验 + if(nums.length == 0){ + return res; + } + //path链路中已使用标记下,这个used也是比较巧妙的 + boolean[] used = new boolean[nums.length]; + //paths初始化 + Deque paths = new ArrayDeque(); + dfs(nums,used,0,paths,res); + //dfs遍历 + return res; + } + + public void dfs(int[] nums, boolean[] used, int deepth, Deque paths, List> res){ + //退出条件 + if( deepth == nums.length){ + //这里注意,新建一个list,因为paths会被多个地方引用到,会被修改 + res.add(new ArrayList(paths)); + return; + } + //处理当前层 + for(int i=0;i Date: Wed, 21 Oct 2020 22:34:12 +0800 Subject: [PATCH 09/12] Update README.md --- Week_03/README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..3cacb5be 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,28 @@ -学习笔记 \ No newline at end of file +## 算法知识总结 + +##### 分治 +- 分治的本质是递归 + - 非要说区别:分治多了一步: 合并子结果 + - 递归就是用到栈,所以跟栈也类似 + +- 碰到一个题目去找它的重复性 + - 重复性有最近重复性,最优重复性; + - 最优重复性就是动态规划; + - 最近重复性:根据怎么构造,以及怎么分解,就有分治,回溯或者其他各种办法,本质上就是一种递归 + +##### 回溯 +- 用于求解多阶段决策问题。多阶段决策问题即: + - 求解一个问题分为很多步骤(阶段); + - 每一个步骤(阶段)可以有多种选择。 +- **剪枝** + - 回溯算法中遍历所有可能情况算法复杂度高,合理使用剪枝可以很大程度降低复杂性 +- 深度优先遍历vs 广度优先遍历 + - 为什么回溯算法不使用广度优先遍历,深度优先可以借助程序栈保存一些信息,基本都有深度优先 + + +##### 刷题 +- 组合:每下探一层i+1,避免有重复元素; 剪枝可以比较大缩短时间,这块还没来及细看 +- 全排列:used数组使用比较巧妙 + +##### 心路历程 +- 脑图没有进展,五毒进展不大,算法作业又是拖到了week4周三,周末动力不足哈,抽出小半天学了会儿 From c737975246b91d3bac6858927acab42b1e6a1984 Mon Sep 17 00:00:00 2001 From: itchanges Date: Mon, 26 Oct 2020 17:40:53 +0800 Subject: [PATCH 10/12] Create Algorithm.java --- Week_04/Algorithm.java | 1 + 1 file changed, 1 insertion(+) create mode 100644 Week_04/Algorithm.java diff --git a/Week_04/Algorithm.java b/Week_04/Algorithm.java new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Week_04/Algorithm.java @@ -0,0 +1 @@ + From 2939b2ad9a14607b8047828e145604b2f7f1d898 Mon Sep 17 00:00:00 2001 From: itchanges Date: Fri, 30 Oct 2020 23:15:01 +0800 Subject: [PATCH 11/12] Update Algorithm.java --- Week_04/Algorithm.java | 192 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/Week_04/Algorithm.java b/Week_04/Algorithm.java index 8b137891..80e46422 100644 --- a/Week_04/Algorithm.java +++ b/Week_04/Algorithm.java @@ -1 +1,193 @@ +//柠檬水找零 +public boolean lemonadeChange(int[] bills) { + int five = 0; + int ten = 0; + for(int bill : bills){ + if(bill == 5){ + five++; + }else if(bill == 10){ + if(five > 0){ + five--; + ten++; + }else{ + return false; + } + }else if(bill == 20){ + if(five > 0 && ten > 0 ){ + five--; + ten--; + }else if(five >= 3){ + five = five - 3; + }else{ + return false; + } + } + } + return true; + } + + + +//单词接龙1 +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if(wordList == null || wordList.size()==0){ + return 0; + } + //广度优先遍历,找最短路径 + //组织一个hashmap,key是带通配符的单词,示例:*og, d*g, do* + //然后使用队列层次遍历 + Map> patternMap = new HashMap(); + //记录访问过的节点,避免重复 + Set visitedWord = new HashSet(); + //所有单词的长度相同 + int wordLength = wordList.get(0).length(); + for(String word : wordList){ + for(int i=0;i matchPatternWords = patternMap.getOrDefault(wordPattern, new ArrayList()); + matchPatternWords.add(word); + patternMap.put(wordPattern,matchPatternWords); + } + } + + Queue queue = new LinkedList(); + //最短转换序列,是包含起始节点的,所以从1开始 + queue.add( new WordObj(beginWord,1)); + visitedWord.add(beginWord); + while( ! queue.isEmpty()){ + WordObj wordObj1 = queue.remove(); + int level = wordObj1.getLevel(); + String word1 = wordObj1.getWord(); + + for(int i=0;i matchPatternWords = patternMap.get(wordPattern); + if(matchPatternWords == null || matchPatternWords.size() == 0 ){ + continue; + } + for(String matchPatternWord : matchPatternWords ){ + if(matchPatternWord.equals(endWord)){ + return level+1; + } + if( ! visitedWord.contains(matchPatternWord)){ + visitedWord.add(matchPatternWord); + queue.add(new WordObj(matchPatternWord, level+1)); + } + } + } + } + return 0; + } + class WordObj { + private String word; + private int level; + + public WordObj(String word,int level){ + this.word = word; + this.level = level; + } + public String getWord(){ + return word; + } + public int getLevel(){ + return level; + } + } +} + + + +//单词接龙2 +class Solution { + public List> findLadders(String beginWord, String endWord, List wordList) { + //校验wordList是否包含endWord + if( ! wordList.contains(endWord)){ + return new ArrayList(); + } + //记录结果 + List> res = new ArrayList(); + //return bfs(beginWord,endWord,wordList); + //bfs + //因为要记录路径,所以队列元素类型是List + Queue> q = new LinkedList(); + //创建访问记录,避免重复访问(出现重复访问,路径肯定不是最短的,这里要最短路径) + Set visited = new HashSet(); + //创建set,获取下层节点(getNeighbours)时,判断是否存在于wordList + Set dict = new HashSet(); + dict.addAll(wordList); + //当前层是否存在最短路径,如果存在,当前层遍历完后就结束while循环 (所有最短路径都在同一层) + boolean isFound = false; + //添加首个元素 + List firstPath = new ArrayList(); + firstPath.add(beginWord); + q.add(firstPath); + visited.add(beginWord); + while( ! q.isEmpty()){ + //每层首个元素开始,计算size,然后for循环遍历当前层的元素 + int size = q.size(); + //当前层处理后,再统一放到visited集合 + Set subVisited = new HashSet(); + for(int i=0; i< size; i++){ + List path1 = q.remove(); + //获取末尾的元素 + String node1 = path1.get(path1.size()-1); + List neighbours = getNeighbours(node1,dict); + for(String neighbour : neighbours){ + //在所有的最短路径中,一个元素不可能同时出现在不同层 + if( ! visited.contains(neighbour)){ + subVisited.add(neighbour); + //匹配到结果 + if(neighbour.equals(endWord)){ + isFound = true; + path1.add(neighbour); + //拷贝元素,避免引用修改等问题 + res.add(new ArrayList(path1)); + }else{ + //在list中添加元素,等path1塞到队列后,再把末尾元素移除; + path1.add(neighbour); + } + q.add(new ArrayList(path1)); + //恢复状态,继续下一个neighbour处理 + path1.remove(path1.size()-1); + } + } + } + //当前层遍历完后,subVisited放到visited + visited.addAll(subVisited); + if(isFound){ + break; + } + } + return res; + } + + /** + * 获取所有相邻元素(字母相差一位的元素) + */ + private List getNeighbours(String word, Set dict ){ + List res = new ArrayList(); + for(char i='a';i<='z';i++){ + //word转char数组 + char[] chs = word.toCharArray() ; + for(int j=0;j Date: Fri, 30 Oct 2020 23:42:34 +0800 Subject: [PATCH 12/12] Update README.md --- Week_04/README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..b4df6c8b 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,33 @@ -学习笔记 \ No newline at end of file +##### 深度优先 + +- 除了深度优先,广度优先,还有“优先级优先”,“优先级优先”更适合生活场景,比如:推荐算法 + + +##### 广度优先 + +- 广度优先搜索是一种用于图的查找算法,可查找最短路径 + +- "单词接龙2"中的广度优先遍历, 每次处理一层, 是解决问题的一个小技巧 + +##### 贪心,回溯,动态规划比较 + +- 贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能 + +- 贪心,回溯,动态规划比较 + + - 贪心:当下做局部最优判断 + + - 回溯:能够回退 + + - 动态规划:最优判断 + 回退 + + +##### 刷题 +- 柠檬水找零: 相对比较简单,容易理解. 贪心算法的应用 +- 单词接龙: 广度优先的应用 +- 单词接龙2: 有点难,在广度优先上的基础上做了一些改变, 花的时间多. 刚开始在"单词接龙"的基础上写,发现跑超时了. 最后参考了优秀题解又重新做了一遍. + + +##### 心路历程 +- 感觉这周能沉下心来刷题了 +- 五毒方法还要坚持,期中直播感觉之前学的都忘的差不多啦