一。递归定义
① 要么二叉树没有根结点,是一个空树
②要么二叉树由根结点、左子树、右子树组成,且左子树和右子树都是二叉树
二叉树的许多算法都需要直接用到这个递归的定义来实现算法。
二。存储结构
使用链表来定义,定义方式如下:
struct node{
typename data; //数据域
node* lchild; //指向左子树根结点的指针
node* rchild; //指向右子树根结点的指针
};
由于二叉树建树前根结点不存在,因此其地址一般设为NULL
node* root = NULL;
如果需要新建结点(例如往二叉树中插入结点的时候),可以使用下面函数:
node* newNode(int v){
node* Node = new node; //申请一个node型变量的地址空间
Node->data = v; //结点权值为v
Node->lchild = Node->rchild = NULL; //初始状态下没有左右孩子
return Node; //返回新建结点的地址
}
三。基本操作
1. 结点的查找修改
思路:先判断当前结点是否是需要查找的结点,如果是则对其进行修改操作,如果不是,则分别往该结点的左孩子和右孩子递归,直到当前结点为NULL为止
void search(node* root, int x, int newdata){
if(root == NULL){
return; //空树(递归边界)
}
if(root->data == x){ //找到数据域为x的结点,把它修改为newdata
root->data = newdata;
}
search(root->lchlid, x, newdata); //往左子树搜索x(递归式)
search(root->rchild, x, newdata); //往右子树搜索x(递归式)
}
2. 结点的插入
注意,insert函数必须加引用,而前面的search函数不用加引用。因为search函数中修改的是指针root指向的内容,而不是root本身。
四。二叉树遍历
1. 先序遍历
根结点->左子树->右子树
void preorder(node* root){
if(root == NULL){
return; //到达空树,递归边界
}
printf("%d\n", root->data); //访问根结点root,例如将其数据域输出
preorder(root->lchild); //访问左子树
preorder(root->rchild); //访问右子树
}
2. 中序遍历
左子树->根结点->右子树
void inorder(node* root){
if(root == NULL){
return; //到达空树,递归边界
}
preorder(root->lchild); //访问左子树
printf("%d\n", root->data); //访问根结点root,例如将其数据域输出
preorder(root->rchild); //访问右子树
}
只要知道根结点,就可以根据根结点在中序遍历序列中的位置区分出左子树和右子树
3. 后序遍历
左子树->右子树->根结点
void postorder(node* root){
if(root == NULL){
return; //到达空树,递归边界
}
preorder(root->lchild); //访问左子树
preorder(root->rchild); //访问右子树
printf("%d\n", root->data); //访问根结点root,例如将其数据域输出
}
4. 层序遍历
层序遍历相当于对二叉树从根结点开始的广度优先搜索。
void LayerOrder(node* root){
queue<node*> q; //注意队列中是存地址
q.push(root); //将根结点地址入队
while(!q.empty()){
node* now = q.front(); //取出队首元素
q.pop();
printf("%d ", now->data); //访问队首元素
if(now->lchild != NULL) q.push(now->lchild); //左子树非空
if(now->rchild != NULL) q.push(now->rchild); //右子树非空
}
}
5. 根据先序遍历序列和中序遍历序列构造二叉树
node* create(int prel, int preR, int inL, int inR){
if(preL > preR){
return NULL; //先序序列长度小于等于0时,直接返回
}
node* root = new node; // 新建一个新的结点,用来存储当前二叉树的根结点
root->data = pre[preL]; //新结点的数据域为根结点的值
int k;
for(k == inL; k<=inR; k++){
if(in[k] == pre[preL]){ //在中序序列中找到in[k] == pre[preL]结点
break;
}
}
int numLeft = k - inL; //左子树的结点个数
//左子树的先序区间为[preL+1, preL+numLeft],中序区间为[inL,k-1]
//返回左子树的根结点地址,赋值给root的左指针
root->lchild = create(preL+1, preL+numLeft, inL, k-1);
//右子树的先序区间为[preL+numLeft+1, preR],中序区间为[k+1,inR]
//返回右子树的根结点地址,赋值给root的右指针
root->rchild = create(preL+numLeft+1, preR, k+1, inR);
return root; //返回根结点地址
}