数据结构----二叉树的存储方式

0 阅读5分钟

二叉树

二叉树的每个节点都只允许有两个子树,每个节点的度不超过2.

二叉树的概念

满二叉树:所有节点都有左右子树,且所有叶子节点都在同一层.一棵深度为k且含有2^k-1个节点的二叉树称为满二叉树.

完全二叉树:由一棵满二叉树把所有节点从上到下从左往右编号后按编号从大到小删去几个节点得到的二叉树是完全二叉树.一棵k层的完全二叉树的所有叶子节点在k或k-1层.

二叉树的性质

  1. 在二叉树的第i层最多含有2^(i-1)个节点
  2. 深度为k的二叉树最多有2^k - 1个节点
  3. 在二叉树中,叶节点比度为2的节点多一个

<1> 总节点为各种节点之和: n = n0 + n1 + n2

<2> 除了根节点其他节点和自己的父节点都由一根线连接,度为n的节点连有n条线

( n - 1 = 1 * n1 + 2 * n2 )

两式可得 n0 = n2 + 1

image.png

5.在编号的完全二叉树中节点为i的节点,其父节点编号为[i / 2],左孩子编号为2 * i,右孩子编号为2 * i + 1

二叉树的存储方式

二叉树的顺序存储

可以把任意一棵二叉树补齐为一棵完全二叉树,然后通过二叉树的性质5的编号规则把二叉树储存在一个线性表上.

#include<stdio.h>
#include<stdlib.h>
#define maxx 1005
int flag;//=0 左孩子。=1 右孩子
char data[maxx];
int Find(char fx)
{
	int i;
	for(i=1;i<maxx;i++)
	{
		if(data[i]==fx)
		{
			break;
		}
	}
	return i;
}
int main()
{
	int n;
	char r;
	//避免补上的NULL结点的影响 
	for(int i=0;i<maxx;i++)
	data[i]=' ';

	scanf("%d",&n);
	getchar();
	scanf("%c",&r);
	data[1]=r;
	char x,fx;
	for(int i=1;i<=n-1;i++)
	{
		getchar();
		scanf("%c %c %d",&x,&fx,&flag);//x是fx的孩子
		int fxi=Find(fx);
	
		if(flag==0)
		{
			data[fxi*2]=x;

		}
		else
		{
			data[fxi*2+1]=x;
		}

	}
	
	getchar();
	scanf("%c",&x);
	int i=Find(x);
	//父亲
	if(i/2==0)
	{
		printf("该结点是根结点\n");
	}
	else
	{
		printf("该结点的父亲是:%c\n",data[i/2]);
	}
	//找孩子
	printf("该结点的左孩子是%c,右孩子是%c\n",data[i*2],data[i*2+1]);


	return 0;
} 
/*
7
a
b a 0
c a 1
d b 0
e b 1
f e 0
g e 1
*/

用全局变量flag来判断这个节点是父亲节点的左孩子还是右孩子.所有的数据都可以用一张内存连续的线性表来储存.

Find函数只需要输入目标节点的data,然后遍历整张线性表,如果找到就直接返回下标.

main函数中先把线性表中所有数据全初始化为' ',初始化根节点只需要把输入的root数据存入编号为1的位置.插入操作需要输入节点x,其父亲节点fx,还有flag.找到父亲节点的下标fxi,然后通过判断flag,左孩子存在fxi*2处,右孩子存在fxi*2+1处.

找x节点的父亲节点只需要对编号进行操作,如果i/2==0那么x节点是根节点,反之其父亲节点是data[i/2].

二叉树的链式存储

二叉树的链式存储直接用二叉链表存储即可.每个节点保持该节点数据,左孩子地址,右孩子地址,以及父节点地址.

#include<stdio.h>
#include<stdlib.h>
//二叉链表的结点结构 
typedef struct Node
{
	char data;
	struct Node* l;//指向左孩子
	struct Node* r;//指向右孩子
	struct Node* fa;//指向右孩子
}BTNode, * BTree;

BTree InitBTree(char r)
{
	BTNode* s = (BTNode*)malloc(sizeof(BTNode));
	//if(s==NULL)
	s->data = r;
	s->l = s->r = NULL;
	s->fa = NULL;
	return s;

}
BTNode* Find(BTree root, char fx)
{
	if (root->data == fx)
	{
		return root;
	}

	BTNode* ans = NULL;
	if (root->l != NULL)
	{

		ans = Find(root->l, fx);
		if (ans != NULL)
		{
			return ans;
		}
	}
	if (root->r != NULL)
	{

		ans = Find(root->r, fx);
		if (ans != NULL)
		{
			return ans;
		}
	}
	return NULL;

}
BTree Insert(BTree root, char x, char fx, int flag)
{
	BTNode* f = Find(root, fx);
	BTNode* s = (BTNode*)malloc(sizeof(BTNode));
	//if(s==NULL)
	s->data = x;
	s->l = s->r = NULL;
	s->fa = f;

	if (flag == 0)f->l = s;
	else f->r = s;
	return root;
}
int main()
{
	int n;
	char r;
	scanf("%d", &n);
	getchar();
	scanf("%c", &r);
	BTree root = InitBTree(r);
	char x, fx;
	int flag;//=0 左孩子。=1 右孩子
	for (int i = 1; i <= n - 1; i++)
	{
		getchar();
		scanf("%c %c %d", &x, &fx, &flag);//x是fx的孩子
		root = Insert(root, x, fx, flag);
	}
	getchar();
	scanf("%c", &x);
	BTNode* p = Find(root, x);
	if (p->l != NULL)printf("左孩子是%c\n", p->l->data);
	if (p->r != NULL)printf("右孩子是%c\n", p->r->data);

	if (p->fa == NULL)printf("该结点是根结点\n");
	else printf("双亲结点是%c\n", p->fa->data);



	return 0;
}

用结构体封装节点,用typedef取两个别名区分根节点和普通节点.

初始化二叉树:读入根节点数据,malloc一个根节点,把根节点的数据更新,且把孩子和父亲节点的地址全部置空,最后返回根节点即可.

查找函数:二叉链表中的查找可以利用递归实现,如果根节点的数据和目标节点相同就直接返回根节点,判断左右子树是否为空,不为空则分别遍历左右子树.用节点ans来记录目标节点,如果找到了目标节点直接返回ans即可.如果递归结束还没找到目标节点那么返回NULL.

插入函数:用f节点记录父亲节点,malloc一个新节点s,把s的数据更新为x,再把孩子地址置空,父亲指针指向节点f.判断flag来确定父亲节点f的哪个指针指向s.

二叉树是一种特别的树,每个节点最多只有两个孩子,分别叫左孩子和右孩子,子树有严格的左右顺序,不能随便互换.和普通树相比,它结构更简单、好实现.