一、二叉树的性质
定义:结点的度(结点拥有的子树数目称为结点的度)不超过2的树。
特点:
(a)每个结点最多有两个子结点。
(b)左子树和右子树是有顺序的,次序不能颠倒。
(c)即使某结点只有一个子树,也要区分左右子树。
二叉树的类型:
① 满二叉树:在满二叉树中,除了叶节点外,每个节点都有两个子节点,且所有叶节点都在同一层级上。
② 完全二叉树:完全二叉树是指除了最后一层外,其他层都是满的,并且最后一层的节点从左到右连续存在。
③ 二叉搜索树:二叉搜索树是一种有序的二叉树,对于每个节点,其左子树的值都小于节点的值,右子树的值都大于节点的值。这种有序性质使得二叉搜索树在查找、插入和删除等操作上有很高的效率。
④ 平衡二叉树:平衡二叉树是指任意节点的左子树和右子树的高度差不超过1的二叉树。平衡二叉树可以提高插入、删除和查找等操作的效率,常见的平衡二叉树有AVL树和红黑树。
二、二叉树的遍历
所谓前序,中序,后续遍历命名的由来是我们访问二叉树根节点的顺序。前序遍历就是优先访问根节点(中左右),中序遍历是第二个访问根节点(左中右),后续遍历就是访问完左右节点之后最后访问根节点(左右中)。
2.1 递归法
#include<iostream>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value): val(value), left(nullptr), right(nullptr){}
};
// 先序遍历
void preorder_traversal(TreeNode* root)
{
if(!root)return;
cout<<root->val<<" ";
preorder_traversal(root->left);
preorder_traversal(root->right);
}
// 中序遍历
void inorder_traversal(TreeNode* root)
{
if(!root)return;
if(root->left)inorder_traversal(root->left);
cout<<root->val<<" ";
if(root->right)inorder_traversal(root->right);
}
// 后序遍历
void postorder_traversal(TreeNode* root)
{
if(!root)return;
if(root->left)postorder_traversal(root->left);
if(root->right)postorder_traversal(root->right);
cout<<root->val<<" ";
}
int main()
{
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
cout<<"preorder:";
preorder_traversal(root);
cout<<endl<<"inorder:";
inorder_traversal(root);
cout<<endl<<"postorder:";
postorder_traversal(root);
}
2.2 非递归法
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value): val(value),left(nullptr),right(nullptr){}
};
// 先序遍历
void preorder_traversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur;
st.push(root);
while(!st.empty())
{
cur=st.top();
st.pop();
if(cur->right)st.push(cur->right);
if(cur->left)st.push(cur->left);
cout<<cur->val<<" ";
}
}
// 中序遍历 找最左 只存中
void inorder_traversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur=root;
while(cur||!st.empty())
{
// 找最左的结点
if(cur)
{
st.push(cur);
cur=cur->left;
}
else
{
cur=st.top();
st.pop();
cout<<cur->val<<" ";
// 中序遍历右子树
cur=cur->right;
}
}
}
// 后序遍历 找最左 只存中 注意遍历中间结点时需要查看右子树是否已经遍历过
void postorder_traversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur=root;
// last存放上一个遍历的节点
TreeNode* last=nullptr;
while(cur||!st.empty())
{
if(cur)
{
st.push(cur);
cur=cur->left;
}
else
{
cur=st.top();
st.pop();
// 右子树存在且未遍历过
if(cur->right&&cur->right!=last)
{
st.push(cur);
cur=cur->right;
}
// 右子树不存在或已遍历过
else
{
cout<<cur->val<<" ";
last=cur;
cur=nullptr;
}
}
}
}
// 层序遍历
void levelorder_traversal(TreeNode* root)
{
queue<TreeNode*> que;
TreeNode* cur;
que.push(root);
while(!que.empty())
{
cur=que.front();
que.pop();
if(cur->left)que.push(cur->left);
if(cur->right)que.push(cur->right);
cout<<cur->val<<" ";
}
}
int main()
{
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
cout<<"preorder:";
preorder_traversal(root);
cout<<endl<<"inorder:";
inorder_traversal(root);
cout<<endl<<"postorder:";
postorder_traversal(root);
cout<<endl<<"levelorder:";
levelorder_traversal(root);
}
三、二叉树的应用
在二叉树的应用中,通常需要对树递归,递归主要由三要素构成。
a) 递归结束的条件。
b) 递归传递的方式。
c) 是否有返回值。如果当前层递归只需要向下传递参数,向下判断条件,而不需要通过下一层递归的返回值来判断当前层条件时,通常不需要返回值;如果当前层递归需要通过下一层递归的返回值来判断当前层的条件时,则通常需要有返回值。
3.1 求二叉树的高度
#include<iostream>
#include<queue>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value): val(value),left(nullptr),right(nullptr){}
};
// 递归法 传入的参数为当前结点和当前节点的父节点的高度 返回的参数为当前节点左右子树高度的较大值
int length_tree_recursion(TreeNode* root,int length)
{
// 左子树和右子树的高度
int left_length,right_length;
// 当前结点不存在,直接返回父节点的高度
if(!root)return length;
// 当前结点存在,高度+1
length=length+1;
// 继续递归计算左右子树的高度
left_length=length_tree_recursion(root->left,length);
right_length=length_tree_recursion(root->right,length);
// 最终返回左右子树高度的较大值
return max(left_length,right_length);
}
// 非递归法 需要用层序遍历
int length_tree(TreeNode* root)
{
queue<TreeNode*> que;
TreeNode* cur;
int length=0;
que.push(root);
while(!que.empty())
{
// size记录当前高度的节点个数 常用于层序遍历
int size=que.size();
while(size--)
{
cur=que.front();
que.pop();
if(cur->left)que.push(cur->left);
if(cur->right)que.push(cur->right);
}
length++;
}
return length;
}
int main()
{
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
root->left->left->left = new TreeNode(8);
root->left->left->left->left = new TreeNode(9);
root->left->left->left->left->left = new TreeNode(10);
int length;
length=length_tree_recursion(root,0);
cout<<length<<endl;
length=length_tree(root);
cout<<length<<endl;
}
3.2 查找与根节点相距为K的节点
#include<iostream>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value):val(value),left(nullptr),right(nullptr){}
};
// 前序遍历递归, root代表当前节点, length代表上一层节点到根节点的距离, k代表要求距离
void find_node(TreeNode* root,int length,int k)
{
if(!root)return;
length++;
if(length==k)
{
cout<<root->val<<" ";
return;
}
find_node(root->left,length, k);
find_node(root->right,length,k);
}
int main()
{
TreeNode* root=new TreeNode(4);
root->left=new TreeNode(1);
root->left->right=new TreeNode(3);
root->left->right->left=new TreeNode(2);
root->right=new TreeNode(5);
root->right->right=new TreeNode(7);
root->right->right->right=new TreeNode(9);
int k=2;
find_node(root,-1,k);
}
3.3 在二叉搜索树中寻找第K大的值
#include<iostream>
#include<vector>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value): val(value), left(nullptr), right(nullptr){}
};
// 通过中序遍历得到的数组为升序排列
void inorder_traversal(TreeNode* root, vector<int>& arr)
{
if(!root)return;
inorder_traversal(root->left, arr);
arr.push_back(root->val);
inorder_traversal(root->right,arr);
}
int main()
{
TreeNode* root=new TreeNode(4);
root->left=new TreeNode(1);
root->left->right=new TreeNode(3);
root->left->right->left=new TreeNode(2);
root->right=new TreeNode(5);
root->right->right=new TreeNode(7);
root->right->right->right=new TreeNode(9);
vector<int> arr;
inorder_traversal(root, arr);
int K=5;
cout<<arr[arr.size()-K];
}
3.4 在二叉树中查找给定节点的祖先节点
#include<iostream>
using namespace std;
struct TreeNode{
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int value):val(value),left(nullptr),right(nullptr){}
};
// 传入参数为给定节点 返回值为当前节点是否为给定节点的祖先节点(true和false)
bool find_ancestor_node(TreeNode* root,TreeNode* givennode)
{
if(!root)return false;
// 如果当前节点的左或右子树为给定节点的祖先节点时 直接返回true
if(root->left==givennode||root->right==givennode)
{
cout<<root->val<<" ";
return true;
}
// 如果当前节点的左或右子树不是给定节点的祖先节点时 继续向下遍历 判断左右子树是否为给定节点的祖先节点
bool left_node=find_ancestor_node(root->left,givennode);
bool right_node=find_ancestor_node(root->right,givennode);
// 左右子树若为给定节点的祖先节点 则打印
if(left_node==true||right_node==true)cout<<root->val<<" ";
return left_node|right_node;
}
int main()
{
TreeNode* root=new TreeNode(4);
root->left=new TreeNode(1);
root->left->right=new TreeNode(3);
root->left->right->left=new TreeNode(2);
root->right=new TreeNode(5);
root->right->right=new TreeNode(7);
root->right->right->right=new TreeNode(9);
TreeNode* GivenNode=root->left->right;
find_ancestor_node(root,GivenNode);
}