二叉树的形式:
//二叉树结点的形式:以类似于链表结点的结构体构建二叉树
struct node {
int data;
node* lchild,rchild;
};
二叉树结点的构建:
//构造二叉树结点:使用自定义函数;
node* newNode(int w){//w是权值
node* Node=new node;//也可以写作node *Node=(node*)malloc(sizeof(node)),作用是给结点开辟空间;
Node->data=w;
Node->lchild=NULL;
Node->rchild=NUll;
return Node;
}
二叉树的寻找与修改:
//二叉树的寻找与修改;
//使用递归函数,递归函数==递归边界+递归式;
void search(node* root,int x,int newdata){//x是要寻找的值,newdata是要修改的值;
if(root==NULL){//递归边界
return ;
}
if(root->data==x){
root->data=newdata;
}
search(root->lchild,x,newdata);//递归式;
search(root->rchild,x,newdata);
}
二叉树的插入:
//二叉树结点的插入
//结点寻找失败的地方就是二叉树结点可插入的地方;
//也是使用递归函数;
void insert(node* &root,int x){//需要使用引用,因为这里创造了新的结点,对整个二叉树的结构造成了影响,特别是新建的结点对父节点造成的影响;
if(root==NULL){//递归边界;
//找到位置就创造结点';
root=newNode(x);//创造权值为x的新节点,使用自定义函数newNode;
return ;
}
if(由二叉树的性质,x应该插在左子树){
insert(root->lchild,x);//递归式;
}else {
insert(root->rchild,x);
}
}
二叉树的建立:
//二叉树的创建
node* Create(int data[ ],int n){
node* root=NULL;
for(int i=0;i<n;i++){
insert(root,data[i]);
}
return root;//返回根节点的地址;
}
//二叉树的遍历:
二叉树的遍历有4种方法,分别是先序,中序,后序遍历和层次遍历,其中的"先中后都是指根节点遍历所在的位置",且左子树的遍历永远在右子树前面;
前三者用的是DFS,后者则是用BFS
根据二叉树的递归定义:可以将树的遍历分解为对根节点,左子树,右子树这三部分的遍历;
三种用深度优先搜索的遍历:
//先序遍历:访问顺序为访问根->进入左子树->进入右子数;
void preorder(node* root){//perorder先序遍历;
if(root==NULL){//递归边界,树为空树的时候(树中没有结点,根节点为NULL);
return ;
}
printf("%d",root->data);
preorder(root->lchild);
preorder(root->rchild);
}
//中序遍历:访问顺序为,左->根->右;
void inorder(node* root){
if(root==NULL)return ;
inorder(root->lchild);
printf("%d\n",root->data);//根就是要操作的结点;
inorder(root->rchild);
}
//后续遍历:访问顺序为:左->右->根;
void postorder(node* root){
if(root==NULL)return ;
postorder(root->lchild);
postorder(root->rchild);
printf("%d",root->data);
}
//可以通过访问的结点的顺序从而确定一棵树,先序(第一个为根结点)和后序(最后一个为根节点)遍历都只能确定根结点,只有中序遍历可以将左右子树分开
//方法是:从两个遍历序列中的一个确认根节点,从而从中序遍历中区分根节点的左右子树,再根据对比和遍历性质从而求出该而二叉树;
层序遍历:
//层序遍历(广度优先搜索)
//层序遍历,从根节点开始从上往下逐层进行遍历,同一层遵循从左到右遍历,即从上到下,从左到右;
//这种从左往右的访问顺序就像队伍一样,且每访问一个队首元素都会把其左右孩子放入;
void layerOrder(node* root){
queue<node*> Q;//注意是指针队列,因为可能要对该地址下的数据进行修改,因此需要的是node地址,而不是node副本;
Q.push(root);
while(!Q.empty()){
node* top=Q.front();
Q.pop();
printf("%d",top->data);
if(top->lchild!=NULL)Q.push(top->lchild);
if(top->lchild!=NULL)Q.push(top->lchild);
}
}
//已知先序遍历序列和中序遍历序列如何重建这二叉树
//先序序列区间为[preL,preR],中序序列区间[inL,inR],返回根结点地址;
//其主要过程就是找到根节点,建立根节点,分左右树,然后在左右树再找根节点再创建的一个递归过程;
node* create(int preL,int preR,int int inL,int inR){
if(preL>preR)return NULL;//子树没有结点了,体现在序列里面就是preL>preR;
node* root=new node;//树或者子树的根节点,开辟新空间,返回该空间的地址;
root->data=pre[preL];//先序序列的第一个结点就是根节点;
int k;
for(k=inL;k<=inR;k++){//遍历中序遍历序列,找到根节点的位置;
if(in[k]==root->data){
break;
}
}
//找到了根节点的位置,中序遍历的根节点的两边就是左右子树序列;
int numleft=k-inL;//左子树结点个数;
root->lchild=create(preL+1,preL+numleft,inL,k-1); //遍历左子树;
root->rchild=create(PreL+numleft+1,preR,k+1,inR);
return root;//返回根节点地址;
}
中序遍历序列可以跟先序遍历,后序遍历以及层次遍历序列中任意一个来构建唯一的二叉树,而后者两两搭配或者三个一起上都无法构建唯一的二叉树,原因是这三个遍历的主要作用都是提供根节点,必须由中序遍历来区分左右子树。
完全二叉树:
①完全二叉树的定义:完全二叉树是指完全填充的树,但底层可能除外,底层是从左到右填充的。
②完全二叉树的存储除了用二叉链表存储以外还可以用更方便的数组进行存储,从1开始(根节点下标,1号位存放的必须是根节点),到n,下标为其结点的编号,值为权重。
这样就有:完全二叉树中任意一个结点编号为x,则左节点一定是2x,右节点的编号一定是2x+1
除以以外,该数组中元素的存放顺序恰好是该完全二叉树的层序遍历序列。