版权声明:Dirichlet_zju https://blog.csdn.net/Dirichlet_zju/article/details/84715654
目录
2.2.节点插入与删除(后面二叉搜索树与平衡二叉树会分别讲)
一、树与二叉树
1.树的存储
一般来说,树采用链表来定义:
typedef struct Node *PtrToNode;
typedef struct PtrToNode Tree;
typedef struct PtrToNode Position;
typedef int ElementType;
struct Node{
ElementType Data;
Position Left, Right;
};
当然,在静态树中,也可以利用数组来表示:
const int MAXN = 1000;
Typedef int ElementType;
struct Node{
ElementType Data;
int left, right;//Tree中的下标
}Tree[MAXN];
2.树的基本操作
这里以链表指针表示的动态二叉树为例。
2.1.节点查找
Position Find(Tree T, ElementType X){
if(T==NULL) return NULL;//没找到
if(T->data==X) return T;
return Find(T->left; X);
return Find(T->right; X);
}
2.2.节点插入与删除(后面二叉搜索树与平衡二叉树会分别讲)
二、树的遍历
1.二叉树的遍历
1.1.前序遍历
1)递归解法:
/*树的前序遍历
*使用:递归
*思路:先输出头结点,再遍历左子树和右子树
*/
void PreOrderTraversal(Tree T){
if(T){
printf("%d\n", T->data);
PreOrderTraversal(T->left);
PreOrderTraversal(T->right);
}
}
2)非递归解法:注意是或的关系,且进入循环前不push
/*树的前序遍历
*使用:栈stack,#include<stack>
*思路:先输出头结点,再遍历左子树和右子树
*/
void PreOrderTraversal(Tree T){
stack<Node> S;
Tree Tmp=T;
while(Tmp || !S.empty()){//树非空或堆栈非空
while(Tmp){ //树非空,打印并寻找左子树
printf("%d\n", Tmp->data);
S.push(Tmp);
Tmp=Tmp->left;
}
if(!S.empty()){ //堆栈非空,弹出并寻找右子树
Tmp=S.top();
S.pop();
Tmp=Tmp->right;
}
}
}
1.2.中序遍历
1)递归解法
/*树的中序遍历
*使用:递归
*思路:先遍历左子树,再输出头结点和右子树
*/
void InOrderTraversal(Tree T){
if(T){
InOrderTraversal(T->left);
printf("%d\n", T->data);
InOrderTraversal(T->right);
}
}
2)非递归解法
/*树的前序遍历
*使用:栈stack,#include<stack>
*思路:先遍历左子树,再输出头结点和右子树
*/
void InOrderTraversal(Tree T){
stack<Node> S;
Tree Tmp=T;
while(Tmp || !S.empty()){//树非空或堆栈非空
while(Tmp){ //树非空,打印并寻找左子树
S.push(Tmp);
Tmp=Tmp->left;
}
if(!S.empty()){ //堆栈非空,弹出并寻找右子树
printf("%d\n", Tmp->data);
Tmp=S.top();
S.pop();
Tmp=Tmp->right;
}
}
}
1.3.后序遍历
1)递归解法
/*树的后序遍历
*使用:递归
*思路:先遍历左子树和右子树,再输出头结点
*/
void PastOrderTraversal(Tree T){
if(T){
PastOrderTraversal(T->left);
printf("%d\n", T->data);
PastOrderTraversal(T->right);
}
}
2)非递归解法:需要在节点中加入bool类型的IsFirstTraversal标志位
void PastOrderTraversal(Tree T){//后续非递归
Tree Tmp = T;
stack<Tree> S;
while(Tmp || !S.empty()){
while(Tmp){
S.push(Tmp);
Tmp=Tmp->left;
}
if(!S.empty()){
Tmp=S.top();
S.pop();
if(Tmp->IsFirstTraversal){
Tmp->IsFirstTraversal=false;
S.push(Tmp);
Tmp=Tmp->right;
}
else{
cout<<Tmp->data;
}
}
}
}
1.4.层序遍历
层序遍历一般用非递归实现:
/*树的层序遍历
*使用:队列
*思路:头结点加入队列;之后头结点弹出队列并把其儿子入队;
* 循环直至队列为空*/
void LevelOrderTraversal(Tree T){
queue<Tree> Q;
if(T==NULL) return;//边界条件
Q.push(T);
while(!Q.empty()){
T=Q.front();
Q.pop();
printf("%d ", T->data);
if(T->left) Q.push(T->left);
if(T->right) Q.push(T->right);
}
}
1.5.已知遍历顺序建树
例:已知后序遍历和中序遍历,建树并输出层序遍历:
#include <iostream>
#include <queue>
//注意点:1.边界条件;
// 2.后序遍历开始位置和结束位置的确定不能依靠从中序遍历计算到的inRoot
using namespace std;
const int maxn = 35;
typedef struct Node *Tree;
struct Node{
int data;
Tree left=NULL, right=NULL;
};
int n, post[maxn], in[maxn];
Tree T;
/*后序遍历最后一位是根节点rootKey,
*在中序遍历找到rootKey所在位置root后,
*中序left到root-1是左子树,root+1到right是右子树
*后序left到root-1是左子树,root到right-1是右子树
*递归,找到边界条件:
*/
Tree GenerateTree(int postL, int postR, int inL, int inR){
if(postR<postL) return NULL;//边界条件
int inRoot;//中序遍历根节点的位置
//寻找根节点inRoot
for(inRoot=0; inRoot<inR+1; inRoot++){
if(in[inRoot]==post[postR]) break;
}
int numleft = inRoot-inL;//左子树长度,用于确定后序遍历左右子树范围
Tree tr = (Tree)malloc(sizeof(struct Node));//新建树节点,也可以用Tree tr = new Tree;不加括号
tr->data=post[postR];//根节点data赋值
tr->left=GenerateTree(postL, postL+numleft-1, inL, inRoot-1);//递归返回左子树
tr->right=GenerateTree(postL+numleft, postR-1, inRoot+1, inR);//递归返回右子树
return tr;
}
int pointer = 0;//空格控制器
void LevelOrderTraversal(){
queue<Tree> q;
q.push(T);
Tree tmp=T;
while(!q.empty()){
tmp=q.front();
q.pop();
cout<<tmp->data;
pointer++;
if(pointer!=n) cout<<" ";//空格控制
if(tmp->left) q.push(tmp->left);
if(tmp->right) q.push(tmp->right);
}
}
int main()
{
cin>>n;
for(int i=0; i<n; i++) cin>>post[i];//输入后序遍历
for(int i=0; i<n; i++) cin>>in[i];//输入中序遍历
T = GenerateTree(0, n-1, 0, n-1);
LevelOrderTraversal();
return 0;
}
2.一般树的遍历
本节针对节点个数不限且子节点没有先后次序的树。方便起见,对于一般树考试时希望采用静态写法:
const int maxn = 10000;
typedef int dataType;
struct Node{
dataType data;
vector<int> child;
}Tree[maxn];
由于子节点个数不定,因此一般树的遍历只考虑先序遍历和层序遍历。
2.1.先序遍历(递归、不考虑子节点排序)
void PreOrderTraversal(int u){
printf("%d\n", T[u].data);
for(int i=0; i<T[u].child.size(); i++){
PreOrderTraversal(T[u].child[i]);
}
}
2.2.层序遍历
void LevelOrderTraversal(int u){//非递归
queue<int> Q;
Q.push(u);
while(!Q.empty()){
u=Q.front();
Q.pop();
cout<<u<<endl;
for(int i=0; i<Tree[u].child.size(); i++){
Q.push(Tree[u].child[i]);
}
}
}
三、二叉搜索树
1.定义
二叉搜索树(Binary Search Tree, BST)又称作二叉查找树、排序二叉树等,是二叉树的一种特殊形式。BST的每一个节点的左子节点小于(或小于等于)该节点,右节点大于该节点,形成一种从左到右从小到大的存储形式。
性质:二叉搜索树的中序遍历有序
2.操作集
2.1.树的结构与建树
typedef struct Node *BST;
typedef BST Position;
struct Node{
int data;
Position left, right;
};
//建树
BST Create(int data[], int n){//数组以及数组中数据个数
BST T = NULL;//不能新建,最好为空
for(int i=0; i<n; i++){
Insert(data[i], T);
}
return T;
}
2.2.查找
1)查找任意元素
//查找
Position Search(int X, BST T){
if(T==NULL) return NULL;
if(X==T->data) return T;
else if(X<T->data) return Search(X, T->left);
else return Search(X, T->right);
}
2)查找最值
Position FindMin(BST T){//查找最小值
Position tmp = T;
if(tmp==NULL) return NULL;
while(tmp->left != NULL){
tmp=tmp->left;
}
return tmp;
}
Position FindMax(BST T){//查找最大值
Position tmp = T;
if(tmp==NULL) return NULL;
while(tmp->right != NULL){
tmp=tmp->right;
}
return tmp;
}
2.3.插入
//插入
void Insert(int X, BST T){
if(T==NULL){
BST tmp = new BST;
tmp->data=X;
tmp->left=tmp->right=NULL;
T=tmp;
}
if(X==T->data) return;//节点已存在
else if(X<T->data) Insert(X, T->left);
else Insert(X, T->right);
}
2.4.删除
void Delete(int X, BST T){
Position P = Search(X, T);//查找被删除元素位置
if(P->left==NULL && P->right==NULL){//没有子节点
free(P);//有待商榷?
}
//有两个子节点,找右子节点最小值替换
else if(P->right!=NULL && P->left!=NULL){
Position minRight = FindMin(P->right);
P->data=minRight->data;//替换data
Delete(minRight->data, P->right);
}
else if(P->left!=NULL){//左不为空,右为空
Position tmp = P->left;
P->data=tmp->data;
P->left=tmp->left;
P->right=tmp->right;
free(tmp);
}
else if(P->right!=NULL){//右不为空
Position tmp = P->right;
P->data=tmp->data;
P->left=tmp->left;
P->right=tmp->right;
free(tmp);
}
}
四、平衡二叉树
1.定义
所谓二叉平衡树,是在保持二叉搜索树性质的,且左右子树高度差的绝对值不超过1的二叉树。
性质:查询时间复杂度O(logn)
结构:仅增加了height即该节点的高度,叶节点为1,其上每一层父节点的高度是其左右子树最大值+1
typedef struct Node* Position;
typedef Position Tree;
struct Node{
int data, height;
Position left, right;
};
2.基本操作集
2.1.基本函数
Position NewNode(int v){//新建data是v的点
Position T = (Position)malloc(sizeof(struct Node));
T->data=v;
T->height=1;
T->left=T->right=NULL;
return T;
}
int GetHeight(Position P){
if(P==NULL) return 0;//空节点高度为0
else return P->height;
}
int GetBalanceFactor(Position P){
return GetHeight(P->left) - GetHeight(P->right);
}
void UpdateHeight(Position P){
P->height = max(GetHeight(P->left), GetHeight(P->right)) + 1;//左右儿子最大值+1
}
2.2.查找
//同二叉搜索树
Tree Search(int X, Tree T){
if(T==NULL) return NULL;
if(X==T->data) return T;
if(X<T->data) return T->left;
else(X<T->data) return T->right;
}
2.3.插入
//插入
void Insert(int X, Tree T){
if(T==NULL){
Position P = NewNode(X);
return;
}
if(X<T->data){
Insert(X, T->left);
UpdateHeight(T);//从低往上重置树各节点高度
if(GetBalanceFactor(T)==2){//判断本节点平衡因子是否为2,左大右小为正
if(GetBalanceFactor(T->left)==1){//LL型
T = RightRotate(T);
}
else if(GetBalanceFactor(T->right)==-1){//LR型
T->left = LeftRotate(T->left);
T = RightRotate(T);
}
}
}
else{
Insert(X, T->right);
UpdateHeight(T);
if(GetBalanceFactor(T)==-2){
if(GetBalanceFactor(T->right==-1)){//RR型
LeftRotate(T);
}
else if(GetBalanceFactor(T->right==1)){//RL型
RightRoTate(T->right);
LeftRotate(T);
}
}
}
}
其中左旋右旋分别是:
//左旋
Position LeftRotate(Tree T){
Tree tmp = T->right;//左旋就找右儿子
T->right=tmp->left;//第一步
tmp->left=T;//第二步
UpdateHeight(T);//先更新考下的节点的高度
UpdateHeight(tmp);//在更新考上节点的高度
return tmp;//第三步
}
//右旋
Position RightRotate(Tree T){
Tree tmp = T->left;
T->left=tmp->right;
tmp->right=T;
UpdateHeight(T);
UpdateHeight(tmp);
return tmp;
}