数据结构之二叉树

二叉树

1.完全二叉树

  1. 定义:如果一个二叉树扣除最下面一层节点后成为一个满二叉树,并且被扣除的节点都向左靠齐,那么这个二叉树是完全二叉树

  2. 在这里插入图片描述

  3. 性质

    1. 叶子节点一定只出现在最下面一层
    2. 最下面一层叶子节点一定集中在左边连续位置
    3. 倒数第二层,如果有叶子节点,一定出现在右边连续位置
    4. 节点数一致,完全二叉树深度最小
    5. 对于一个具有n个节点的完全二叉树,如果按照从上到下,同一层的节点按从左到右的顺序对二叉树中所有节点从1开始编号,对于序号为i的节点,有:
      1. 如果i>1,则序号为i的节点的双亲节点的序号为不大于(i/2)的最大整数
      2. 如果2i>n,则第i个节点无左子女(终端节点),否则,其左子女节点为2i
      3. 如果2i+1>n,则i节点无右子女,否则其右子女节点为2i+1
  4. 完全二叉树的顺序存储

    1. 根据性质5,可以把一个二叉树按性质5排序后存放在一个一维数组中,这样无需做其他任何操作,即可完成二叉树的存储,查找
/*************************************************************** 
***  完全二叉树的顺序存储
****************************************************************/
#define MAXSIZE 20
typedef char datatype;     //树的节点类型
//定义一维数组用来存储树

datatype tree[MAXSIZE];

int n;   //树中实际包含的节点个数
  1. 一般二叉树的顺序存储
    一般二叉树由于不具备性质5,下标无法体现节点之间的关系,所以根据二叉树的性质,每个节点最多只有两个子节点,所以在存储二叉树节点时,可以添加两个域,分别用来存储两个子节点的下标
/***************************************************************
***  普通二叉树的顺序存储
****************************************************************/

typedef struct
{
	datatype info[MAXSIZE];
	int lchild, rchild;
}node;
int n;  /*树中实际节点个数*/
int root;  /*用来存放根节点下标*/

有时,不但需要从父母访问子女,还需要从子女节点访问到父母,只需要在每个节点上加上父母的下标即可

/***************************************************************
***  普通二叉树的顺序存储
****************************************************************/

typedef struct
{
	datatype info[MAXSIZE];
	int lchild, rchild;
	int parent;   /*用来存放父母节点下标*/
}node;
int n;  /*树中实际节点个数*/
int root;  /*用来存放根节点下标*/

链式存储

二叉树的顺序存储虽然简单,但必须预先给出数组大小,容易发生数组越界,所以链式存储更加适合用来存储二叉树
链式存储每个节点同样包含三个域,分别是左右孩子域以及值域,不同于顺序存储的是,左右孩子域存储子节点的方式不再是存储下标,而是通过指针实现

/***************************************************************
***  普通二叉树的链式存储
****************************************************************/

typedef struct treeNode
{
	datatype info;
	treeNode * lchild, * rchild;
	treeNode * parent;
}bintNode;
bintNode *root1;   /*指向根节点的指针*/

二叉树的遍历

1.前序遍历

遍历顺序为 根->左->右

  • 前序遍历的递归实现
/*前序遍历*/
void preorder(bintNode * t)
{
	if (t)
	{
		printf("%c", t->info);
		preorder(t->lchild);
		preorder(t->rchild);
	}
}

```c
* 前序遍历非递归实现
递归算法效率低,所以一般使用非递归算法,使用非递归遍历时,必须用一个栈来记录回溯点,以便将来回溯

```c
/*定义栈,用来实现非递归回溯*/
typedef struct
{
	bintNode * data[100];
	int top;   /*栈顶指针*/
	int tag[100];
}seqstack;
void push(seqstack * p,bintNode * t)   /*入栈*/
{
	p->data[p->top] = t;
	p->top++;
}
bintNode * pop(seqstack * p)
{
	if (p->top != 0)   /*如果栈不为空*/
	{
		p->top--;
		return p->data[p->top];   /*返回栈顶元素*/
	}
	else
	{
		return NULL;
	}

根据二叉树前序遍历的定义,程序先访问根节点,在访问左子树,最后访问右子树,比如图一,前序遍历的结果应该是1,2,4,8,9,5,10,3,6,7
在访问完根节点1后,进入由2,4,5,8,9,10组成的左子树,在这一棵子树上,依旧遵循前序遍历的规则,先访问根节点2,然后访问2的左子树,同样,4是2的左子树的根节点,访问玩根4后访问左子树8,8没有后继节点,返回访问右子树9,在这之后需要返回2去访问2的右子树,为了能够返回去,需要设立回溯点,在访问完1后,把1放在栈中,再去访问2,2访问完后把2再放在栈中,4也一样,这样在访问完8后发现没有后继节点,就从栈中取出栈顶元素,由于栈的先入后出性,取出的栈顶元素就是最后放进去的元素4,取出4后访问他的右节点,访问完后再从栈中取出栈顶元素,也就是2,依次便可访问完整个二叉树

void preorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			printf("%c", s->info);
			push(&seq, s);
			s = s->lchild;
		}
		else
		{
			s=pop(&seq);
			s = s->rchild;
		}
	}
}

2.中序遍历

中序遍历与前序遍历几乎一致,仅仅改变访问先后次序

  • 递归实现
/*中序遍历*/
void inorder(bintNode * t)
{
	if (t)
	{
		inorder(t->lchild);
		printf("%c", t->info);
		inorder(t->rchild);
	}
}

  • 非递归实现
void  inorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			push(&seq, s);
			s = s->lchild;	
		}
		else
		{
			s = pop(&seq);
			printf("%c", s->info);
			s = s->rchild;
		}
	}
}

后序遍历

  • 递归实现
/*后序遍历*/
void postorder(bintNode * t)
{
	if (t)
	{
		postorder(t->lchild);
		postorder(t->rchild);
		printf("%c", t->info);
	}
}

  • 非递归实现
    后序遍历的非递归实现和前序中序有一些不同,应为根据后序遍历的定义,需要先访问左子树,在访问右子树,最后访问根节点,在循环访问左子树的过程中,我们把左子树对应的上一级根节点存放在栈中,当左子树访问完需要访问右子树时,需要回溯上去,但这时根节点还没有被访问,如果让其出栈,根节点就会丢失,这时我们需要在定义栈时怎加一个数组tag用来标记栈中元素的状态,每个元素刚进栈时,tag为0,当他第一次位于栈顶即将被处理时(应该访问他的右子树时),tag=0,将他的右子树作为处理对象,此时,改栈顶元素依然保留在栈中,把tag=1;表示右子树访问完成,然后将其出栈,访问他自己,依次循环完成遍历
  • 以图一为例:
    按后续遍历规则,最终遍历结果应该是:8,9,4,10,5,2,6,7,3,1
    程序运行的过程是:从根节点1开始访问,依次按后续遍历规则访问2,4,8,并把他们的tag=0,8没有后继节点,也就是左右子树访问完成,把tag=1,出栈访问自己,此时栈顶元素为4,tag=0标志这即将访问4的右子树,把二叉树指针指向栈顶元素,访问其右子树,完成之后把tag=1,访问栈顶元素,依次循环完成遍历
/*后序遍历*/
void postorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			seq.data[seq.top] = s;   /*应为需要定义tag,不能使用push*/
			seq.tag[seq.top] = 0;
			seq.top++;
			s = s->lchild;
		}
		else
		{
			if (seq.tag[seq.top - 1] == 1)
			{
				seq.top--;
				s = seq.data[seq.top];
				printf("%c", s->info);
				s = NULL;
			}
			else
			{
				s = seq.data[seq.top - 1];
				seq.tag[seq.top-1] = 1;
				s = s->rchild;
			}
		}
	}
}

二叉树的创建

/***************************************************************
***  二叉树的创建
****************************************************************/

/*根据前序遍历创建*/
bintNode *createbintree()
{
	char ch;
	bintNode *t;
	if ((ch = getchar()) == '#')
		t = NULL;
	else
	{
		t = (bintNode *)malloc(sizeof(bintNode));
		t->info = ch;
		t->lchild = createbintree();
		t->rchild = createbintree();
	}
	return t;
}

/*根据中序遍历创建*/
bintNode *createbintree()
{
	char ch;
	bintNode *t;
	if ((ch = getchar()) == '#')
		t = NULL;
	else
	{
		t = (bintNode *)malloc(sizeof(bintNode));
		t->lchild = createbintree();
		t->info = ch;
		t->rchild = createbintree();
	}
	return t;
}

完整代码

/***************************************************************
***  二叉树
***  1.二叉树的创建
***  2.二叉树的遍历
***  3.统计二叉树的节点数
***  4.求二叉树的高度
***  5.判断二叉树是否等价
****************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXSIZE 20
typedef char datatype;     //树的节点类型

/*************************************************************** 
***  完全二叉树的顺序存储
****************************************************************/

datatype tree[MAXSIZE];   //定义一维数组用来存储树
int n_all;   //树中实际包含的节点个数

/***************************************************************
***  普通二叉树的顺序存储
****************************************************************/

typedef struct
{
	datatype info[MAXSIZE];
	int lchild, rchild;
	int parent;   /*用来存放父母节点下标*/
}node;
int n;  /*树中实际节点个数*/
int root;  /*用来存放根节点下标*/

/***************************************************************
***  普通二叉树的链式存储
****************************************************************/

typedef struct treeNode
{
	datatype info;
	struct treeNode * lchild, * rchild;
	struct treeNode * parent;
}bintNode;
bintNode * roots;   /*指向根节点的指针*/

/*定义栈,用来实现非递归回溯*/
typedef struct
{
	bintNode * data[100];
	int top;   /*栈顶指针*/
	int tag[100];
}seqstack;
void push(seqstack * p,bintNode * t)   /*入栈*/
{
	p->data[p->top] = t;
	p->top++;
}
bintNode * pop(seqstack * p)
{
	if (p->top != 0)   /*如果栈不为空*/
	{
		p->top--;
		return p->data[p->top];   /*返回栈顶元素*/
	}
	else
	{
		return NULL;
	}
}
/***************************************************************
***  二叉树遍历的递归实现
****************************************************************/

/*前序遍历*/
void preorder(bintNode * t)
{
	if (t)
	{
		printf("%c", t->info);
		preorder(t->lchild);
		preorder(t->rchild);
	}
}

/*中序遍历*/
void inorder(bintNode * t)
{
	if (t)
	{
		inorder(t->lchild);
		printf("%c", t->info);
		inorder(t->rchild);
	}
}

/*后序遍历*/
void postorder(bintNode * t)
{
	if (t)
	{
		postorder(t->lchild);
		postorder(t->rchild);
		printf("%c", t->info);
	}
}

/***************************************************************
***  二叉树遍历的非递归实现
****************************************************************/

/*前序遍历*/
void preorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			printf("%c", s->info);
			push(&seq, s);
			s = s->lchild;
		}
		else
		{
			s=pop(&seq);
			s = s->rchild;
		}
	}
}

/*中序遍历*/
void  inorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			push(&seq, s);
			s = s->lchild;	
		}
		else
		{
			s = pop(&seq);
			printf("%c", s->info);
			s = s->rchild;
		}
	}
}

/*后序遍历*/
void postorder1(bintNode * s)
{
	seqstack  seq;
	seq.top = 0;  /*初始化栈*/
	while (seq.top != 0 || (s))
	{
		if (s)
		{
			seq.data[seq.top] = s;   /*应为需要定义tag,不能使用push*/
			seq.tag[seq.top] = 0;
			seq.top++;
			s = s->lchild;
		}
		else
		{
			if (seq.tag[seq.top - 1] == 1)
			{
				seq.top--;
				s = seq.data[seq.top];
				printf("%c", s->info);
				s = NULL;
			}
			else
			{
				s = seq.data[seq.top - 1];
				seq.tag[seq.top-1] = 1;
				s = s->rchild;
			}
		}
	}
}

/***************************************************************
***  二叉树的创建
****************************************************************/

/*根据前序遍历创建*/
bintNode *createbintree()
{
	char ch;
	bintNode *t;
	if ((ch = getchar()) == '#')
		t = NULL;
	else
	{
		t = (bintNode *)malloc(sizeof(bintNode));
		t->info = ch;
		t->lchild = createbintree();
		t->rchild = createbintree();
	}
	return t;
}

/*根据中序遍历创建*/
bintNode *createbintree1()
{
	char ch;
	bintNode *t;
	if ((ch = getchar()) == '#')
		t = NULL;
	else
	{
		t = (bintNode *)malloc(sizeof(bintNode));
		t->lchild = createbintree1();
		t->info = ch;
		t->rchild = createbintree1();
	}
	return t;
}

int main()
{
	bintNode * s;
	s = createbintree();   /*通过前序遍历创建二叉树*/
	printf("\n递归前序遍历\n");
	preorder(s);
	printf("\n非递归前序遍历\n");
	preorder1(s);
	printf("\n递归中序遍历\n");
	inorder(s);
	printf("\n非递归中序遍历\n");
	inorder1(s);
	printf("\n递归后序遍历\n");
	postorder(s);
	printf("\n非递归后序遍历\n");
	postorder1(s);
	system("pause");
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Junebao

如果你碰巧财力雄厚的话...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值