二叉树理论基础
二叉树的种类
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。
完全二叉树
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
二叉搜索树
二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树
平衡二叉搜索树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树的存储方式
二叉树可以链式存储,也可以顺序存储。
链式存储方式就用指针, 顺序存储的方式就是用数组。
链式存储如图:
顺序存储的方式如图:
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
二叉树的遍历方式
二叉树主要有两种遍历方式:
- 深度优先遍历:先往深走,遇到叶子节点再往回走。
- 广度优先遍历:一层一层的去遍历。
- 深度优先遍历
- 前序遍历(递归法,迭代法)中左右
- 中序遍历(递归法,迭代法)左中右
- 后序遍历(递归法,迭代法)左右中
- 广度优先遍历
- 层次遍历(迭代法)
定义一个二叉树节点的结构体
struct TreeNode {
int val;
struct TreeNode* left;
struct TreeNode* right;
};
初始化一个 TreeNode
结构体的实例
struct TreeNode* createNode(int value) {
struct TreeNode* node = (struct TreeNode*)malloc(sizeof(struct TreeNode));
node->val = value;
node->left = NULL;
node->right = NULL;
return node;
}
java写法
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val) { this.val = val; }
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
递归遍历
//前序遍历:
void preOrder(struct TreeNode* root, int* ret, int* returnSize) {
if(root == NULL)
return;
ret[(*returnSize)++] = root->val;
preOrder(root->left, ret, returnSize);
preOrder(root->right, ret, returnSize);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
int* ret = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
preOrder(root, ret, returnSize);
return ret;
}
//中序遍历:
void inOrder(struct TreeNode* node, int* ret, int* returnSize) {
if(!node)
return;
inOrder(node->left, ret, returnSize);
ret[(*returnSize)++] = node->val;
inOrder(node->right, ret, returnSize);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
int* ret = (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
inOrder(root, ret, returnSize);
return ret;
}
//后序遍历:
void postOrder(struct TreeNode* node, int* ret, int* returnSize) {
if(node == NULL)
return;
postOrder(node->left, ret, returnSize);
postOrder(node->right, ret, returnSize);
ret[(*returnSize)++] = node->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize){
int* ret= (int*)malloc(sizeof(int) * 100);
*returnSize = 0;
postOrder(root, ret, returnSize);
return ret;
}
java写法
// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<Integer>();
preorder(root, result);
return result;
}
public void preorder(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
result.add(root.val);
preorder(root.left, result);
preorder(root.right, result);
}
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inorder(root, res);
return res;
}
void inorder(TreeNode root, List<Integer> list) {
if (root == null) {
return;
}
inorder(root.left, list);
list.add(root.val); // 注意这一句
inorder(root.right, list);
}
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postorder(root, res);
return res;
}
void postorder(TreeNode root, List<Integer> list) {
if (root == null) {
return;
}
postorder(root.left, list);
postorder(root.right, list);
list.add(root.val); // 注意这一句
}
}
二叉树的迭代遍历
前序遍历(迭代法)
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。(要先加入 右孩子,再加入左孩子这样出栈的时候才是中左右的顺序。)
中序遍历(迭代法)
在迭代的过程中有两个操作:
- 处理:将元素放进result数组中
- 访问:遍历节点
前序遍历是中左右,先访问的元素是中间节点,要处理的元素也是中间节点。
中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中)。
在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
后序遍历(迭代法)
先序遍历是中左右,后续遍历是左右中,只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了,如下图:
前序遍历代码:
c
void preorderTraversal(struct TreeNode* root, int* returnSize, int* result) {
if (root == NULL)
return;
struct TreeNode* stack[100];
int top = -1;
stack[++top] = root;
while (top >= 0) {
struct TreeNode* node = stack[top--];
result[(*returnSize)++] = node->val;
if (node->right != NULL)
stack[++top] = node->right;
if (node->left != NULL)
stack[++top] = node->left;
}
}
中序遍历代码:
c
void inorderTraversal(struct TreeNode* root, int* returnSize, int* result) {
if (root == NULL)
return;
struct TreeNode* stack[100];
int top = -1;
struct TreeNode* cur = root;
while (top >= 0 || cur != NULL) {
while (cur != NULL) {
stack[++top] = cur;
cur = cur->left;
}
cur = stack[top--];
result[(*returnSize)++] = cur->val;
cur = cur->right;
}
}
后序遍历代码:
c
void postorderTraversal(struct TreeNode* root, int* returnSize, int* result) {
if (root == NULL)
return;
struct TreeNode* stack1[100];
struct TreeNode* stack2[100];
int top1 = -1;
int top2 = -1;
stack1[++top1] = root;
while (top1 >= 0) {
struct TreeNode* node = stack1[top1--];
stack2[++top2] = node;
if (node->left != NULL)
stack1[++top1] = node->left;
if (node->right != NULL)
stack1[++top1] = node->right;
}
while (top2 >= 0) {
struct TreeNode* node = stack2[top2--];
result[(*returnSize)++] = node->val;
}
}
java写法
// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right != null){
stack.push(node.right);
}
if (node.left != null){
stack.push(node.left);
}
}
return result;
}
}
// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()){
if (cur != null){
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
result.add(cur.val);
cur = cur.right;
}
}
return result;
}
}
// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result = new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.left != null){
stack.push(node.left);
}
if (node.right != null){
stack.push(node.right);
}
}
Collections.reverse(result);
return result;
}
}