二叉树基本操作

目录

一、树概念及结构

1.树的概念

2. 树的基本术语

二:二叉树

1.二叉树

2.二叉树基本操作

1.定义二叉树节点结构

2.创建二叉树

3.前序遍历(递归和非递归)

4.中序遍历(递归和非递归)

5.后序遍历(递归和非递归)

6.层次遍历

7.左右子树交换

8.获得树的深度

三:综合

        源码:


一、树概念及结构


1.树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。

2. 树的基本术语

 

(1)结点:树中的一个独立单元。包含一个数据元素及若干指向其于树的分支,如图中的A、B、C、D等。(下面术语中均以图为例来说明)
(2)结点的度:结点拥有的子树数称为结点的度。例如,A的度为3, C的度为1,F的度0。
(3)树的度:树的度是树内各结点度的最大值。图所示的树的度为3。
(4)叶子:度为0的结点称为叶子或终端结点。结点K、L、F、G、M、I、J都是树的叶子。
(5)非终端结点:度不为0的结点称为非终端结点或分支结点。除根结点之外,非终端结点也为内部结点。
(6)双亲和孩子:结点的子树的根称为该结点的孩子,相应地,该结点称为孩子的双亲。例如,B的双亲为A,B的孩子有E和F。
(7)兄弟:同一个双亲的孩子之间互称兄弟。例如,H、I和J互为兄弟。
(8)祖先:从根到该结点所经分支上的所有结点。例如,M的祖先为A、D和H。
(9)子孙:以某结点为根的子树中的任一结点都称为该结点的子孙。如B的子孙为E,K,L和F
(10)层次:结点的层次从根开始定义起,根为第一层,根的孩子为第二层。树中任一结点的层次
等于其双亲结点的层次加1。

(11)堂兄弟,双亲在同-层的结占石头党只总例加结占G与E、F、H、I、J互为堂兄弟。

(12)树的深度:树中结点的最大层次称为树的深度或高度。图所示的树的深度为4。
(13)有序树和无序树:如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。在有序树中最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子。
(14)森林:是m(m≥0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。由此,也可以用森林和树相互递归的定义来描述树。

二:二叉树

1.二叉树

二叉树(Binary Tree)是n(n≥0)个结点所构成的集合,它或为空树(n = 0);或为非空树

对于非空树T:(1)有且仅有一个称之为根的结点

                        (2)除根结点以外的其余结点分为两个互不相交的子集T1和T2,分别称为T的左子树和右子树,且T1和T2本身又都是二叉树。

完全二叉树:深度为k 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k 的满二叉树中编号从1至n的结点一一对应

满二叉树:一棵深度为k 且有2k−1个结点的二叉树。

2.二叉树基本操作

注:在非递归遍历时是使用数组结构来代替栈操作

1.定义二叉树节点结构

typedef struct BiTNode{ // 定义树节点结构 
	char data;
	struct BiTNode *lchild, *rchild; //左右孩子
}BiNode, *BiTree;

2.创建二叉树

BiTree CreateBiTree(){ // 创建树 
	BiTree T;
	char c;
	scanf("%c", &c);
	if(c == '#'){
		T= NULL;
	}else{
		T = (BiTree)malloc(sizeof(BiTNode));
		T->data = c;
		T->lchild = CreateBiTree();
		T->rchild = CreateBiTree();
	}
	return T;
}

注:建立二叉树时,这里是以前序遍历的方式,输入的是扩展二叉树,也就是要告诉计算机什么是叶结点,否则将一直递归,当输入“#”时,指针指向NULL,说明是叶结点。

例如:希望建立下面的二叉树则要输入 3 9 # # 20 # #      
     3
   /   \
   9   20
  / \  / \
 #  # #  #

建立下面二叉树则要输入 3 9 # # 20 15 # # #

    3
   / \
  9  20
    /  
   15   

3.前序遍历(递归和非递归)

 前序遍历(非递归)
     1.先打印根节点的值,由于栈是先进后出,
     2.所以先将 右子树放进去,再将左子树放进去

PreOrderTraverse(BiTree T){ // 前序遍历(递归) 
	if(T){
		printf("%c  ",T->data);
		PreOrderTraverse(T->lchild);
		PreOrderTraverse(T->rchild);
	}
}

void preOrderNRec(BiTree T){  // 前序遍历(非递归) 
    // 先打印根节点的值,由于栈是先进后出,
	// 所以先将 右子树放进去,再将左子树放进去
	BiTree stack[MAX], node;
	int k = -1; // stack下标 
	if(T == NULL){
		return;
	}else{
		k++;
		stack[k] = T;
		while(k > -1){
			node = stack[k--];
			printf("%c  ",node->data);
			
			if(node->rchild != NULL){ //将左孩子入数组
				stack[++k] = node->rchild;
			}
			if(node->lchild != NULL){ // 将右孩子入数组
				stack[++k] = node->lchild;
			}
		}
		
	}
}

4.中序遍历(递归和非递归)

中序遍历(非递归)
       1. 从根结点开始,遍历左孩子同时压栈,当遍历结束,说明当前遍历的结点没有左孩子,
       2. 从栈中取出来调用操作函数,然后访问该结点的右孩子,继续以上重复性的操作。

InOrderTraverse(BiTree T){  // 中序遍历(递归) 
	if(T){
		InOrderTraverse(T->lchild);
		printf("%c  ",T->data);
		InOrderTraverse(T->rchild);
	}
}

void InOrderNRec(BiTree T){  //  中序遍历(非递归) 
    //   1. 从根结点开始,遍历左孩子同时压栈,当遍历结束,说明当前遍历的结点没有左孩子,
    //   2. 从栈中取出来调用操作函数,然后访问该结点的右孩子,继续以上重复性的操作。  
    BiTree stack[MAX], node;
    int top = 0;
    if(T == NULL){
    	return;
	}
	node = T;
	while(node != NULL || top > 0){ // 节点不为空,栈不为空 
		
		while(node != NULL){ // 左子树入栈 
			stack[++top] = node;
			node = node->lchild;
		}
		
		node = stack[top--];
		printf("%c  ",node->data);
		
		node = node->rchild; // 扫描右节点 
		
	}
}

5.后序遍历(递归和非递归)

 后序遍历(非递归)
     那么可以把后序当作:根->右->左,最后再反转回来即可。
     由于我们是使用的栈,所以先保存根节点, 然后先放进去 左节点,再放 进去 右节点

PostOrderTraverse(BiTree T){  //后序遍历(递归) 
	if(T){
		PostOrderTraverse(T->lchild);
		PostOrderTraverse(T->rchild);
		printf("%c  ",T->data);
	}
}

void PostOrderNRec(BiTree T){  //  后序遍历(非递归)
    //  那么可以把后序当作:根->右->左,最后再反转回来即可。
    //  由于我们是使用的栈,所以先保存根节点, 然后先放进去 左节点,再放 进去 右节点
    BiTree stack[MAX], node;
    int top = 0; // stack 下标 
    int count = 0; // array下标 
    char array[MAX];  // 方便数组反转 
	
	if(T == NULL){
		return;
	}else{
		top++;
		stack[top] = T;  // 根节点入栈 
		while(top > 0){
			node = stack[top--]; // 根节点出栈 
			array[count++] = node->data; // 根节点数据记录到数组中 
			
			if(node->lchild != NULL){ // 左节点入栈 
				stack[++top] = node->lchild;
			}
			if(node->rchild != NULL){ // 右节点入栈 
				stack[++top] = node->rchild;
			}
		}
	}
	
	for(int i = count - 1; i >= 0; i--){ // 反转输出 
		printf("%c  ",array[i]);
	}
}

6.层次遍历

void cengci(BiTree T){ // // 层次遍历(数组) 
	BiTree temp[MAX];
	int in = 0;
	int out = 0;
	
	if(T){
		temp[in] = T; in++;
		while(in > out){
			if (temp[out])
            {
            printf("%c  ",temp[out]->data);
            temp[in++] = temp[out]->lchild; // 左孩子入栈
            temp[in++] = temp[out]->rchild; // 右孩子入栈
            }
        out++;
		}
	}else{
		return;
	}
}

7.左右子树交换

void change(BiTree T){ // 左右子树交换 
    BiTree temp;
	if(T){
		temp = T->lchild;  // 左右子树交换
		T->lchild = T->rchild;
		T->rchild = temp;
		change(T->lchild); // 左孩子的左右子树交换
		change(T->rchild); // 右孩子的左右子树交换
	}else{
		return;
	}
}

8.获得树的深度

int GetHeight(BiTree T){ // 得到树的深度 
    if(T==NULL)return 0;
    int x,y;
    x=GetHeight(T->lchild)+1;
    y=GetHeight(T->rchild)+1;
    return x>y?x:y;
}

三:综合

        源码:

#include<stdio.h>
#include<stdlib.h>
#define MAX 100

typedef struct BiTNode{ // 定义树节点结构 
	char data;
	struct BiTNode *lchild, *rchild;
}BiNode, *BiTree;

int GetHeight(BiTree T){ // 得到树的深度 
    if(T==NULL)return 0;
    int x,y;
    x=GetHeight(T->lchild)+1;
    y=GetHeight(T->rchild)+1;
    return x>y?x:y;
}

BiTree CreateBiTree(){ // 创建树 
	BiTree T;
	char c;
	scanf("%c", &c);
	if(c == '#'){
		T= NULL;
	}else{
		T = (BiTree)malloc(sizeof(BiTNode));
		T->data = c;
		T->lchild = CreateBiTree();
		T->rchild = CreateBiTree();
	}
	return T;
}

PreOrderTraverse(BiTree T){ // 前序遍历(递归) 
	if(T){
		printf("%c  ",T->data);
		PreOrderTraverse(T->lchild);
		PreOrderTraverse(T->rchild);
	}
}

InOrderTraverse(BiTree T){  // 中序遍历(递归) 
	if(T){
		InOrderTraverse(T->lchild);
		printf("%c  ",T->data);
		InOrderTraverse(T->rchild);
	}
}

PostOrderTraverse(BiTree T){  //后序遍历(递归) 
	if(T){
		PostOrderTraverse(T->lchild);
		PostOrderTraverse(T->rchild);
		printf("%c  ",T->data);
	}
}

void preOrderNRec(BiTree T){  // 前序遍历(非递归) 
    // 先打印根节点的值,由于栈是先进后出,
	// 所以先将 右子树放进去,再将左子树放进去
	BiTree stack[MAX], node;
	int k = -1; // stack下标 
	if(T == NULL){
		return;
	}else{
		k++;
		stack[k] = T;
		while(k > -1){
			node = stack[k--];
			printf("%c  ",node->data);
			
			if(node->rchild != NULL){
				stack[++k] = node->rchild;
			}
			if(node->lchild != NULL){
				stack[++k] = node->lchild;
			}
		}
		
	}
}

void InOrderNRec(BiTree T){  //  中序遍历(非递归) 
    //   1. 从根结点开始,遍历左孩子同时压栈,当遍历结束,说明当前遍历的结点没有左孩子,
    //   2. 从栈中取出来调用操作函数,然后访问该结点的右孩子,继续以上重复性的操作。  
    BiTree stack[MAX], node;
    int top = 0;
    if(T == NULL){
    	return;
	}
	node = T;
	while(node != NULL || top > 0){ // 节点不为空,栈不为空 
		
		while(node != NULL){ // 左子树入栈 
			stack[++top] = node;
			node = node->lchild;
		}
		
		node = stack[top--];
		printf("%c  ",node->data);
		
		node = node->rchild; // 扫描右节点 
		
	}
}

void PostOrderNRec(BiTree T){  //  后序遍历(非递归)
    //  那么可以把后序当作:根->右->左,最后再反转回来即可。
    //  由于我们是使用的栈,所以先保存根节点, 然后先放进去 左节点,再放 进去 右节点
    BiTree stack[MAX], node;
    int top = 0; // stack 下标 
    int count = 0; // array下标 
    char array[MAX];  // 方便数组反转 
	
	if(T == NULL){
		return;
	}else{
		top++;
		stack[top] = T;  // 根节点入栈 
		while(top > 0){
			node = stack[top--]; // 根节点出栈 
			array[count++] = node->data; // 根节点数据记录到数组中 
			
			if(node->lchild != NULL){ // 左节点入栈 
				stack[++top] = node->lchild;
			}
			if(node->rchild != NULL){ // 右节点入栈 
				stack[++top] = node->rchild;
			}
		}
	}
	
	for(int i = count - 1; i >= 0; i--){ // 反转输出 
		printf("%c  ",array[i]);
	}
}

void backOrderNRec(BiTree root){  // 后序遍历(非递归) 复杂版 
 
	BiTree p = root;
	BiTree stack[MAX];
	int num = 0;
	BiTree have_visited = NULL;
 
	while (NULL != p || num>0)
	{
		while (NULL != p)
		{
			stack[num++] = p;
			p = p->lchild;
		}
		p = stack[num - 1];
		if (NULL == p->rchild|| have_visited == p->rchild)
		{
			printf("%c  ", p->data);
			num--;
			have_visited = p;
			p = NULL;
		}
		else
		{
			p = p->rchild;
		}
	}
	
}

void cengci(BiTree T){ // // 层次遍历(数组) 
	BiTree temp[MAX];
	int in = 0;
	int out = 0;
	
	if(T){
		temp[in] = T; in++;
		while(in > out){
			if (temp[out])
            {
            printf("%c  ",temp[out]->data);
            temp[in++] = temp[out]->lchild;
            temp[in++] = temp[out]->rchild;
            }
        out++;
		}
	}else{
		return;
	}
}

void change(BiTree T){ // 左右子树交换 
    BiTree temp;
	if(T){
		temp = T->lchild;
		T->lchild = T->rchild;
		T->rchild = temp;
		change(T->lchild);
		change(T->rchild);
	}else{
		return;
	}
}

void test(){  // 测试函数 
	BiTree T;
	T = CreateBiTree();
	printf("先序遍历:\n");
	PreOrderTraverse(T);
	printf("\n");
	
	printf("前序遍历(非递归):\n");
	preOrderNRec(T); 
	printf("\n");
	
	printf("中序遍历:\n");
	InOrderTraverse(T);
	printf("\n");
	
	printf("中序遍历(非递归):\n");
	InOrderNRec(T);
	printf("\n");
	
	printf("后序遍历:\n");
	PostOrderTraverse(T);
	printf("\n");
	
	printf("后序遍历(非递归):\n");
	PostOrderNRec(T);
	printf("\n");
	
	printf("后序遍历(非递归)复杂版:\n");
	backOrderNRec(T);
	printf("\n");
	
	printf("深度 :\n%d\n",GetHeight(T));
	
	printf("层次遍历:\n");
	cengci(T);
	printf("\n");
	
	printf("交换子树后前序遍历:\n");
	change(T);
	PreOrderTraverse(T);
	printf("\n");
	
}

int main(){ // 主函数 
	
	test();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值