二叉树基础
种类
- 满二叉树:一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上。
满二叉树的深度为k,有2^k-1个节点的二叉树。
- 完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
- 二叉搜索树:有序树,满足:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
- 平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,满足:
- 是一棵空树或它的左右两个子树的高度差的绝对值不超过1;
- 左右两个子树都是一棵平衡二叉树。
C++中常用容器 |
底层实现 |
map、set、multimap,multiset |
平衡二叉搜索树 |
unordered_map、unordered_set, unordered_map、unordered_map |
哈希表 |
遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。前中后序遍历。
- 广度优先遍历:一层一层的去遍历。层次遍历。
遍历顺序:
- 前序遍历:中左右
- 中序遍历:左中右
- 后序遍历:左右中
二叉树定义:
1 2 3 4 5 6
| struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} };
|
递归遍历
递归三要素:
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数。
前序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13
| void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; vec.push_back(cur->val); traversal(cur->left, vec); traversal(cur->right, vec); }
vector<int> preorderTraversal(TreeNode* root) { vector<int> result; traversal(root, result); return result; }
|
中序遍历:
1 2 3 4 5 6
| void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); vec.push_back(cur->val); traversal(cur->right, vec); }
|
后序遍历:
1 2 3 4 5 6
| void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); traversal(cur->right, vec); vec.push_back(cur->val); }
|
迭代遍历
迭代法:用栈来实现递归
中序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| vector<int> inorderTraversal(TreeNode* root) { vector<int> result; stack<TreeNode*> 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; }
|
前序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| vector<int> preorderTraversal(TreeNode* root) { vector<int> result; stack<TreeNode*> 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); if (node->left) st.push(node->left); st.push(node); st.push(NULL); } else { st.pop(); node = st.top(); st.pop(); result.push_back(node->val); } } return result; }
|
后序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| vector<int> postorderTraversal(TreeNode* root) { vector<int> result; stack<TreeNode*> st; if (root != NULL) st.push(root); while (!st.empty()) { TreeNode* node = st.top(); if (node != NULL) { st.pop(); st.push(node); st.push(NULL);
if (node->right) st.push(node->right); if (node->left) st.push(node->left);
} else { st.pop(); node = st.top(); st.pop(); result.push_back(node->val); } } return result; }
|
层序遍历
解法:广度优先遍历,使用辅助队列,先进先出
层序遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| vector<vector<int>> levelOrder(TreeNode* root) { queue<TreeNode*> que; if (root != NULL) que.push(root); vector<vector<int>> result; while (!que.empty()) { int size = que.size(); vector<int> vec; for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); vec.push_back(node->val); if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(vec); } return result; }
|
二叉树构造
中序与后序遍历序列构造二叉树
力扣题目链接
根据一棵树的中序遍历与后序遍历构造二叉树。
解法:对中序和后序遍历序列进行切割,找好切割区间,建议左闭右开。
中序数组 ==> 左中序数组 + 根 + 右中序数组
后序数组 ==> 左后序数组 + 右后序数组 + 根
递归求解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| TreeNode* traversal (vector<int>& inorder, vector<int>& postorder) { if (postorder.size() == 0) return NULL;
int rootValue = postorder[postorder.size() - 1]; TreeNode* root = new TreeNode(rootValue);
if (postorder.size() == 1) return root;
int delimiterIndex; for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) { if (inorder[delimiterIndex] == rootValue) break; }
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex); vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end() );
postorder.resize(postorder.size() - 1);
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size()); vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
root->left = traversal(leftInorder, leftPostorder); root->right = traversal(rightInorder, rightPostorder);
return root; }
|
构造最大二叉树
力扣题目地址
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
- 二叉树的根是数组中的最大元素。
- 左子树是通过数组中最大值左边部分构造出的最大二叉树。
- 右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
解析:首先找到序列的最大值,以最大值构造根节点,左子树区间 + 根节点(最大值)+ 右子树区间,递归构造子区间至少一个值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| TreeNode* constructMaximumBinaryTree(vector<int>& nums) { TreeNode* node = new TreeNode(0); if (nums.size() == 1) { node->val = nums[0]; return node; } int maxValue = 0; int maxValueIndex = 0; for (int i = 0; i < nums.size(); i++) { if (nums[i] > maxValue) { maxValue = nums[i]; maxValueIndex = i; } } node->val = maxValue; if (maxValueIndex > 0) { vector<int> newVec(nums.begin(), nums.begin() + maxValueIndex); node->left = constructMaximumBinaryTree(newVec); } if (maxValueIndex < (nums.size() - 1)) { vector<int> newVec(nums.begin() + maxValueIndex + 1, nums.end()); node->right = constructMaximumBinaryTree(newVec); } return node; }
|