diff --git a/Week_01/.README.md.swp b/Week_01/.README.md.swp new file mode 100644 index 00000000..3399912b Binary files /dev/null and b/Week_01/.README.md.swp differ diff --git a/Week_01/Git command.pdf b/Week_01/Git command.pdf new file mode 100644 index 00000000..ac95bbd3 Binary files /dev/null and b/Week_01/Git command.pdf differ diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..00bf6031 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,31 @@ -学习笔记 \ No newline at end of file +学习笔记 +For this week, I have learned three topics in terms of Array, Linekd list, Stack, Queue. + +Topic 1: Array +array学的还是可以的之前,这次主要通过练习leetcode的题目,发现其实array也可以很难。 +以前我只会暴力求解,而且没意识自己用的是暴力求解 +这次学到了一种很好用的双指针 +常见就是一个快慢指针,或者一个头尾指针 + +Topic 2: Linked list +学校教的链表把,感觉每个人都写的不一样,老师每次写的也都不一样,导致我一开始就没学好 +这兴趣感觉学的还可以,不过这个东西超哥说了,东西基本上都是固定的,所以考练习就好了。 +也是有了一个学习思路,会在下周继续去练习linked list的singular, double, circule etc. +然后我觉得最重要对我而言,还是画图把,不画图,脑子空想很难想 +这个就想超哥说的,很多时候脑子超过了人脑的维度概念,就比较难以figure out. + +Topic 3: Stack +先进后出的特性 Last in First out +然后主要我觉得超哥是教会我们则么去看一些文件把,这个其实蛮重要的,之前学的时候,老师其实也给了很多资料, +但是不知道是干什么用的,这次稍微明确一些了。 + + +Topic 4: Queue +First in First out +也学习了priority queue,不过我还要研究研究,我还是学生,之前没学好,所以感觉时间花的要久一点,而且有些太基础的内容, +需要我自己去查阅。 + +本周感悟: +就像超哥说的,第一周坚持一下很容易,有激情,希望自己以后也能保持好吧。good luck to myself +这个学习笔记不知道写什么老实说,我会附带上传xmind 做的课程导图. + diff --git a/Week_01/leetcode21.java b/Week_01/leetcode21.java new file mode 100644 index 00000000..44645160 --- /dev/null +++ b/Week_01/leetcode21.java @@ -0,0 +1,26 @@ +class leetcode21 { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + // l1 is empty,return l2 + if(l1 == null ) return l2; + //l2 is empty, return l1 + if(l2 == null) return l1; + ListNode dummyHead = new ListNode(-99); + ListNode ptr =dummyHead; + + while(l1 != null && l2 != null) { + if(l1.val <= l2.val) { + ptr.next = l1; + l1=l1.next; + ptr=ptr.next; + }else{ + ptr.next =l2; + l2=l2.next; + ptr=ptr.next; + } + } + //合并完以后可能还有一个没有合并完的,最多一个,所以就指向未完的链表 + ptr.next = l1 == null ? l2 : l1; + return dummyHead.next; + + } +} \ No newline at end of file diff --git a/Week_01/leetcode26.java b/Week_01/leetcode26.java new file mode 100644 index 00000000..b3c3faaa --- /dev/null +++ b/Week_01/leetcode26.java @@ -0,0 +1,15 @@ +class leetcode26 { + public int removeDuplicates(int[] nums) { + //two pointer method + //i is fatser pointer + //j is a slow pointer + int i = 0; + for(int j = 1; j < nums.length; j++) { + if(nums[j] != nums[i]) { + i++; + nums[i] = nums[j]; + } + } + return i + 1; + } +} \ No newline at end of file diff --git "a/Week_01/\347\254\254\344\270\211\350\257\276\357\274\210\346\225\260\347\273\204\343\200\201\351\223\276\350\241\250\350\267\263\357\274\211.pdf" "b/Week_01/\347\254\254\344\270\211\350\257\276\357\274\210\346\225\260\347\273\204\343\200\201\351\223\276\350\241\250\350\267\263\357\274\211.pdf" new file mode 100644 index 00000000..7d4be242 Binary files /dev/null and "b/Week_01/\347\254\254\344\270\211\350\257\276\357\274\210\346\225\260\347\273\204\343\200\201\351\223\276\350\241\250\350\267\263\357\274\211.pdf" differ diff --git "a/Week_01/\347\254\254\345\233\233\350\257\276Stack and Queue.pdf" "b/Week_01/\347\254\254\345\233\233\350\257\276Stack and Queue.pdf" new file mode 100644 index 00000000..d6ca1fc1 Binary files /dev/null and "b/Week_01/\347\254\254\345\233\233\350\257\276Stack and Queue.pdf" differ diff --git "a/Week_01/\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245\357\274\210\346\226\271\346\263\225\350\256\272\357\274\211.pdf" "b/Week_01/\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245\357\274\210\346\226\271\346\263\225\350\256\272\357\274\211.pdf" new file mode 100644 index 00000000..e5202bbe Binary files /dev/null and "b/Week_01/\347\256\227\346\263\225\350\256\255\347\273\203\350\220\245\357\274\210\346\226\271\346\263\225\350\256\272\357\274\211.pdf" differ diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..59c8ae68 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,64 @@ -学习笔记 \ No newline at end of file +学习笔记 +还是一样,主要的内容我都做了思维导图,这里就简写一些把 + +For hash table: +1.每个key会通过hashing functioin然后最后去到hash table的 +eg: lies ,每个字母之和为429, 然后通过hash function,放到hash table +2.如果碰到两个一样的值,那么hashtable就会在后面开始延长,以链表的形式 +3.如果设计的比较差的hash table,其实和链表差不多了就,O(n) +4.好的hash table可以有效的避免冲突,达到O(1) +Average +Search: O(1) +Insertion: O(1) +Access: O(1) + + +For tree: +其实linked list就是特殊化的Tree +Tree就算是特殊化的Graph, 当有cycle的时候其实就是graph了 +然后学习了二叉树的遍历: +pre-order: root, left, right +in-order: left, root, right +post-order:left right root +这一块的内容对于递归用的非常多,然后老师强调了其实递归并不是一定是效率低的一个内容 + +BST: +简单来说就是一个有序的二叉树 +三个特点 +1. 左子树上 所有结点 的值 均小于它根结点; +2. 右子树上 所有结点 的值 均大于它根结点; +3. 以此类推:左、右子树 也分别为二叉查找。 (这就是 重复性 !) +然后对于删除有一个需要注意: +如果删掉以后需要选择一个节点去替换:那么选择右边的最小的那个来替换 +Average +Search: O(logn) +Insertion: O(logn) +Access: O(logn) + + +For heap: +主要特点:可以迅速找到一堆数中的最大/最小的数据结构 +比如大顶堆,这样子find-max:O(1) +但是你会发现这个java实现方式就是用的priorityQueue去实现的,这个在本周作业里面常常用到,很实用 + +二叉堆性质: +1.一个完全二叉树full binary tree +2.数中任意节点的值>=其子节点的值 +一般通过数组实现 +关于Index关系: +left children : 2*i +1; +right children: 2*i +2; +partent floor((i-1)/2); + +Insert需要特别强调: +新元素都加到heap的尾部,这个和实现有关系 +然后heapifyup + +Delete max:跟insert类似 +先把最后一个元素替换到顶部 +然后heapifyDown,具体还是看图理解和复习 + +for Studying: +这周感觉还可以把,比上周感觉时间分配的更均匀一点,希望下周继续努力,不过由于自己基础感觉有些内容还没学过,可能需要再去学一下 +不然有些lamda函数,有点懵,还要这个有一些老师写的什么操作感觉有点看不懂,什么Map.Entry,不知道在干嘛 +有空我会在研究一下 diff --git a/Week_02/leetcode1.java b/Week_02/leetcode1.java new file mode 100644 index 00000000..0bd0fce3 --- /dev/null +++ b/Week_02/leetcode1.java @@ -0,0 +1,16 @@ +class leetcode1 { + public int[] twoSum(int[] nums, int target) { + //先做一个hashtable + //利用target -nums[i] + Map hashtable = new HashMap(); + for(int i = 0; i < nums.length ;i++) { + if(hashtable.containsKey(target- nums[i])) { + return new int[]{hashtable.get(target-nums[i]),i}; + } + hashtable.put(nums[i],i); + } + return new int[0]; + + + } +} \ No newline at end of file diff --git a/Week_02/leetcode242.java b/Week_02/leetcode242.java new file mode 100644 index 00000000..9d34c356 --- /dev/null +++ b/Week_02/leetcode242.java @@ -0,0 +1,26 @@ +class leetcode242 { + public boolean isAnagram(String s, String t) { + if(s.length() != t.length()) return false; + + int counter[] = new int[26]; + for(int i = 0 ; i< s.length(); i++) { + counter[s.charAt(i)-'a']++; + counter[t.charAt(i)-'a']--; + } + boolean p=true; + for(int i=0 ; i< counter.length ;i++) { + if(counter[i] !=0){ + p=false; + } + + } + return p; + + + + + + + + } +} \ No newline at end of file diff --git a/Week_02/leetcode347.java b/Week_02/leetcode347.java new file mode 100644 index 00000000..1476f18d --- /dev/null +++ b/Week_02/leetcode347.java @@ -0,0 +1,35 @@ +class leetcode347 { + public int[] topKFrequent(int[] nums, int k) { + //思路: + //1.hashmap --> 统计次数 + //2.heap -->output +Map occurrences = new HashMap(); + for (int num : nums) { + occurrences.put(num, occurrences.getOrDefault(num, 0) + 1); + } + + // int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数 + PriorityQueue queue = new PriorityQueue(new Comparator() { + public int compare(int[] m, int[] n) { + return m[1] - n[1]; + } + }); + for (Map.Entry entry : occurrences.entrySet()) { + int num = entry.getKey(), count = entry.getValue(); + if (queue.size() == k) { + if (queue.peek()[1] < count) { + queue.poll(); + queue.offer(new int[]{num, count}); + } + } else { + queue.offer(new int[]{num, count}); + } + } + int[] ret = new int[k]; + for (int i = 0; i < k; ++i) { + ret[i] = queue.poll()[0]; + } + return ret; + + } +} \ No newline at end of file diff --git a/Week_02/leetcode589.java b/Week_02/leetcode589.java new file mode 100644 index 00000000..fe7ab4bf --- /dev/null +++ b/Week_02/leetcode589.java @@ -0,0 +1,24 @@ +class leetcode589 { + public List preorder(Node root) { + LinkedList stack = new LinkedList<>(); + LinkedList output = new LinkedList<>(); + if(root == null) return output; + //把根节点放进去 + stack.add(root); + while(!stack.isEmpty()) { + + Node node = stack.pollLast(); + System.out.println(node.val); + output.add(node.val); + //这布很关键,根据stack先入后出来的 + //1后面想出3,就得最后放3,这样子就算先出来 + //所以需要这一步 + Collections.reverse(node.children);//集合中的值反转, + for(Node item: node.children) {//直接把子节点的集合遍历加入栈中 + stack.add(item); + } + } + return output; + + } +} \ No newline at end of file diff --git "a/Week_02/\347\254\254\344\272\224\350\257\276-\345\223\210\345\270\214\350\241\250\343\200\201\346\230\240\345\260\204\343\200\201\351\233\206\345\220\210\347\232\204\345\256\236\347\216\260\344\270\216\347\211\271\346\200\247.pdf" "b/Week_02/\347\254\254\344\272\224\350\257\276-\345\223\210\345\270\214\350\241\250\343\200\201\346\230\240\345\260\204\343\200\201\351\233\206\345\220\210\347\232\204\345\256\236\347\216\260\344\270\216\347\211\271\346\200\247.pdf" new file mode 100644 index 00000000..bbf5b4e2 Binary files /dev/null and "b/Week_02/\347\254\254\344\272\224\350\257\276-\345\223\210\345\270\214\350\241\250\343\200\201\346\230\240\345\260\204\343\200\201\351\233\206\345\220\210\347\232\204\345\256\236\347\216\260\344\270\216\347\211\271\346\200\247.pdf" differ diff --git "a/Week_02/\347\254\254\345\205\255\350\257\276-\346\240\221\343\200\201\344\272\214\345\217\211\346\240\221\343\200\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221,\345\240\206\357\274\214\345\233\276.pdf" "b/Week_02/\347\254\254\345\205\255\350\257\276-\346\240\221\343\200\201\344\272\214\345\217\211\346\240\221\343\200\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221,\345\240\206\357\274\214\345\233\276.pdf" new file mode 100644 index 00000000..dc72e705 Binary files /dev/null and "b/Week_02/\347\254\254\345\205\255\350\257\276-\346\240\221\343\200\201\344\272\214\345\217\211\346\240\221\343\200\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221,\345\240\206\357\274\214\345\233\276.pdf" differ diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..fec34b61 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,82 @@ -学习笔记 \ No newline at end of file +# 学习笔记 + +看了前几周别人总的总结,发现自己好像写的不是很到位,下周好好改进自己,这周时间来不及了! +这周内容其实对我而言有点不太理解,练leetcode时候感觉很差,特别是回溯,分支,这些大学里面都没讲过,其实发现自己大三的话,其实基础可能的确就是差一点。 +感觉还是要多花点时间先去理解一下底层的东西。我会加油的! +这周学校的大作业比较多,这上面时间花的就少一点,下周感恩节放假了,可以好好研究一下! + +## For 递归呢: +按照四步法: +1.terminator +2.process logic in the current logic 处理当前逻辑 +3.drill down 下探到下一层 +4.reverse the current level status if needed 清理当前层(这个不是每次都用得到) + + +### 回顾到之前的树,用的都是递归的思路: + ``` + def preorder(self, root): + if root: + self.traverse_path.append(root.val) + self.preorder(root.left) + self.preorder(root.right) +def inorder(self, root): + if root: + self.inorder(root.left) + self.traverse_path.append(root.val) + self.inorder(root.right) +def postorder(self, root): + if root: + self.postorder(root.left) + self.postorder(root.right) + self.traverse_path.append(root.val) + ``` + +### Java 代码模版for recursion + ``` + 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 +} + ``` + +## For 分治: +本质上就是先divide 变成sub-problem, +然后conquer,去给出sub-solution +最后merge在一起,就是solution +原理很简单,但是实操有点难,我还需要多练习去理解一下逻辑 + +### 分治代码模板(可以理解为比recursion多了一步) + ``` + def divide_conquer(problem, param1, param2, ...): + //recursion terminator + if problem is None: + print_result + return + // prepare data + data = prepare_data(problem) + subproblems = split_problem(problem, data) + //conquer subproblems + subresult1 = self.divide_conquer(subproblems[0], p1, ...) + subresult2 = self.divide_conquer(subproblems[1], p1, ...) + subresult3 = self.divide_conquer(subproblems[2], p1, ...) + ... + // process and generate the final result + result = process_result(subresult1, subresult2, subresult3, …) + // revert the current level states + ``` + +## For 回溯: +利用试错的思想,分别解决问题 +通常就是用递归方法来实现的 +在反复重复上述的步骤后可能出现两种情况: +• 找到一个可能存在的正确的答案; +• 在尝试了所有可能的分步方法后宣告该问题没有答案。 diff --git a/Week_03/leetcode105.java b/Week_03/leetcode105.java new file mode 100644 index 00000000..72fbb233 --- /dev/null +++ b/Week_03/leetcode105.java @@ -0,0 +1,35 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class leetcode105 { + public TreeNode buildTree(int[] preorder, int[] inorder) { + int preLen = preorder.length; + int inLen = inorder.length; + + Map map = new HashMap<>(preLen); + for(int i=0 ; i< inLen; i++) { + map.put(inorder[i], i); + } + return helper(preorder,0, preLen-1, map, 0, inLen-1); + + + } + private TreeNode helper(int[] preorder, int preLeft, int preRight, Map map, int inLeft, int inRight) { + if(preLeft > preRight || inLeft > inRight) return null; + + int rootVal = preorder[preLeft]; + TreeNode root=new TreeNode(rootVal); + int pIndex = map.get(rootVal); + + root.left = helper(preorder,preLeft+1, pIndex-inLeft+preLeft, map, inLeft, pIndex-1); + root.right = helper(preorder,pIndex-inLeft+preLeft+1,preRight,map,pIndex+1, inRight); + return root; + + } +} \ No newline at end of file diff --git a/Week_03/leetcode77.java b/Week_03/leetcode77.java new file mode 100644 index 00000000..2585fd14 --- /dev/null +++ b/Week_03/leetcode77.java @@ -0,0 +1,24 @@ +class leetcode77 { + List temp = new ArrayList<>(); + List> res = new ArrayList>(); + + public List> combine(int n, int k) { + dfs(1, n, k); + return res; + } + + public void dfs(int cur, int n, int k) { + // terminator + if (temp.size() + (n - cur + 1) < k) { + return; + } + if (temp.size() == k) { + res.add(new ArrayList(temp)); + return; + } + temp.add(cur); + dfs(cur + 1, n, k); + temp.remove(temp.size() - 1); + dfs(cur + 1, n, k); + } +} \ No newline at end of file diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..585bf1c7 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,177 @@ -学习笔记 \ No newline at end of file +学习笔记 +本周学习了两个内容,贪心算法greedy 和2分查找。 + +# 贪心算法 Greedy +贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,从而希望导致结果是全局最好或最优的算法。 +使用场景:1.当你可以证明每一步都是最优解的时候,这个适合就可以直接用 +2.由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题。 + + +## greedy VS 动态规划 +贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。 +动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 + +## example of greedy +非整除关系的硬币,可选集合:Coins = [10, 9, 1] +求拼出总数为 18 最少需要几个硬币? +Answer:18 - 9 = 9 + 9 - 9 = 0 + + +# 二分查找 +## 二分查找的前提 +1. 目标函数单调性(单调递增或者递减) +2. 存在上下界(bounded) +3. 能够通过索引访问(index accessible) + +代码模版 +``` +left, right = 0, len(array) - 1 +while left <= right: +mid = (left + right) / 2 +if array[mid] == target: +# find the target!! +break or return result +elif array[mid] < target: +left = mid + 1 +else: +right = mid - 1 +``` + +### for java +``` +// Java +public int binarySearch(int[] array, int target) { + int left = 0, right = array.length - 1, mid; + while (left <= right) { + mid = (right - left) / 2 + left; + + if (array[mid] == target) { + return mid; + } else if (array[mid] > target) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + return -1; +} +``` + +### for python +``` +# Python +left, right = 0, len(array) - 1 +while left <= right: + mid = (left + right) / 2 + if array[mid] == target: + # find the target!! + break or return result + elif array[mid] < target: + left = mid + 1 + else: + right = mid - 1 +``` + +# coding exercise: +### leetcode 55 跳跃游戏 +``` +class Solution { + public boolean canJump(int[] nums) { + //巧妙地从后往前greedy + //greedy分为3种:1. front->back greedy 2. back->front greedy 3. 部分切入,then greedy + + if(nums == null) { + return false; + } + int endReachable = nums.length-1;//从最后开始 + for( int i = nums.length - 1 ; i >=0 ; i--) { + if(nums[i]+ i >= endReachable) { + endReachable=i; + // System.out.println(i); + } + } + //如果能回到0点,说明能够达到初始位置 + return endReachable==0; + + } +} + + + + +``` +### leetcode122 买卖股票的最佳时机 II +这题看起来很easy,但是主要是你得想到用贪心,整个系列一起看就还不错 +``` +class Solution { + public int maxProfit(int[] prices) { + int res=0; + for(int i = 1; i< prices.length ; i++) { + res += Math.max(prices[i]-prices[i-1],0); + } + return res; + + } +} +``` +### leetcode 69. x 的平方根(关于牛顿迭代法) + +``` +//用二分查找先证明单调性 +// y= x^2 --> 单调递增 (x>0) +//方法2:牛顿迭代法 +class Solution { + public int mySqrt(int a) { + long x = a; + while (x * x > a) { + x = (x + a / x) / 2; + } + return (int) x; + + + } +} +``` + + +### leetcode 33. 搜索旋转排序数组 +``` +class Solution { + public int search(int[] nums, int target) { + int n = nums.length; + if(n == 0 ) return -1; + //长度=1 的情况 + if(n == 1) return nums[0] == target ? 0 : -1; + //二分查找算法 + int l=0, r = n-1; + while(l <= r) { + int mid = (l+r)/2; + if(nums[mid] == target) return mid; + if(nums[0] <= nums[mid]) { //左单调区间 + // if(nums[0] <= target && target < nums[mid]){ + // r = mid-1; + // }else { + // l= mid+1; + // } + + (nums[0] <= target && target < nums[mid]) ? r = mid-1:l= mid+1; + + }else {//有单调区间 + // if(nums[mid]=n || j>=m || grid[i][j] !='1') return ; + grid[i][j] ='0'; + //右左,上下移动 + DFS(grid,i+1,j); + DFS(grid,i-1,j); + DFS(grid,i,j+1); + DFS(grid,i,j-1); + } +} \ No newline at end of file diff --git a/Week_04/leetcode55.java b/Week_04/leetcode55.java new file mode 100644 index 00000000..ac3ac4c6 --- /dev/null +++ b/Week_04/leetcode55.java @@ -0,0 +1,20 @@ +class leetcode55 { + public boolean canJump(int[] nums) { + //巧妙地从后往前greedy + //greedy分为3种:1. front->back greedy 2. back->front greedy 3. 部分切入,then greedy + + if(nums == null) { + return false; + } + int endReachable = nums.length-1;//从最后开始 + for( int i = nums.length - 1 ; i >=0 ; i--) { + if(nums[i]+ i >= endReachable) { + endReachable=i; + // System.out.println(i); + } + } + //如果能回到0点,说明能够达到初始位置 + return endReachable==0; + + } +} \ No newline at end of file diff --git a/Week_06/README.md b/Week_06/README.md index 50de3041..a0a75a51 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,133 @@ -学习笔记 \ No newline at end of file +学习笔记 + +# 动态规划 + +## 递归代码模版 +``` +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 +} + + +``` + +## 分治代码模板 +``` +def divide_conquer(problem, param1, param2, ...): +# recursion terminator +if problem is None: +print_result +return +# prepare data +data = prepare_data(problem) +subproblems = split_problem(problem, data) +# conquer subproblems +subresult1 = self.divide_conquer(subproblems[0], p1, ...) +subresult2 = self.divide_conquer(subproblems[1], p1, ...) +subresult3 = self.divide_conquer(subproblems[2], p1, ...) +… +# process and generate the final result +result = process_result(subresult1, subresult2, subresult3, …) +# revert the current level states + +``` +### 本质: +寻找重复性 —> 计算机指令集 +1. 人肉递归低效、很累 +2. 找到最近最简方法,将其拆解成可重复解决的问题 +3. 数学归纳法思维(抵制人肉递归的诱惑) + +## 动态规划 Dynamic Programming +### defintion +1.“Simplifying a complicated problem by breaking it down into +simpler sub-problems” +(in a recursive manner) + +2.Divide & Conquer + Optimal substructure +分治 + 最优子结构 + +### key points +动态规划 和 递归或者分治 没有根本上的区别(关键看有无最优的子结构) +共性:找到重复子问题 +差异性:最优子结构、中途可以淘汰次优解 + +### 做题思路: +1. 最优子结构 opt[n] = best_of(opt[n-1], opt[n-2], …) +2. 储存中间状态:opt[i] +3. 递推公式(美其名曰:状态转移方程或者 DP 方程) +Fib: opt[i] = opt[n-1] + opt[n-2] +二维路径:opt[i,j] = opt[i+1][j] + opt[i][j+1] (且判断a[i,j]是否空地) + +#### 64. 最小路径和 +https://leetcode-cn.com/problems/minimum-path-sum/ +从上到下的思路 +``` +class Solution { + public int minPathSum(int[][] grid) { + //动态规划,从上倒下,累加 + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int rows = grid.length, columns = grid[0].length; + //状态数组 + int[][] dp = new int[rows][columns]; + //初始化00 + dp[0][0] = grid[0][0]; + //让第一行都先+,因为只能右/下,这个情况只能右 + for (int i = 1; i < rows; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + //让第一列都先+,因为只能右/下,这个情况只能下 + for (int j = 1; j < columns; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + //对其他的数加法 + for (int i = 1; i < rows; i++) { + for (int j = 1; j < columns; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } + } + //最后一个元素就是最短路径 + return dp[rows - 1][columns - 1]; + } +} +``` + +#### 647. 回文子串 + +``` +class Solution { + public int countSubstrings(String s) { + int n=s.length(); + int dp[][]=new int[n][n]; + int nums=s.length(); + + for (int i = n - 1; i >= 0; i--) { + dp[i][i] = 1; + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (j - i < 3){ + dp[i][j] = 1; + }else { + dp[i][j] = dp[i + 1][j - 1]; + } + } + if (dp[i][j]==1 ){ + nums++; + } + } + }return nums; + + } +} +``` + diff --git a/Week_06/leetcode64.java b/Week_06/leetcode64.java new file mode 100644 index 00000000..9fd42164 --- /dev/null +++ b/Week_06/leetcode64.java @@ -0,0 +1,29 @@ +class leetcode64 { + public int minPathSum(int[][] grid) { + //动态规划,从上倒下,累加 + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int rows = grid.length, columns = grid[0].length; + //状态数组 + int[][] dp = new int[rows][columns]; + //初始化00 + dp[0][0] = grid[0][0]; + //让第一行都先+,因为只能右/下,这个情况只能右 + for (int i = 1; i < rows; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + //让第一列都先+,因为只能右/下,这个情况只能下 + for (int j = 1; j < columns; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } + //对其他的数加法 + for (int i = 1; i < rows; i++) { + for (int j = 1; j < columns; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } + } + //最后一个元素就是最短路径 + return dp[rows - 1][columns - 1]; + } +} \ No newline at end of file diff --git a/Week_06/leetcode647.java b/Week_06/leetcode647.java new file mode 100644 index 00000000..6f57922c --- /dev/null +++ b/Week_06/leetcode647.java @@ -0,0 +1,24 @@ +class leetcode647 { + public int countSubstrings(String s) { + int n=s.length(); + int dp[][]=new int[n][n]; + int nums=s.length(); + + for (int i = n - 1; i >= 0; i--) { + dp[i][i] = 1; + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (j - i < 3){ + dp[i][j] = 1; + }else { + dp[i][j] = dp[i + 1][j - 1]; + } + } + if (dp[i][j]==1 ){ + nums++; + } + } + }return nums; + + } +} \ No newline at end of file diff --git a/Week_07/README.md b/Week_07/README.md index 50de3041..7ad78ae8 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,451 @@ -学习笔记 \ No newline at end of file +学习笔记 +# 字典树和并查集 + +## 字典树Trie +### definition +字典树,即 Trie 树,又称单词 +查找树或键树,是一种树形结 +构 +1. 结点本身不存完整单词; +2. 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的 +字符串; +3. 每个结点的所有子结点路径代表的字符都不相同 + +### advantage + +最大限度地减少 +无谓的字符串比较,查询效率 +比哈希表高。 + +### 核心思想 + +1. Trie 树的核心思想是空间换时间。 +2. 利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。 + +### sample code +``` +class Trie(object): + def __init__(self): + self.root = {} + self.end_of_word = "#" + def insert(self, word): + node = self.root + for char in word: + node = node.setdefault(char, {}) + node[self.end_of_word] = self.end_of_word + def search(self, word): + node = self.root + for char in word: + if char not in node: + return False + node = node[char] + return self.end_of_word in node + def startsWith(self, prefix): + node = self.root + for char in prefix: + if char not in node: + return False + node = node[char] + return True + +``` + +## 并查集 Disjoint Set +### 基本操作 +• makeSet(s):建立一个新的并查集,其中包含 s 个单元素集合。 +• unionSet(x, y):把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在 +的集合不相交,如果相交则不合并。 +• find(x):找到元素 x 所在的集合的代表,该操作也可以用于判断两个元 +素是否位于同一个集合,只要将它们各自的代表比较一下就可以了。 + + +### sample code +``` +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--; + } +} + +``` +# 高级搜索 +## 初级搜索 +1. 朴素搜索 +2. 优化方式:不重复(fibonacci)、剪枝(生成括号问题) +3. 搜索方向: + DFS: depth first search 深度优先搜索 + BFS: breadth first search 广度优先搜索 + 双向搜索、启发式搜索 +## 剪枝 +eg: 三子棋、五子棋 +主要通过次操作来减少很多多余的操作,来加快运行 +有的复杂度特别大,如果不做剪枝,会崩溃 + +## 回溯法 +### definition +1. 回溯法采用试错的思想,它尝试分步的去解决一个问题 +2. 在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候, +它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。 +Key words: 试差思想,部分解决,无法解决就取消上一步骤 + +### 实现 +回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种情况: +• 找到一个可能存在的正确的答案 +• 在尝试了所有可能的分步方法后宣告该问题没有答案 +在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。 + + +## 双向 BFS: Two-ended BFS +简单来说就是两个bfs同时进行,一个从前开始,一个从后开始 + +## 启发式搜索 Heuristic Search (A*) + +## 估价函数 +启发式函数: h(n),它用来评价哪些结点最有希望的是一个我们要找的结点, +h(n) 会返回一个非负实数,也可以认为是从结点n的目标结点路径的估计成本。 +启发式函数是一种告知搜索方向的方法。 +它提供了一种明智的方法来猜测哪个邻居结点会导向一个目标。 + +## sample code +``` +def AstarSearch(graph, start, end): + pq = collections.priority_queue() # 优先级 —> 估价函数 + pq.append([start]) + visited.add(start) + while pq: + node = pq.pop() # can we add more intelligence here ? + visited.add(node) + process(node) + nodes = generate_related_nodes(node) + unvisited = [node for node in nodes if node not in visited] + pq.push(unvisited) +``` +# 高级树、 AVL AVL AVL AVL 树 +## AVL tree + +1. balance factor: 左子树高度-右子树高度 +balance factor ={-1,0,1} +2. 通过旋转操作来进行平衡(四种 ) +### 四种旋转操作 +https://en.wikipedia.org/wiki/Tree_rotation#/media/File:Rebalancing.gif +#### case 1. 子树 形态:全右子树 +左旋 +#### case 2. 子树 形态:全左子树 +右旋 +#### case 3. 子树 形态:左右子树 +左右旋 +eg: A left is B & B right is C +#### case 4. 子树 形态:右左子树 +右左旋 +eg: A right is B & B left is C + +### drawbacks +不足:结点需要存储额外信息、且调整次数频繁 + +## 红黑树 red black tree +红黑树是一种 近似平衡 的二叉搜索树( BinaryBinaryBinaryBinaryBinaryBinarySearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch TreeSearch Tree), 它能够确保任何一 个结点的 左右子树高度差小于两倍 。具体来说,红黑树是满足如下条件的二叉 搜索树 : +• 每个结点要么是红色 +,黑• 根结点是黑色 +• 每个叶 结点( NILNILNIL结点,空 结点)是黑色的 。 +• 不能有相邻接的两个红色 结点 +• 从任一 结点到其每个叶子的所有路径都包含相同数目黑色 结点 + +### 关键性质 +从根到叶子的最长可能路径不多于短两倍。 + +### 对比avl and red balck tree + +• AVL trees providefaster lookupsthan Red Black Trees because they are more strictly balanced. +• Red Black Trees providefaster insertion and removal operations than AVL trees as fewer rotations are done due to relatively relaxed balancing. +• AVL trees store balance factors or heights with each node, thus requires storage for an integer per node whereas Red Black Tree requires only 1 bit of information per node. +• Red Black Trees are used in most of the language libraries likemap, multimap, multisetin C++whereas AVL trees are used in databaseswhere faster retrievals are required. +### leetcode 547 + +#### use dfs +``` +public class Solution { + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } +} + +``` + +#### use bfs +``` +public class Solution { + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + Queue < Integer > queue = new LinkedList < > (); + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + queue.add(i); + while (!queue.isEmpty()) { + int s = queue.remove(); + visited[s] = 1; + for (int j = 0; j < M.length; j++) { + if (M[s][j] == 1 && visited[j] == 0) + queue.add(j); + } + } + count++; + } + } + return count; + } +} + +``` + +#### use 并查集 +``` +public class Solution { + int find(int parent[], int i) { + if (parent[i] == -1) + return i; + return find(parent, parent[i]); + } + + void union(int parent[], int x, int y) { + int xset = find(parent, x); + int yset = find(parent, y); + if (xset != yset) + parent[xset] = yset; + } + public int findCircleNum(int[][] M) { + int[] parent = new int[M.length]; + Arrays.fill(parent, -1); + for (int i = 0; i < M.length; i++) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && i != j) { + union(parent, i, j); + } + } + } + int count = 0; + for (int i = 0; i < parent.length; i++) { + if (parent[i] == -1) + count++; + } + return count; + } +} + +``` + + +### leetcode 200 岛屿数量 +use dfs +``` +class Solution { + void dfs(char[][] grid, int r, int c) { + int nr = grid.length; + int nc = grid[0].length; + + if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + dfs(grid, r, c); + } + } + } + + return num_islands; + } +} + +``` + + + +use bfs +``` +class Solution { + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + ++num_islands; + grid[r][c] = '0'; + Queue neighbors = new LinkedList<>(); + neighbors.add(r * nc + c); + while (!neighbors.isEmpty()) { + int id = neighbors.remove(); + int row = id / nc; + int col = id % nc; + if (row - 1 >= 0 && grid[row-1][col] == '1') { + neighbors.add((row-1) * nc + col); + grid[row-1][col] = '0'; + } + if (row + 1 < nr && grid[row+1][col] == '1') { + neighbors.add((row+1) * nc + col); + grid[row+1][col] = '0'; + } + if (col - 1 >= 0 && grid[row][col-1] == '1') { + neighbors.add(row * nc + col-1); + grid[row][col-1] = '0'; + } + if (col + 1 < nc && grid[row][col+1] == '1') { + neighbors.add(row * nc + col+1); + grid[row][col+1] = '0'; + } + } + } + } + } + + return num_islands; + } +} + +``` +use 并查集 +``` +class Solution { + class UnionFind { + int count; + int[] parent; + int[] rank; + + public UnionFind(char[][] grid) { + count = 0; + int m = grid.length; + int n = grid[0].length; + parent = new int[m * n]; + rank = new int[m * n]; + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[i][j] == '1') { + parent[i * n + j] = i * n + j; + ++count; + } + rank[i * n + j] = 0; + } + } + } + + public int find(int i) { + if (parent[i] != i) parent[i] = find(parent[i]); + return parent[i]; + } + + public void union(int x, int y) { + int rootx = find(x); + int rooty = find(y); + if (rootx != rooty) { + if (rank[rootx] > rank[rooty]) { + parent[rooty] = rootx; + } else if (rank[rootx] < rank[rooty]) { + parent[rootx] = rooty; + } else { + parent[rooty] = rootx; + rank[rootx] += 1; + } + --count; + } + } + + public int getCount() { + return count; + } + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int nr = grid.length; + int nc = grid[0].length; + int num_islands = 0; + UnionFind uf = new UnionFind(grid); + for (int r = 0; r < nr; ++r) { + for (int c = 0; c < nc; ++c) { + if (grid[r][c] == '1') { + grid[r][c] = '0'; + if (r - 1 >= 0 && grid[r-1][c] == '1') { + uf.union(r * nc + c, (r-1) * nc + c); + } + if (r + 1 < nr && grid[r+1][c] == '1') { + uf.union(r * nc + c, (r+1) * nc + c); + } + if (c - 1 >= 0 && grid[r][c-1] == '1') { + uf.union(r * nc + c, r * nc + c - 1); + } + if (c + 1 < nc && grid[r][c+1] == '1') { + uf.union(r * nc + c, r * nc + c + 1); + } + } + } + } + + return uf.getCount(); + } +} + +``` diff --git a/Week_07/leetcode200.java b/Week_07/leetcode200.java new file mode 100644 index 00000000..194d8ab8 --- /dev/null +++ b/Week_07/leetcode200.java @@ -0,0 +1,36 @@ +class leetcode200 { + //use dfs + void dfs(char[][] grid, int r, int c) { + int n1 = grid.length; + int n2 = grid[0].length; + if (r < 0 || c < 0 || r >= n1 || c >= n2 || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int n1 = grid.length; + int n2 = grid[0].length; + int numIsl = 0; + for (int r = 0; r < n1; ++r) { + for (int c = 0; c < n2; ++c) { + if (grid[r][c] == '1') { + ++numIsl; + dfs(grid, r, c); + } + } + } + + return numIsl; + } +} diff --git a/Week_07/leetcode547.java b/Week_07/leetcode547.java new file mode 100644 index 00000000..cbd6a8a3 --- /dev/null +++ b/Week_07/leetcode547.java @@ -0,0 +1,22 @@ +public class leetcode547 { + //use dfs + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } +} \ No newline at end of file diff --git a/Week_08/leetcode191.java b/Week_08/leetcode191.java new file mode 100644 index 00000000..f3e29cf4 --- /dev/null +++ b/Week_08/leetcode191.java @@ -0,0 +1,15 @@ +public class leetcode191 { + public int hammingWeight(int n) { + //位运算 + int count = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) { + count++; + } + mask <<= 1; + } + return count; + + } +} \ No newline at end of file diff --git a/Week_08/leetcode231.java b/Week_08/leetcode231.java new file mode 100644 index 00000000..bfd0e8a8 --- /dev/null +++ b/Week_08/leetcode231.java @@ -0,0 +1,16 @@ +class leetcode231 { + public boolean isPowerOfTwo(int n) { + /* + 获取二进制中最右边的 1: x & (-x) + + + */ + if (n == 0) + return false; + long x = (long) n; + return (x & (-x)) == x; + } + + + +} \ No newline at end of file diff --git a/Week_10/README.md b/Week_10/README.md index 50de3041..4ac93154 100644 --- a/Week_10/README.md +++ b/Week_10/README.md @@ -1 +1,282 @@ -学习笔记 \ No newline at end of file +## 毕业总结 + +#### 回顾学习方法 +三分视频理解+7分练习 +所以练习才是最重要的 +于是引出了我门的五毒神掌刷题法 + +## 反思以下和小总结 +我是一个美本的大三留学生,因为学校的进度太快,感觉基础没打牢固,于是来上了这个训练营,其实还是很值得的,至少信心上提升了太多了。 +我觉得之前很多本质和一些关键点我都没搞懂,比如Bfs,dfs的算法,比如分治回溯到底是啥。 +有些上课也没有学过的,我也都学了以下,可以说是受益匪浅, +而且发现我上后续课程之中,也发现了很多东西很好用,比如位运算,如果早点来上训练营我可能后期学校课程也能比较轻松了 +感觉还是那句话,笨鸟先飞,没有人能随随便便成功的,想成功都得付出一定的努力,更何况cs这个专业更笨不简单 +然后自己也有很多小毛病,上学期比较忙,也受限于网课的原因,所以态度上不是狠积极,这个东西其实不是我能控制的,如果有时候我还想把分治和回溯,动态规划去研究一下,感觉还是很缺少练习的。 +这门课呢,总体上还是不粗的,除了graph那一块内容少了一点,我门学校还是讲的很多的一块内容,所以有机会,还想再研究一下。 + + + +以下回顾一些我觉得对我而言最有帮助的内容 +## linked list +single linked list 时间复杂度 +prepend prepend O(1) +append append O(1) +lookup O(n) +insert insert O(1) +delete delete O(1) + +### skip list +因为lookup for singule linked list 是O(n) +于是引出了这个skip list +注意:只能用于元素有序的情况 。 +所以,跳表 (skip list )对标的是平衡树 (AVL Tree AVL Tree )和二分查找, 是一种 插入 /删除 /搜索 都是 O(log n) 的数据结构。 1989 年出现 。 +它最大的优势是原理简单、容易实现方便扩展效率更高。因此在一些热门的项目里用来替代平衡树,如 Redis edis、Level evelDB 等 +#### 如何优化 +增加维度:增加多级索引 +在跳表中查询任意数据的时间复杂度就是 O(logn) + + +## stack && queue +stack:Last in - First out +queue:Last in - Last out +• Stack:先入后出;添加、删除皆为 O(1) +• Queue:先入先出;添加、删除皆为 O(1) + +### sample code +for stack +``` +Stack stack = new Stack<>(); +stack.push(1); +stack.push(2); +stack.push(3); +stack.push(4); +System.out.println(stack); +System.out.println(stack.search(4)); +stack.pop(); +stack.pop(); +Integer topElement = stack.peek(); +System.out.println(topElement); +System.out.println(" 3的位置 " + stack.search(3)); +``` +for queue +``` +Queue queue = new LinkedList(); +queue.offer("one"); +queue.offer("two"); +queue.offer("three"); +queue.offer("four"); +System.out.println(queue); +String polledElement = queue.poll(); +System.out.println(polledElement); +System.out.println(queue); +String peekedElement = queue.peek(); +System.out.println(peekedElement); +System.out.println(queue); +while(queue.size() > 0) { +System.out.println(queue.poll()); +} +``` +## DOUBLE ended queue +1. 简单理解:两端可以进出的 Queue +Deque - double ended queue +2.插入和删除都是 O(1) 操作 +### sample code +for deque +``` +Deque deque = new LinkedList(); +deque.push("a"); +deque.push("b"); +deque.push("c"); +System.out.println(deque); +String str = deque.peek(); +System.out.println(str); +System.out.println(deque); +while (deque.size() > 0) { +System.out.println(deque.pop()); +} +System.out.println(deque); +``` + +## priority queue 这个还挺常用的,特别是高端的数据结构的实现 + +1. 插入操作:O(1) +2.取出操作:O(logN) - 按照元素的优先级取出 +3.底层具体实现的数据结构较为多样和复杂:heap、bst、treap + +## hash table +哈希表( Hash Hash table ), 也叫散列表,是根据关键码值( Key value ) + +### java code +#### Map: key-value 对,key不重复 +-new HashMap()/new TreeMap() +-map.set(key.value) +-map.get(key) +-map.has(key) +-map.size() +-map.clear() + +#### set: 不重复元素的集合 +-new HashSet()/new TreeSet() +-set.add(value) +-set.delete(value) +-set.hash(value) + +## 二叉树 +### 遍历(3 kinds) +1. 前序( Pre -order ):根 -左-右 +2. 中序( In -order ):左 -根-右 +3. 后序( Post Post -order ):左 -右-根 + +### bst(binary search tree) +二叉搜索树, 也称二叉 排序 树、有序二叉树( Ordered Binary Tree Binary TreeBinary Tree )、 排 序二叉树( Sorted Binary Tree Binary Tree Binary Tree Binary Tree), 是指一棵空树或者具有下列性质的二叉 树: +1. 左子树上 所有结点 的值 均小于它根结点; +2. 右子树上 所有结点 的值 均大于它根结点; +3. 以此类推:左、右子树 也分别为二叉查找。 (这就是 重复性 !) + + +## recursion +最重要的我绝对是模板 +Python 代码模版 +``` +def recursion(level, param1, param2, ...): +# recursion terminator +if level > MAX_LEVEL: +process_result +return +# process logic in current level +process(level, data...) +# drill down +self.recursion(level + 1, p1, ...) +# reverse the current level status if needed +``` +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 +} +``` +### 思维要点 +1. 不要人肉进行递归(最大误区) +2. 找到最近最简方法,将其拆解成可重复解决的问题(重复子问题) +3. 数学归纳法思维 + +## 分治 Divide & Conquer +### 分治代码模板 +``` +def divide_conquer(problem, param1, param2, ...): +# recursion terminator +if problem is None: +print_result +return +# prepare data +data = prepare_data(problem) +subproblems = split_problem(problem, data) +# conquer subproblems +subresult1 = self.divide_conquer(subproblems[0], p1, ...) +subresult2 = self.divide_conquer(subproblems[1], p1, ...) +subresult3 = self.divide_conquer(subproblems[2], p1, ...) +... +# process and generate the final result +result = process_result(subresult1, subresult2, subresult3, …) +# revert the current level states +``` + +### Backtracking +## definition +回溯法通常用最简单的递归方法来实现,在反复重复上述的步骤后可能出现两种 +情况: +• 找到一个可能存在的正确的答案; +• 在尝试了所有可能的分步方法后宣告该问题没有答案。 +在最坏的情况下,回溯法会导致一次复杂度为指数时间的计算。 + +## bfs and dfs + +### Depth-First-Search +DFS 代码 - 递归写法 +``` +visited = set() +def dfs(node, visited): +if node in visited: # terminator +# already visited +return +visited.add(node) +# process current node here. +... +for next_node in node.children(): +if not next_node in visited: +dfs(next node, visited) +``` +DFS 代码 - 非递归写法 +``` + +def DFS(self, tree): +if tree.root is None: +return [] +visited, stack = [], [tree.root] +while stack: +node = stack.pop() +visited.add(node) +process (node) +nodes = generate_related_nodes(node) +stack.push(nodes) +# other processing work +... +``` +### Breadth-First-Search +``` +def BFS(graph, start, end): +queue = [] +queue.append([start]) +visited.add(start) +while queue: +node = queue.pop() +visited.add(node) +process(node) +nodes = generate_related_nodes(node) +queue.push(nodes) +# other processing work +... +``` + +## greedy +贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有 +利)的选择,从而希望导致结果是全局最好或最优的算法。 + +## 动态规划 +1.“Simplifying a complicated problem by breaking it down into +simpler sub-problems” (in a recursive manner) +2.Divide & Conquer + Optimal substructure +分治 + 最优子结构 +### key points +动态规划 和 递归或者分治 没有根本上的区别(关键看有无最优的子结构) +共性:找到重复子问题 +差异性:最优子结构、中途可以淘汰次优解 + +### example +1. 最优子结构 opt[n] = best_of(opt[n-1], opt[n-2], …) +2. 储存中间状态:opt[i] +3. 递推公式(美其名曰:状态转移方程或者 DP 方程) +Fib: opt[i] = opt[n-1] + opt[n-2] +二维路径:opt[i,j] = opt[i+1][j] + opt[i][j+1] (且判断a[i,j]是否空地) + +## 位运算 +1. 将 x 最右边的 n 位清零: x& (~0 << n) & (~0 << n) & (~0 << n) & (~0 << n) +2. 获取 x 的第 n 位值( 0 或者 1): (x >> n) & 1 (x >> n) & 1 (x >> n) & 1 (x >> n) & 1 (x >> n) & 1 +3. 获取 x 的第 n 位的幂值: x& (1 << & (1 << & (1 << n) +4. 仅将第 n 位置为 1:x | (1 << n) x | (1 << n) x | (1 << n) x | (1 << n) x | (1 << n) +5. 仅将第 n 位置为 0:x & (~ (1 << n)) x & (~ (1 << n)) x & (~ (1 << n)) x & (~ (1 << n)) x & (~ (1 << n)) x & (~ (1 << n)) +6. 将 x 最高位至第 n 位(含)清零: x& ((1 << n) & ((1 << n) & ((1 << n) & ((1 << n) -1) +指定位置的运算 + + diff --git a/leetcode20.java b/leetcode20.java new file mode 100644 index 00000000..a48be701 --- /dev/null +++ b/leetcode20.java @@ -0,0 +1,40 @@ +class leetcode20 { + public boolean isValid(String s) { + Stack stack = new Stack<>(); + //左边括号就入栈 + + for(int i = 0;i < s.length(); i++) { + //左括号入栈 + char temp = s.charAt(i); + if(temp == '{' || temp =='(' || temp =='['){ + stack.push(temp); + } else { + //检查栈是否为控股 + if(stack.isEmpty()) { + return false; // + + }else { + //判断栈顶元素左括号和当前字符的右括号是否匹配 + char cur = s.charAt(i); + char topChar = stack.peek();// return the top value of the stack + if(cur == ')' && topChar =='(' || cur =='}' && topChar == '{' || cur == ']' && topChar == '[') { + stack.pop(); + }else {// not element 对应要求 + return false; + } + + + } + } + + + } + + //判断栈是否为空 + if(!stack.isEmpty()) { + return false; + } + return true; + + } +} \ No newline at end of file diff --git a/leetcode200.java b/leetcode200.java new file mode 100644 index 00000000..194d8ab8 --- /dev/null +++ b/leetcode200.java @@ -0,0 +1,36 @@ +class leetcode200 { + //use dfs + void dfs(char[][] grid, int r, int c) { + int n1 = grid.length; + int n2 = grid[0].length; + if (r < 0 || c < 0 || r >= n1 || c >= n2 || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + dfs(grid, r - 1, c); + dfs(grid, r + 1, c); + dfs(grid, r, c - 1); + dfs(grid, r, c + 1); + } + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + + int n1 = grid.length; + int n2 = grid[0].length; + int numIsl = 0; + for (int r = 0; r < n1; ++r) { + for (int c = 0; c < n2; ++c) { + if (grid[r][c] == '1') { + ++numIsl; + dfs(grid, r, c); + } + } + } + + return numIsl; + } +} diff --git a/leetcode547.java b/leetcode547.java new file mode 100644 index 00000000..cbd6a8a3 --- /dev/null +++ b/leetcode547.java @@ -0,0 +1,22 @@ +public class leetcode547 { + //use dfs + public void dfs(int[][] M, int[] visited, int i) { + for (int j = 0; j < M.length; j++) { + if (M[i][j] == 1 && visited[j] == 0) { + visited[j] = 1; + dfs(M, visited, j); + } + } + } + public int findCircleNum(int[][] M) { + int[] visited = new int[M.length]; + int count = 0; + for (int i = 0; i < M.length; i++) { + if (visited[i] == 0) { + dfs(M, visited, i); + count++; + } + } + return count; + } +} \ No newline at end of file