数据结构之二叉树链表



2.二叉树
     二叉树是n个结点的有限集合,该集合或者为空集(称为空二叉树),或者有一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成
      特点:
          ①每个结点 最多有两棵子树,所以二叉树中不存在度 大于2的结点
          ②左子树和右子树是有顺序的,次序不能任意颠倒
          ③即使树中某结点只有一棵子树,也要区分它是左子树还是右子树
      5种基本形态:
          ①空二叉树
          ②只有一个根节点
          ③根结点只有左子树
          ④根结点只有右子树
          ⑤根结点既有左子树又有右子树

      特殊二叉树
          ①斜树:所有的结点都只有左子树的二叉树叫左斜树。所有及诶单都是只有右子树的二叉树叫右斜树。两者统称为斜树。线性表就可以理解为是树的一种极其特殊的表现形式
          ② 满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树
                    特点:叶子只能出现在最下面一层;非叶子结点的度一定是2;在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多
          ③ 完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i 个结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这课二叉树称为完全二叉树9个人理解就是满二叉树的(完全二叉树的最后一排不完全,缺少右面的结点0)
                    
              完全二叉树的特点:
                         ①叶子结点只能出现在最下两层
                         ②最下层的叶子一定集中在左部全部连续位置
                         ③倒数第二,若有叶子结点,一定都在右部连续位置
                         ④如果结点度为1,则该及诶单只有左孩子,即不存在只有右子树的情况
                         ⑤同样结点树的二叉树,完全二叉树的深度最小


3.二叉树的性质
     ①在二叉树的第i层上至多有2^(i-1)个结点
     ②深度为k的二叉树至多有2^k - 1 个结点
     ③对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0 = n2 + 1
     ④具有n个结点的完全二叉树的深度为【log 2n】+ 1(【x】表示不大于x的最大整数)
     ⑤如果对 一棵有n个结点的完全二叉树(其深度为【log 2n】+1 也就是结点为n )的结点按层序编号,对任一结点i (1<=i <=n)有
          如果 i= 1,则结点i是二叉树的根,无双亲;如果 i > 1,则其双亲是结点【i/2】
          如果 2i > n ,则结点i无左孩子;否则其左孩子是结点 2i
          如果 2i + 1 > n,则结点i无右孩子;否则其右孩子是结点 2i + 1

4.二叉树的存储结构
     顺序存储结构一般只用于完全二叉树,因为如果是一颗深度为k的右斜树,则要分配2^k -1 个存储单元空间
      二叉链表
          二叉树每个结点最多有两个孩子,所以为它涉及一个数据域和两个指针域
lchild data rchild


5.二叉树的遍历
     二叉树的遍历是指从根结点出发,按照某种 次序依次 访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次

     二叉树遍历方法

          ①前序遍历:若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,然后前序遍历右子树。ABDGHCEIF —— 根结点 - 左子树 - 右子树
          ②中序遍历:若树为空,则空操作返回,否则从根结点开始(并不是先访问根结点),中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树GDHBAEICF —— 左子树 - 根结点 - 右子树
          ③后序遍历: 从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点,GHDBIEFCA ——  左右子叶-根结点
          ④层序遍历:从树的第一层,也就是从根结点开始访问,从上而下逐层比那里,在同一层,按从左到右的顺序对节点逐个访问ABCDEFGHI
          把树中的结点变成某种意义的线性序列

          二叉树的定义是用递归的方式,所以实现遍历算法也可以采用递归
     ①前序遍历算法
          void preOderTraverse(bigtree T){
                if(T == NULL)                
                     return;
               printf();
               preOderTrverse(T->lchild);
                preOderTrverse(T->rchild);
          }
     ② 中序遍历算法
     void preOderTraverse(bigtree T){
                if(T == NULL)                
                     return;
                preOderTrverse(T->lchild);
               printf();显示结点数据,可以更改为其他对结点操作
                preOderTrverse(T->rchild);
          }
     ③后序遍历算法         
          void preOderTraverse(bigtree T){
                if(T == NULL)                
                     return;
               preOderTrverse(T->lchild);
                preOderTrverse(T->rchild);
               printf();0
          }
     已知前序比那里序列和中序遍历序列,可以唯一确定一棵二叉树;已知后序遍历序列和中序遍历序列,可以唯一确定一棵二叉树;已知前序和后序遍历,是不能确定一棵二叉树的


关于二叉树顺序存储结构,其实就是用数组来存储数据,根据满二叉树的特点用下标来处理其关系


处理二叉树链表就是使用递归函数进行处理
#include<stdio.h>
#include<malloc.h>

#define ERROR 0
#define OK 1
#define FALSE 0
#define TRUE 1

typedef int type;//结构体中数据域中的类型
typedef int Status;

typedef struct treeNode//结构体
{
	type data ;	//数据
	treeNode *lchild;//左指针
	treeNode *rchild;//右指针
}treeNode,*tree;

Status initTree(treeNode *node){//初始化树
	node = NULL;
	return OK;
}


/*
错误代码:一开始我用一重指针来创建树,可是发现创建不了,会出错,后来发现原因——创建二叉树的时候采用递归的方式,如果每次传的是一重指针,当函数回调的时候,参数指针是局部变量,其生命周期结束,
所以指向的那块内存将不能与上一级结点联系起来
为什么动态链表的时候插入时,传入一级指针可以??
——因为传入的是指针,实际传入的是指针指向那块内存的地址,实际就操作的是指针指向的那块地址。而二叉树的最后指针并没有指向一块内存空间,所以不能够使用一重指针来创建

void create(treeNode *tr){	
	type data ;
	printf("请输入结点的值,类型为数字\n");
	scanf("%d",&data);
	if(data == 0 ){
		tr = NULL;
	}
	else{
		tr = (treeNode*)malloc(sizeof(treeNode));
		if(tr){
			tr->data = data;
			create(tr->lchild);
			create(tr->rchild);
		}
	}
}

  */

  
  
  void create(tree *tr){
	type data ;
	printf("请输入结点的值,类型为数字\n");
	scanf("%d",&data);
	if(data == 0 ){
		*tr = NULL;
	}
	else{
		*tr = (treeNode*)malloc(sizeof(treeNode));
		if(*tr){
			(*tr)->data = data;
			create(&(*tr)->lchild);
			create(&(*tr)->rchild);
		}
	}
}


  
  
  
  /*
当销毁树时,与上面的道理相似,也需要二重指针,如果是一重指针最后销毁的是局部参数指针,因为最后一级指针没有指向一块内存空间,所以不能被销毁
  */
Status destroy(tree *tre){
	if(*tre ){//如果不为空
		if((*tre)->lchild){
			destroy(&(*tre)->lchild);//销毁左子树
		}
		if((*tre) ->rchild){
			destroy(&(*tre)->rchild);//销毁右子树
		}
		free(*tre);
		*tre = NULL;
	}
	return OK;
}


int treeDepth(treeNode* tree){ //计算树的深度
	int i = 0;
	int j = 0;
	if(tree){
		if(tree->lchild){
			i = treeDepth(tree->lchild);
		}
		if(tree->rchild){
			j = treeDepth(tree->rchild);
		}
		return i>j ? i+1:j+1;
	}
	else
		return 0;
}


void preOrderTraverse(treeNode* tree){//前序遍历二叉树
	if(tree){
		printf("%d\t",tree->data);
		preOrderTraverse(tree->lchild);
		preOrderTraverse(tree->rchild);
	}
}



void main(){
	tree tre;
	create(&tre);
	
	printf("该树的深度为:%d\n",treeDepth(tre));
	preOrderTraverse(tre);

	destroy(&tre);
	printf("该树的深度为:%d\n",treeDepth(tre));
	preOrderTraverse(tre);

  }

	


     


























  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值