From a2f9bc86ddb5f778a2cc2e9183f62037e2ff8a9d Mon Sep 17 00:00:00 2001 From: chenximing <243744327@qq.com> Date: Tue, 17 Jul 2018 00:00:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +- .../\344\272\214\345\217\211\346\240\221.md" | 204 +++++------------- .../\346\237\245\346\211\276.md" | 80 ------- 3 files changed, 56 insertions(+), 241 deletions(-) delete mode 100644 "\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\237\245\346\211\276.md" diff --git a/README.md b/README.md index 8c4310d..70b328c 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,12 @@ ## 数据结构与算法 -* [1.查找](数据结构与算法/查找.md) -* [2.排序](数据结构与算法/排序.md) -* [3.二叉树](数据结构与算法/二叉树.md) -* [4.堆](数据结构与算法/堆.md) -* [5.平衡查找树](数据结构与算法/平衡查找树.md) -* [6.哈希表](数据结构与算法/哈希表.md) -* [7.图](数据结构与算法/图.md) +* [1.排序](数据结构与算法/排序.md) +* [2.二叉树](数据结构与算法/二叉树.md) +* [3.堆](数据结构与算法/堆.md) +* [4.平衡查找树](数据结构与算法/平衡查找树.md) +* [5.哈希表](数据结构与算法/哈希表.md) +* [6.图](数据结构与算法/图.md) * [算法题总结](数据结构与算法/算法题总结.md) > 理论知识参考: diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221.md" index 22a9554..a59601a 100644 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221.md" +++ "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\344\272\214\345\217\211\346\240\221.md" @@ -1,22 +1,58 @@ -* [(前中后序)遍历](#前中后序遍历) +* [两种特殊二叉树](#两种特殊二叉树) +* [二叉树定理](#二叉树定理) +* [前中后序遍历](#前中后序遍历) * [递归版](#递归版) * [迭代版](#迭代版) - * [二叉树重建](#二叉树重建) - * [根据前序、中序序列重建二叉树](#根据前序中序序列重建二叉树) - * [根据前序、后序序列重建二叉树(不可行)](#根据前序后序序列重建二叉树不可行) - * [根据中序、后序序列重建二叉树](#根据中序后序序列重建二叉树) - * [二叉树序列化](#二叉树序列化) -* [(广度优先)遍历](#广度优先遍历) -* [路径](#路径) -* [深度](#深度) -* [理论知识](#理论知识) - - [两种特殊二叉树](#两种特殊二叉树) - - [二叉树定理](#二叉树定理) +# 两种特殊二叉树 +* **满二叉树**:除叶子节点外的所有分支节点都含有2个非空子节点的二叉树 +* **完全二叉树**:除了最后一层,其余层都是“满”的,这样的二叉树是完全二叉树 + +![](../pic/al-tree-1.png) + +
+
+ +# 二叉树定理 + +### 1)任意二叉树度数为2节点的个数等于叶节点个数减1 + +当只有1个节点时,度为0。每派生出1度,就会多出1个节点。派生出的度和派生出的节点数一定相等。那么就得出了总度数和节点总数的关系: + +`节点总数 = 总度数 + 1` + +设度数为2的节点数为`X2`,度数为1的节点数为`X1`,度数为0的节点数为`X0`。可以得出如下关系式: + +`X2+X1+X0=2X2+X1+1;` + +推出 + +`X2=X0-1`; + +因此,**度数为2的节点个数等于叶节点数减1** + +### 2)满二叉树定理:非空满二叉树的叶节点数等于其分支节点数加1 + +如果已知前一个结论,那么这个定理显然成立。下面分析如果不知道前一个结论,怎么证明 + +对于只有1个节点的树,该定理成立。从这开始思考,每产生1个分支节点(度数为2)。叶子节点数也会加1。因为要产生一个分支节点,那么这个新的分支节点必然是原来的叶子节点,而新的分支节点又生成了2个新的叶子节点。因此叶子节点的总数先是减1然后加2,因此总数加1。因此,产生n个分支节点时,也产生了n个叶子节点,由于最初只有1个叶子节点,所以该定理成立 + +### 3)一颗非空二叉树空子树的数目等于其节点数目加1 + +考虑只有1个根节点的二叉树:它有2个空子树,1个节点,因此结论成立。从这里开始考虑,每产生1个节点。空子树便会先减1然后加2。就和上面结论中每多出1个分支节点,叶子节点的变化一样。因此在原来结论的基础上,由于空子树和节点等量增长。所以结论成立 + +
+
+ +# 前中后序遍历 + +* **前序**遍历:根->左->右 +* **中序**遍历:左->根->右 +* **后序**遍历:左->右->根 假设树节点的定义如下: @@ -29,12 +65,6 @@ struct TreeNode { }; ``` -# (前中后序)遍历 - -* 前序遍历:根->左->右 -* 中序遍历:左->根->右 -* 后序遍历:左->右->根 - ## 递归版 ```c++ @@ -147,139 +177,5 @@ void postorderTraversalIteration(TreeNode *root) 对于后序遍历,由于其访问序列为:左->右->根。因此还有一种方法,可以按类似前序遍历的方式:根->右->左,然后对得到的结果反序 -> 相关题目: -> * 剑指offer(8):二叉树的下一个节点 -> * 剑指offer(28):对称的二叉树 - -## 二叉树重建 - -### 根据前序、中序序列重建二叉树 - -前序序列的特点:{首元素,左子树节点,右子树节点} -中序序列的特点:{左子树节点,首元素,右子树节点} - -因此,可以根据前序序列的首元素创建根节点,然后遍历中序序列,找到首元素,递归处理左子树和右子树。函数返回(树或子树的)根节点,每个新创建节点分别将左右子节点设置为递归处理的返回节点 - -**如何处理前序序列和中序序列不匹配等非法输入的情况?(throw std::exception("Invalid input"))** - -> 相关题目:剑指offer(7):重建二叉树 - -### 根据前序、后序序列重建二叉树(不可行) - -后序序列中,根节点在末尾,前序序列中,根节点在首部,无法确定左右子树节点的范围,因此无法重建 - -### 根据中序、后序序列重建二叉树 - -和根据前序、中序序列重建的思想一样 - -### 二叉树序列化 - -序列中存储**空节点信息**(如使用‘$’)可以使用前序序列重建二叉树 - -> 相关题目: -> * 剑指offer(37):序列化二叉树 - -# (广度优先)遍历 - -**使用队列** - -```c++ -void depthTraversal(TreeNode *root) -{ - deque d; - if(root) - d.push_back(root); - - TreeNode *curr; - while(!d.empty()){ - curr = d.front(); - d.pop_front(); - - cout << curr << " "; - - if(curr->left) - d.push_back(curr->left); - if(curr->right) - d.push_back(curr->right); - } -} -``` - -> 相关题目: -> * 剑指offer(32):不分行从上到下打印二叉树(题目一) -> * 剑指offer(32):分行从上到下打印二叉树(题目二) -> * 剑指offer(32):之字形打印二叉树(题目三) - -# 路径 - -使用**辅助空间**保存路径(如vector) - -```c++ -/* - * 函数获取到达节点dest的路径 - * dest是否为nullptr的检查应该在外层,因为该函数是一个递归的过程,一旦dest不为空,后续的递归都不会为空 - * 如果查找的节点不存在树中,可以通过path中的元素或者函数返回值判断 - */ -bool getPathCore(TreeNode *root,TreeNode *dest,vector &path) -{ - if(!root) - return false; - - //首先假设当前节点是所要查找路径中的一个节点,插入 - path.push_back(root); - - if(root == dest) //先判断当前节点是否是查找的节点(前序遍历) - return true; - - bool find = false; - - //如果左子树中找到,则不会继续对右子树进行查找 - find = getPathCore(root->left,dest,path) || getPathCore(root->right,dest,path); - - //左子树或右子树中找到则直接返回(因为路径中肯定包含了当前节点,不返回会弹出) - if(find) - return find; - - //如果(左右)子树中都没找到,说明所要查找的路径不经过这个节点,弹出 - path.pop_back(); - - return false; -} -``` - -> 相关题目: -> * 剑指offer(34):二叉树中和为某一值的(所有)路径 - -# 深度 - -```c++ -int TreeDepth(TreeNode* pRoot) -{ - if(!pRoot) - return 0; - - int depth1 = 1 + TreeDepth(pRoot->left); - int depth2 = 1 + TreeDepth(pRoot->right); - - return depth1 > depth2 ? depth1 : depth2; -} -``` - -> 相关题目: -> * 剑指offer(55):二叉树的深度(题目一) -> * 剑指offer(55):平衡二叉树AVL(题目二) - -# 理论知识 - -## 两种特殊二叉树 - -* **满二叉树**:除叶子节点外的所有分支节点都含有2个非空子节点的二叉树 -* **完全二叉树**:除了最后一层,其余层都是“满”的,这样的二叉树是完全二叉树 - -![](../pic/al-tree-1.png) - -## 二叉树定理 - -1. **满二叉树定理**:非空满二叉树的叶节点数等于其分支节点数加1 -2. 一颗非空二叉树空子树的数目等于其节点数目加1 -3. 任意二叉树度数为2节点的个数等于叶节点个数减1 \ No newline at end of file +
+
\ No newline at end of file diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\237\245\346\211\276.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\237\245\346\211\276.md" deleted file mode 100644 index b251e2a..0000000 --- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\237\245\346\211\276.md" +++ /dev/null @@ -1,80 +0,0 @@ -* [二分查找](#二分查找) - * [1.查找元素第一次出现的下标](#1查找元素第一次出现的下标) - * [2.查找元素最后一次出现的下标](#2查找元素最后一次出现的下标) - -
- -## 二分查找 - -```c++ -int BinarySearch(vector data,int k){ - int sz = data.size(); - int l = 0,r = sz - 1; - while(l <= r){ - int mid = (l + r) >> 1; - if(data[mid] > k) - r = mid - 1; - else if(data[mid] < k) - l = mid + 1; - else - return mid; - } - - return -1; -} -``` - -### 1.查找元素第一次出现的下标 - -```c++ -int GetFirstK(vector data,int k){ - int sz = data.size(); - int l = 0,r = sz - 1; - while(l <= r){ - int mid = (l + r) >> 1; - if(data[mid] > k) - r = mid - 1; - else if(data[mid] < k) - l = mid + 1; - else{ - if(mid == 0 || data[mid - 1] != k) - return mid; - else - r = mid - 1; - } - } - - return -1; -} -``` - -### 2.查找元素最后一次出现的下标 - -```c++ -int GetLastK(vector data,int k){ - int sz = data.size(); - int l = 0,r = sz - 1; - while(l <= r){ - int mid = (l + r) >> 1; - if(data[mid] > k) - r = mid - 1; - else if(data[mid] < k) - l = mid + 1; - else{ - if(mid == sz - 1 || data[mid + 1] != k) - return mid; - else - l = mid + 1; - } - } - - return -1; -} -``` - -> 相关题目: -> * 剑指offer(11):[旋转数组的最小数字](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=2&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking) -> * Leetcode(33):[旋转后数组中查找数字](https://leetcode.com/problems/search-in-rotated-sorted-array/description/) -> * 剑指offer(53):[数字在排序数组中出现的次数](https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=2&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) -> * 剑指offer(53):[0~n-1中缺失的数字](code/53-2.md) -> * 剑指offer(53):[数组中数值和下标相等的元素](code/53-3.md) \ No newline at end of file