diff --git a/README.md b/README.md index 75e5ef03..6345d83e 100644 --- a/README.md +++ b/README.md @@ -74,17 +74,52 @@ * 二叉树经典题目 - -(待补充.....) +(补充ing) # 算法模板 -## 二分法 -(待补充....) +## 二分查找法 + +``` +class Solution { +public: + int searchInsert(vector& nums, int target) { + int n = nums.size(); + int left = 0; + int right = n; // 我们定义target在左闭右开的区间里,[left, right) + while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间 + int middle = left + ((right - left) >> 1); + if (nums[middle] > target) { + right = middle; // target 在左区间,因为是左闭右开的区间,nums[middle]一定不是我们的目标值,所以right = middle,在[left, middle)中继续寻找目标值 + } else if (nums[middle] < target) { + left = middle + 1; // target 在右区间,在 [middle+1, right)中 + } else { // nums[middle] == target + return middle; // 数组中找到目标值的情况,直接返回下标 + } + } + return right; + } +}; + +``` ## KMP -(待补充....) +``` +void kmp(int* next, const string& s){ + next[0] = -1; + int j = -1; + for(int i = 1; i < s.size(); i++){ + while (j >= 0 && s[i] != s[j + 1]) { + j = next[j]; + } + if (s[i] == s[j + 1]) { + j++; + } + next[i] = j; + } +} +``` ## 二叉树 @@ -226,8 +261,24 @@ vector> levelOrder(TreeNode* root) { ### 二叉树深度 +``` +int getDepth(TreeNode* node) { + if (node == NULL) return 0; + return 1 + max(getDepth(node->left), getDepth(node->right)); +} +``` +(补充ing) + ### 二叉树节点数量 +``` +int countNodes(TreeNode* root) { + if (root == NULL) return 0; + return 1 + countNodes(root->left) + countNodes(root->right); +} +``` + + # LeetCode 最强题解: |题目 | 类型 | 难度 | 解题方法 | diff --git a/problems/0144.二叉树的前序遍历.md b/problems/0144.二叉树的前序遍历.md index 092a4b3b..107b4c40 100644 --- a/problems/0144.二叉树的前序遍历.md +++ b/problems/0144.二叉树的前序遍历.md @@ -177,9 +177,7 @@ public: ``` -那么后序遍历呢 - -先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序 +再来看后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序 ,然后在反转result数组,输出的结果顺序就是左右中了,如下图: ![前序到后序](https://img-blog.csdnimg.cn/20200808200338924.png) @@ -208,8 +206,52 @@ public: ``` +此时我们实现了前后中遍历的三种迭代法,是不是发现迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。 + +重头戏来了,接下来介绍一下统一写法。 + +#### 迭代法统一写法 + +我们以中序遍历为例,之前说使用栈的话,**无法同时解决处理过程和访问过程不一致的情况**,那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,标记就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 + +代码如下: +``` +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vector result; + stack st; + if (root != NULL) st.push(root); + while (!st.empty()) { + TreeNode* node = st.top(); + if (node != NULL) { + st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中 + if (node->right) st.push(node->right); // 添加右节点 + + st.push(node); // 添加中节点 + st.push(NULL); // 中节点访问过,但是还没有处理,需要做一下标记。 + + if (node->left) st.push(node->left); // 添加左节点 + } else { + st.pop(); // 将空节点弹出 + node = st.top(); // 重新取出栈中元素 + st.pop(); + result.push_back(node->val); // 加入到数组中 + } + } + return result; + } +}; +``` + +看代码有点抽象我们来看一下动画: + + +大家在看一下具体代码实现 + +接下来给大家介绍一种统一的写法。 我们再来看一下代码。 ## C++代码 diff --git a/video/中序遍历迭代(统一写法).mp4 b/video/中序遍历迭代(统一写法).mp4 new file mode 100644 index 00000000..6f3c1cc2 Binary files /dev/null and b/video/中序遍历迭代(统一写法).mp4 differ