diff --git a/Week01/NOTE.md b/Week01/NOTE.md index 50de30414..16966ae81 100644 --- a/Week01/NOTE.md +++ b/Week01/NOTE.md @@ -1 +1,5 @@ -学习笔记 \ No newline at end of file +学习笔记 +第一周的学习内容:数组、链表、跳表、栈、队列、双端队列、循环队列 +第一周的练习中,学会了解题思路的突破,不会只想一种方法来解题,而是想几种方法,寻找最优的解题思路,并背诵。 +学会了5次做题法。 +经过一周的训练,算法的思路有了极大的提升。 diff --git a/Week01/first.java b/Week01/first.java new file mode 100644 index 000000000..ecc20aeee --- /dev/null +++ b/Week01/first.java @@ -0,0 +1,49 @@ + +//题目:旋转数组 + + +//1.暴力法思路(目前我的解题思路只有这种):数组向右移动,每移动一次,最后位置的元素都放到0位置上,因此,此算法的时间复杂度为O(n*k);空间复杂度为O(1)符合题干 +class Solution { + public void rotate(int[] nums, int k) { + int temp, pre; + for(int i = 0; i< k;i++){ + pre = nums[nums.length-1];//每向右移动一位,都要指向最后一个位置 + for(int j = 0; j < nums.length; j++){//数组的元素每向右移动一次,整个数组的元素都要往后移动一位,最后一位数组的元素放到第一位 + //交换位置 + temp = nums[j]; + nums[j]= pre; + pre = temp; + } + } + } +} + + +//2.使用反转(背诵的解题方法) +//原理: +//原始数组 : 1 2 3 4 5 6 7 +//反转所有数字后 : 7 6 5 4 3 2 1 +//反转前 k 个数字后 : 5 6 7 4 3 2 1 +//反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果 + +public class Solution { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1);//反转整个数组 + reverse(nums, 0, k - 1);//反转前k个数组 + reverse(nums, k, nums.length - 1);//反转后K个数组 + } + //反转方法 + //nums 数组 + //start:开始位置 + //end:结束位置 + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} \ No newline at end of file diff --git a/Week01/second.java b/Week01/second.java new file mode 100644 index 000000000..145c91d42 --- /dev/null +++ b/Week01/second.java @@ -0,0 +1,21 @@ + +//题目: 删除排序数组中的重复项 + + + +//暴力方法(方法可行,但是不满足题干):1.使用两个数组,一个数组是原来的数组,一个是新的数组 +//双指针法:(快慢指针) + +class Solution { + + public int removeDuplicates(int[] nums) { + int i=0; + for(int j = 1;j inorderTraversal(TreeNode root){ + List res = new ArrayList<>(); + helper(root,res); + return res; + } + + public void helper(TreeNode root, List res){ + if (root == null) { + if (root.left == null) { + helper(root.left, res); + } + res.add(root.val); + if (root.right == null) { + helper(root.right, res); + } + } + + } + +} diff --git a/Week03/NOTE.md b/Week03/NOTE.md index 50de30414..3b94a5b90 100644 --- a/Week03/NOTE.md +++ b/Week03/NOTE.md @@ -1 +1,11 @@ -学习笔记 \ No newline at end of file +第三周学习笔记 +本周学习了递归,学会了递归模板: +1. recursion terminator(递归终止条件) +2. process logic in current level (处理当前逻辑层) +3. drill down (下探到下一层) +4. reverse the current level status if needed (清理当前层) + +学习了分治思想及回溯 + + + diff --git a/Week03/first.java b/Week03/first.java new file mode 100644 index 000000000..bf2c2dc11 --- /dev/null +++ b/Week03/first.java @@ -0,0 +1,32 @@ + +//题目:组合 +//回溯法 + +class Solution { + List> output = new LinkedList(); + int n; + int k; + + public void backtrack(int first, LinkedList curr) { + // if the combination is done + if (curr.size() == k) + output.add(new LinkedList(curr)); + + for (int i = first; i < n + 1; ++i) { + // add i into the current combination + curr.add(i); + // use next integers to complete the combination + backtrack(i + 1, curr); + // backtrack + curr.removeLast(); + } + } + + public List> combine(int n, int k) { + this.n = n; + this.k = k; + backtrack(1, new LinkedList()); + return output; + } +} + diff --git a/Week03/second.java b/Week03/second.java new file mode 100644 index 000000000..f2a29dd79 --- /dev/null +++ b/Week03/second.java @@ -0,0 +1,36 @@ + +//题目: 全排列 + + +//回溯 + +class Solution { + public void backtrack(int n, + ArrayList output, + List> res, + int first) { + // 所有数都填完了 + if (first == n) + res.add(new ArrayList(output)); + for (int i = first; i < n; i++) { + // 动态维护数组 + Collections.swap(output, first, i); + // 继续递归填下一个数 + backtrack(n, output, res, first + 1); + // 撤销操作 + Collections.swap(output, first, i); + } + } + + public List> permute(int[] nums) { + List> res = new LinkedList(); + + ArrayList output = new ArrayList(); + for (int num : nums) + output.add(num); + + int n = nums.length; + backtrack(n, output, res, 0); + return res; + } +} diff --git a/Week06/NOTE.md b/Week06/NOTE.md index 50de30414..d84cb3d50 100644 --- a/Week06/NOTE.md +++ b/Week06/NOTE.md @@ -1 +1,48 @@ -学习笔记 \ No newline at end of file +学习笔记 + +本周学习动态规划 +复习递归的伪代码 +java +public void recur(int level, int param) { + // terminator 递归的结束条件 + if (level > MAX_LEVEL) { + // process result + return; + } + // process current logic 处理当前节点 + process(level, param); + // drill down 进入下一节点 + recur( level: level + 1, newParam); + // restore current status 处理返回 + +} + +分治的伪代码 +C/C++ +int divide_conquer(Problem *problem, int params) { + // recursion terminator + if (problem == nullptr) { + process_result + return return_result; + } + + // process current problem + subproblems = split_problem(problem, data) + subresult1 = divide_conquer(subproblem[0], p1) + subresult2 = divide_conquer(subproblem[1], p1) + subresult3 = divide_conquer(subproblem[2], p1) + ... + + // merge + result = process_result(subresult1, subresult2, subresult3) + // revert the current level status + + return 0; +} + + +定义:讲一个复杂的问题,将它分解为简单的子问题(用递归的方式) +关键点: +1.动态规划与递归或者分治没有本质上的区别(关键看有无最优的子结构) +2.共性:找到重复子问题 +3.差异性:最优子结构,中途可淘汰次优解 diff --git a/Week06/first.java b/Week06/first.java new file mode 100644 index 000000000..55f103f63 --- /dev/null +++ b/Week06/first.java @@ -0,0 +1,34 @@ + +//题目:回文子串 +//动态规划法 +class Solution { + public int countSubstrings(String s) { + if (s == null || s.equals("")) { + return 0; + } + int n = s.length(); + boolean[][] dp = new boolean[n][n]; + int result = s.length(); + for (int i = 0; i < n; i++) dp[i][i] = true; + for (int i = n - 1; i >= 0; i--) { + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (j - i == 1) { + dp[i][j] = true; + } else { + dp[i][j] = dp[i + 1][j - 1]; + } + } else { + dp[i][j] = false; + } + if (dp[i][j]) { + result++; + } + } + } + return result; + + } +} + + diff --git a/Week06/second.java b/Week06/second.java new file mode 100644 index 000000000..78ed6e220 --- /dev/null +++ b/Week06/second.java @@ -0,0 +1,38 @@ + +//题目: 最大正方形 + + +struct Node{ + int row_nums; + int col_nums; + Node():row_nums(0), col_nums(0){} + }; + +class Solution { + public: + int maximalSquare(vector>& matrix) { + if(matrix.empty() || matrix[0].empty()) return 0; + int rows = matrix.size(), cols = matrix[0].size(); + // 1. 确定dp数组和状态 + vector> dp(rows+1, vector(cols+1)); + // 2. base case(dp[0][...]=0 & dp[...][0]=0) + // 3. 状态转移方程 + for(int i = 1; i <= rows; i++){ + for(int j = 1; j <= cols; j++){ + if(matrix[i-1][j-1] == '1'){ + dp[i][j].row_nums = min(dp[i-1][j].row_nums, dp[i-1][j-1].row_nums)+1; + dp[i][j].col_nums = min(dp[i][j-1].col_nums, dp[i-1][j-1].col_nums)+1; + } + } + } + // 4. 遍历dp计算最大正方形 + int max_size = 0; + for(int i = 1; i <= rows; i++){ + for(int j = 1; j <= cols; j++){ + int edge = min(dp[i][j].row_nums, dp[i][j].col_nums); + max_size = max(max_size, edge*edge); + } + } + return max_size; + } +}; diff --git a/Week07/NOTE.md b/Week07/NOTE.md index 50de30414..2e73fa807 100644 --- a/Week07/NOTE.md +++ b/Week07/NOTE.md @@ -1 +1,67 @@ -学习笔记 \ No newline at end of file +学习笔记 +1.字典树 +字典树, Trie树,又称单词直接查找数或键树,是一种树形结构,典型要应用是用于统计或排序大量的字符串(但不限于字符串),所以京城被搜索引擎系统用于文本词频统计 +优点: +最大限度地减少无畏的字符串比较,查询效率比哈希表高 +基本性质: +1.节点本身不存在完成单词 +2.从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串 +3.每个节点的所存子节点路径代表的字符都不相同 +核心思想 +1.Trie tree的核心思想是空间换时间 +2.利用字符串的公共前缀来降低查询时间的开销以达到提供效率的目的 + +2.高级搜索 +一、并查集 +适用情景: +1.组团、配对问题 +2.Group or not + +基本操作 +makeSet(S):建立一个新的并查集,其中包含S个单元素集合 +unionSet(x,y):把元素X和元素Y所在的集合合并,要求X、Y所在的集合不相交,如果相交则不合并 +find(x):找到元素x所在的集合的代表,该操作也可以用于判断两个元素是否位于同一集合,只要将他们的代表比较一下就可以 + + +红黑树和AVL +AVL +平衡因子:它的子子树的高度减去它的右子树的高度(有时相反) 不大于绝对值1 +平衡因子的由来:查询的时间复杂度等于树的深度 +通过旋转操作进行平衡(四种) +1.左旋 +2.右旋 +3.左右旋 +4.右左旋 + +AVL总结: +1、AVL是一个平衡二叉搜索树 +2、每个节点存平衡因子{-1,0,1} +3、四种旋转操作 +AVL不足:节点需要存储额外的信息,且调整次数频繁 + +红黑树 +1.红黑树是一种近似平衡的二叉搜索树,它能够确保任何一个节点的左右子树的高度差小于2倍,具体来说,红黑树是满足如下条件的二叉搜索树: +1、每个节点,要么红色,要么黑色 +2、根节点是黑色 +3、每个叶节点(NIL节点、空节点)是黑色 +4、不能有相邻的两个红色节点 +5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 +关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长 + +AVL与红黑树对比 +1、AVL查询效率比红黑树快 +2、红黑树的插入删除操作比AVL快(旋转少) +3、读操作多,写操作少,使用AVL +4、插入和删除操作多,使用红黑树 + + + + + + + + + + + + diff --git a/Week07/first.java b/Week07/first.java new file mode 100644 index 000000000..49d104ee5 --- /dev/null +++ b/Week07/first.java @@ -0,0 +1,32 @@ + +//题目:有效数独 + +class Solution { + public boolean isValidSudoku(char[][] board) { + if(board == null) + return false; + if(board.length !=9) + return false; + int[] map = new int[9]; + for(int y=0; y<9; y++){ + if(board[y] == null || board[y].length != 9) + return false; + for(int x=0; x<9; x++){ + int key = board[y][x] - '1'; //key:数字 + if(key >= 0 && key <= 8) { //1~9有效数字 + int index = (1< vistied = new HashSet<>(); + dfs(0, start, end, bank, vistied); + return count == Integer.MAX_VALUE ? -1 : count; + } + +private void dfs(int minStepCount, String start, String end, String[] bank, HashSet vistied) { + if (start.equals(end)) { + count = Math.min(count, minStepCount); + return; + } + for (int i = 0; i < bank.length; i++) { + int diff = 0; + for (int j = 0; j < 8; j++) { + if (bank[i].charAt(j) != start.charAt(j)) diff++; + if (diff > 1) break; + } + if (diff == 1 && !vistied.contains(bank[i])) { + vistied.add(bank[i]); + dfs(minStepCount + 1, bank[i], end, bank, vistied); + vistied.remove(bank[i]); + } + } + } diff --git a/Week08/NOTE.md b/Week08/NOTE.md index 50de30414..464befc5c 100644 --- a/Week08/NOTE.md +++ b/Week08/NOTE.md @@ -1 +1,70 @@ -学习笔记 \ No newline at end of file +学习笔记 +1.位运算 + 位运算符 + 左移 << + 右移 >> + 按位或 | + 按位与& + 按位取反 ~ + 按位异或 ^ + 异或:相同的为0 不同的为1 也可以用不进位加法来理解 ,特点: + x^0 =x + x^1s = ~x + x^(~x)=1s + x^x = 0 + c = a^b => a^c=b, b^c=a + a^b^c = a^(b^c)=(a^b)^c +指定位置的位运算 +1.将x最右边的n位清零 :x&(~0<>n&1) +3.获取x的第n位的幂值:x&(1>>n) +4.仅将第n位置为1:x|(1<(x&1)==1 + x%2==0---->(x&1)==0 + 2.x>>1 ----> x/2 + 即x=x/2 --> x=x>>1 + mid= (lelft+right)/2 ---->mid = (left+right)>>1 + 3.x=x&(x-1)清零最低位的1 + 4.x&-x =>得到最低位的1 +5.x&~x =>0 + + + +2.布隆过利器和LRU缓存 +3.排序算法 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Week08/first.java b/Week08/first.java new file mode 100644 index 000000000..77665683e --- /dev/null +++ b/Week08/first.java @@ -0,0 +1,14 @@ + +//题目:2的幂 + +class Solution { + public boolean isPowerOfTwo(int n) { + if (n == 0) return false; + long x = (long) n; + return (x & (-x)) == x; + } +} + + + + diff --git a/Week08/second.java b/Week08/second.java new file mode 100644 index 000000000..f3e7c6a30 --- /dev/null +++ b/Week08/second.java @@ -0,0 +1,19 @@ + +//题目: 合并区间 +public int[][] merge(int[][] intervals) { + int len = intervals.length; + if (len < 1) return intervals; + + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + List list = new ArrayList<>(len); + for (int i = 0; i < len - 1; i++) { + if (intervals[i][1] >= intervals[i + 1][0]) { + intervals[i + 1][0] = intervals[i][0]; + intervals[i + 1][1] = Math.max(intervals[i + 1][1], intervals[i][1]); + } else list.add(intervals[i]); + } + list.add(intervals[len - 1]); // 别忘了最后一个 + + return list.toArray(new int[list.size()][2]); +}