009——二叉树(1)

目录

二叉树的五种基本形态:

1.二叉树可以是空树

2.只有一个根节点的树

3.斜树:只有左子树或右子树的树

4.左右孩子都有的树

二叉树的性质:

1.假设根节点是第一层,在二叉树的第i层上最多有2^(n-1)个结点

2.深度为k的二叉树,最多有(2^k)-1个结点

3.任意一个非空二叉树,度为0的结点数比度为2的结点数多一个

4.在完全二叉树中有无度为1的判断

5.具有N个结点的完全二叉树的深度为log2(N+1)(向下取整)或者(log2N)+1(向上取整)

6. 如果有一棵n个结点的完全二叉树,其结点编号按照层次序(从上到下,从左到右),则除根结点外,满足[i/2 , i, 2i, 2i+1]的规则

二叉树的存储方式

1.直接采用数组存储二叉树,将任意一个二叉树补成完全二叉树,不过这样容易造成空间上的浪费

2.二叉链表存储

针对二叉树的操作

1.二叉树的遍历/搜索

广度

深度

采用递归

采用栈


二叉树的五种基本形态:

1.二叉树可以是空树

2.只有一个根节点的树

3.斜树:只有左子树或右子树的树

4.左右孩子都有的树

二叉树的性质:

1.假设根节点是第一层,在二叉树的第i层上最多有2^(n-1)个结点

解释:若想要结点数最多,则要求除叶子结点外,每个结点的度都为2

层数                                        该层结点数

一层:        根节点                        1

二层:        1*2                             2

三层            2*2                             4

四层            4*2                             8

 n层           2*2*...*2                    2^(n-1)

2.深度为k的二叉树,最多有(2^k)-1个结点

将所有层次的结点数相加,再根据等比数列求和公式可得

满二叉树

而有(2^k)-1个结点的树有被称作满二叉树,满二叉树没有度为1的结点

完全二叉树

对满二叉树的结点编号(从上到下再从左到右)从后面删除若干个连续的编号最大的结点,剩下的部分就是完全二叉树

如下图删除15.14.13

同时,对于完全二叉树来说,度为1的结点要么没有,要么只有一个

满二叉树也是完全二叉树

3.任意一个非空二叉树,度为0的结点数比度为2的结点数多一个

解释:

按照度来分,我们可以将其分成度分别为0,1,2的结点:

n = n0 + n1 + n2

按照有无父亲,或者说是是否可以当作孩子结点,我们可以分成根节点和其他结点:

n = 1 + (n-1)

而在这(n-1)个孩子结点里,我们可以再将每个结点看作父亲,

度为0的父亲没有孩子        0 * n0

度为1的父亲一个孩子        1 * n1

度为2的父亲两个孩子        2 * n1

所以可以推出

n = 1 +  0 * n0 +  n1 + 2n1

n0 + n1 + n2  = 1 +   n1 + 2n1

                 n0 = n2 + 1

或者从连线个数的角度理解:

n个结点的树有n-1条连线

度为0的树向下有0条

度为1的树向下有n1条

度为2的树向下有2n2条

n1+n2+n0-1 =n1+2n2 

             n0-1=n2

4.在完全二叉树中有无度为1的判断

没有度为1的结点,n=n0+n2=n2+1+n2=2n2+1

有度为1的结点,n=n0+n1+n2=2n2+2

所以在完全二叉树中,结点数为奇数,没有度为1的结点;结点数为偶数,有度为1的结点且只有1个

5.具有N个结点的完全二叉树的深度为log2(N+1)(向下取整)或者(log2N)+1(向上取整)

6. 如果有一棵n个结点的完全二叉树,其结点编号按照层次序(从上到下,从左到右),则除根结点外,满足[i/2 , i, 2i, 2i+1]的规则

二叉树的存储方式

1.直接采用数组存储二叉树,将任意一个二叉树补成完全二叉树,不过这样容易造成空间上的浪费

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
char data[1005];
int flag;//=0 左  =1右孩子 
int find(char fx)
{
	for (int i = 1; i < 1005; i++)
	{
		if (data[i] == fx)
		{
			return i;
		}
	}
}
int main()
{
	int n;
	char root;
	printf("读入数据个数:\n");
	scanf("%d", &n);
	for (int i = 0; i < 1005; i++)
	{
		data[i] = ' ';
	}
	getchar();
	printf("读入根结点:\n");
	scanf("%c", &root);
	data[1] = root;
	char x, fx;//数据为x,fx为x的父亲  
	int fxi, xi;
	for (int i = 1; i <= n - 1; i++)
	{
		getchar();
		printf("读入剩下的结点(x fx flag):\n");
		scanf("%c %c %d", &x, &fx, &flag);
		fxi = find(fx);//寻找父亲结点的下标 
		if (flag == 0)
		{
			xi = 2 * fxi;
		}
		else {
			xi = 2 * fxi + 1;
		}
		data[xi] = x;
	}
	getchar();

	printf("查找某个结点的孩子父亲结点:\n");
	scanf("%c", &x);
	xi = find(x);
	if (xi == 1)
	{
		printf("根节点,无父亲节点\n");
	}
	else {
		printf("父亲结点:%c\n", data[xi / 2]);
	}
	printf("孩子节点是:%c %c\n", data[2 * xi], data[2 * xi + 1]);

	return 0;
}

2.二叉链表存储

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
//二叉链表节点结构
typedef struct BTNode {
	char data;
	struct BTNode* left;//保存左孩子地址
	struct BTNode* right;//保存右孩子地址
	//struct BTNode* fa;//保存父亲的地址 
}BTNode, * BTree;
BTree initBTree(char root)
{
	BTNode* r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
	{
		printf("空间分配失败\n");
		return NULL;
	}
	r->data = root;
	r->left = r->right = NULL;
	//r->fa=NULL; 
	return r;
}
BTNode* find(BTree r, char fx)
{
	if (r == NULL || r->data == fx)
	{
		return r;
	}

	if (r->left != NULL)
	{
		BTNode* ans = find(r->left, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	if (r->right != NULL)
	{
		BTNode* ans = find(r->right, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	return NULL;
}

BTree insert(BTree r, char x, char fx, int flag)
{
	BTNode* f = find(r, fx);
	if (f == NULL)
	{
		printf("父亲节点不存在,不能插入\n");
	}
	else
	{
		BTNode* s = (BTNode*)malloc(sizeof(BTNode));
		//判断s==NULL
		s->data = x;
		s->left = s->right = NULL;
		//s->fa=f;
		if (flag == 0)
		{
			f->left = s;
		}
		else {
			f->right = s;
		}
	}
	return r;
}
int main()
{
	int n;
	int flag;//=0 左  =1右孩子 
	BTree r = NULL;
	char root;
	printf("输入结点的总个数:\n");
	scanf("%d", &n);
	getchar();
	printf("输入根节点:\n");
	scanf("%c", &root);
	r = initBTree(root);
	char x, fx;
	for (int i = 1; i <= n - 1; i++)
	{
		getchar();
		printf("输入结点信息(x, fx, flag):\n");

		scanf("%c %c %d", &x, &fx, &flag);
		r = insert(r, x, fx, flag);
	}

	getchar();
	printf("输入要查找的结点:\n");
	scanf("%c", &x);

	BTNode* p = find(r, x);
	if (p != NULL && p->left != NULL)
	{
		printf("左孩子%c\n", p->left->data);
	}
	if (p != NULL && p->right != NULL)
	{
		printf("右孩子%c\n", p->right->data);
	}


	return 0;
}

针对二叉树的操作

1.二叉树的遍历/搜索

遍历:沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次

对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继)

二叉树是非线性结构,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题,这里分作按层次(广度)深度两种

广度

按层次,父子关系,知道了父,那么就把其所有的子结点都看一遍

算法思想
引入队列,将根结点入队
从队列中取出队头元素,访问该结点,将该结点的所有孩子节点入队
再次从队列中取出队头元素,并访问,以此重复
直到队列为空,说明所有元素都遍历完成

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
//链式队列
//队列的单链表节点结构 
typedef struct QNode {
	char data;//数据
	struct QNode* next;
}QNode;
QNode* front;
QNode* rear;
void Initqueue()//初始化队列 
{
	QNode* q = (QNode*)malloc(sizeof(QNode));
	if (q == NULL)
	{
		printf("队列分配失败\n");
		return;
	}
	front = rear = q;
	q->next = NULL;
}
void Enqueue(char x)//入队 
{
	QNode* s = (QNode*)malloc(sizeof(QNode));
	//s判空 
	s->data = x;
	s->next = NULL;
	rear->next = s;
	rear = s;
}
int IsEmpty()
{
	if (front->next == NULL)
	{
		return 1;//空 
	}
	return 0;//非空 
}
char Dequeue()
{
	if (IsEmpty() == 0)
	{
		QNode* q = front->next;
		front->next = q->next;
		char x = q->data;
		if (front->next == NULL)
		{
			rear = front;
		}
		free(q);
		q = NULL;
		return x;
	}
	else {
		printf("队空\n");
		char x = ' ';
		return x;
	}
}

//二叉链表节点结构
typedef struct BTNode {
	char data;
	struct BTNode* left;//保存左孩子地址
	struct BTNode* right;//保存右孩子地址 
}BTNode, * BTree;
BTree initBTree(char root)
{
	BTNode* r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
	{
		printf("空间分配失败\n");
		return NULL;
	}
	r->data = root;
	r->left = r->right = NULL;

	return r;
}
BTNode* find(BTree r, char fx)
{
	if (r == NULL || r->data == fx)
	{
		return r;
	}

	if (r->left != NULL)
	{
		BTNode* ans = find(r->left, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	if (r->right != NULL)
	{
		BTNode* ans = find(r->right, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	return NULL;
}

BTree insert(BTree r, char x, char fx, int flag)
{
	BTNode* f = find(r, fx);
	if (f == NULL)
	{
		printf("父亲节点不存在,不能插入\n");
	}
	else
	{
		BTNode* s = (BTNode*)malloc(sizeof(BTNode));
		//判断s==NULL
		s->data = x;
		s->left = s->right = NULL;
		if (flag == 0)
		{
			f->left = s;
		}
		else {
			f->right = s;
		}
	}
	return r;
}
void visit(char x)
{
	printf("%c ", x);
}
void LevelOrder(BTree r)//层次遍历 
{
	Initqueue();//初始化队列 
	if (r == NULL)
	{
		printf("空树,无法遍历\n");
		return;
	}
	Enqueue(r->data);//根节点数据入队
	char x;
	BTNode* q = NULL;
	while (IsEmpty() == 0)
	{
		x = Dequeue();
		visit(x); //访问节点 
		q = find(r, x);//找x所在的节点q 
		if (q->left != NULL)
		{
			Enqueue(q->left->data);
		}
		if (q->right != NULL)
		{
			Enqueue(q->right->data);
		}
	}
	printf("\n");
}



int main()
{
	int n;
	int flag;//=0 左  =1右孩子 
	BTree r = NULL;
	char root;
	scanf("%d", &n);
	getchar();
	scanf("%c", &root);
	r = initBTree(root);
	char x, fx;
	for (int i = 1; i <= n - 1; i++)
	{
		getchar();
		scanf("%c %c %d", &x, &fx, &flag);
		r = insert(r, x, fx, flag);
	}


	LevelOrder(r);

	return 0;
}
/*
9
A
B A 0
E A 1
C B 1
D C 0
F E 1
G F 0
H G 0
K G 1
*/

这里还需要注意队列存储的数据,如果是值,那么队列类型就是值的类型;如果是结点,那么队列类型就是整个结构体类型

深度

按深度,一条道走到黑,然后再返回走另一条道

        深度有三种:先序、中序、后序

先序:根左右

中序:左根右

后序:左右根

采用递归
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
//链式队列
//队列的单链表节点结构 
typedef struct QNode {
	char data;//数据
	struct QNode* next;
}QNode;
QNode* front;
QNode* rear;

int IsEmpty()
{
	if (front->next == NULL)
	{
		return 1;//空 
	}
	return 0;//非空 
}

//二叉链表节点结构
typedef struct BTNode {
	char data;
	struct BTNode* left;//保存左孩子地址
	struct BTNode* right;//保存右孩子地址 
}BTNode, * BTree;
BTree initBTree(char root)
{
	BTNode* r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
	{
		printf("空间分配失败\n");
		return NULL;
	}
	r->data = root;
	r->left = r->right = NULL;

	return r;
}
BTNode* find(BTree r, char fx)
{
	if (r == NULL || r->data == fx)
	{
		return r;
	}

	if (r->left != NULL)
	{
		BTNode* ans = find(r->left, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	if (r->right != NULL)
	{
		BTNode* ans = find(r->right, fx);
		if (ans != NULL && ans->data == fx)
		{
			return ans;
		}
	}
	return NULL;
}

BTree insert(BTree r, char x, char fx, int flag)
{
	BTNode* f = find(r, fx);
	if (f == NULL)
	{
		printf("父亲节点不存在,不能插入\n");
	}
	else
	{
		BTNode* s = (BTNode*)malloc(sizeof(BTNode));
		//判断s==NULL
		s->data = x;
		s->left = s->right = NULL;
		if (flag == 0)
		{
			f->left = s;
		}
		else {
			f->right = s;
		}
	}
	return r;
}
void visit(char x)
{
	printf("%c ", x);
}

//对以r为根的树进行先序遍历 
void PerOrder(BTree r)//先序遍历
{
	if (r == NULL)//空树不需要遍历 
	{
		return;
	}

	visit(r->data);//访问根节点

	PerOrder(r->left);//对左子树进行先序遍历

	PerOrder(r->right);//对右子树进行先序遍历
}
//对以r为根的树进行中序遍历 
void InOrder(BTree r)//中序遍历
{
	if (r == NULL)//空树不需要遍历 
	{
		return;
	}
	InOrder(r->left);//对左子树进行中序遍历

	visit(r->data);//访问根节点

	InOrder(r->right);//对右子树进行中序遍历
}
//对以r为根的树进行后序遍历 
void PostOrder(BTree r)//后序遍历
{
	if (r == NULL)//空树不需要遍历 
	{
		return;
	}
	PostOrder(r->left);//对左子树进行后序遍历
	PostOrder(r->right);//对右子树进行后序遍历
	visit(r->data);//访问根节点
}
int main()
{
	int n;
	int flag;//=0 左  =1右孩子 
	BTree r = NULL;
	char root;
	scanf("%d", &n);
	getchar();
	scanf("%c", &root);
	r = initBTree(root);
	char x, fx;
	for (int i = 1; i <= n - 1; i++)
	{
		getchar();
		scanf("%c %c %d", &x, &fx, &flag);
		r = insert(r, x, fx, flag);
	}

	
	//先序遍历
	PerOrder(r); 
	//中序遍历s
	InOrder(r); 
	//后序遍历
	PostOrder(r);
	return 0;
}
/*
9
A
B A 0
E A 1
C B 1
D C 0
F E 1
G F 0
H G 0
K G 1
*/
采用栈
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
# include <string.h>
// 树的结点结构 
typedef struct BTNode {
	char data;
	struct BTNode* left;
	struct BTNode* right;
}BTNode, * BTree;
int flag;//=0 左孩子, =1 右孩子 
//---------------------链栈---------------------- 
//单链表的节点结构:将整个节点入队
typedef struct stackNode {
	struct BTNode* data;
	struct stackNode* next;
}sstack;

//初始化链栈
sstack* initstack()
{
	sstack* s = (sstack*)malloc(sizeof(sstack));
	if (s == NULL)
	{
		printf("栈空间分配失败\n");
		return NULL;
	}
	s->next = NULL;
	return s;
}
//入栈
void ppush(sstack* s, BTNode* k)
{//头插
	sstack* p = (sstack*)malloc(sizeof(sstack));
	p->data = k;
	p->next = s->next;
	s->next = p;

}
//判空
int empty(sstack* s)
{
	if (s->next == NULL)
	{
		return 1;//栈空 
	}
	return 0;//栈非空 
}
//出栈:返回栈顶元素,将其在栈中删除
BTNode* ppop(sstack* s)
{
	if (empty(s) == 1)
	{
		printf("栈空\n");
		return NULL;
	}
	sstack* p = s->next;
	BTNode* k = p->data;
	s->next = p->next;
	free(p);
	p = NULL;
	return k;
}
//得到栈顶元素,但不出栈
BTNode* gett(sstack* s)
{
	if (empty(s) == 1)
	{
		printf("栈空\n");
		return NULL;
	}
	BTNode* k = s->next->data;
	return k;
}
//-----------------------------------------------------------------
//建树 

BTree initTree(char x)
{
	BTNode* r = (BTNode*)malloc(sizeof(BTNode));
	if (r == NULL)
	{
		printf("分配失败\n");
		return NULL;
	}
	else {
		r->data = x;
		r->left = r->right = NULL;
		return r;
	}

}
BTNode* find(BTree ro, char fx)
{//递归
	if (ro == NULL || ro->data == fx)
	{
		return ro;
	}
	if (ro->left != NULL)
	{
		BTNode* f = find(ro->left, fx);
		if (f != NULL && f->data == fx)
		{
			return f;
		}
	}
	if (ro->right != NULL)
	{
		BTNode* f = find(ro->right, fx);
		if (f != NULL && f->data == fx)
		{
			return f;
		}
	}
	return NULL;

}
void insert(BTree ro, char x, char fx, int flag)
{
	BTNode* f = find(ro, fx);
	if (f != NULL)
	{
		BTNode* s = (BTNode*)malloc(sizeof(BTNode));
		s->data = x;
		s->left = s->right = NULL;
		if (flag == 0)
		{
			f->left = s;
		}
		else {
			f->right = s;
		}
	}
}
//--------------遍历------------------
void visit(BTNode* p)
{
	printf("%c ", p->data);
}
//先序
void PreOrder(BTree ro)
{
	sstack* s = initstack();
	if (ro == NULL)
	{
		printf("空树,不需要遍历\n");
		return;
	}
	BTNode* p = ro;//p辅助遍历
	BTNode* k = NULL;
	while (p != NULL || empty(s) == 0)
	{
		if (p != NULL)
		{
			visit(p);
			ppush(s, p);
			p = p->left;
		}
		else
		{
			k = ppop(s);
			p = k->right;
		}
	}
}
//中序
void InOrder(BTree ro)
{
	sstack* s = initstack();
	if (ro == NULL)
	{
		printf("空树,不需要遍历\n");
		return;
	}
	BTNode* p = ro;//p辅助遍历
	BTNode* k = NULL;
	while (p != NULL || empty(s) == 0)
	{
		if (p != NULL)
		{
			ppush(s, p);
			p = p->left;
		}
		else
		{
			k = ppop(s);
			visit(k);
			p = k->right;
		}
	}
}
//后序
void PostOrder(BTree ro)
{
	sstack* s = initstack();
	if (ro == NULL)
	{
		printf("空树,不需要遍历\n");
		return;
	}
	BTNode* p = ro;//p辅助遍历
	BTNode* pre = NULL;//pre在遍历过程中保存刚刚访问过的节点 
	BTNode* k = NULL;
	while (p != NULL || empty(s) == 0)
	{
		if (p != NULL)
		{
			ppush(s, p);
			p = p->left;
		}
		else
		{
			k = gett(s);
			if (k->right != NULL && pre != k->right)//右孩子存在且没访问 
			{
				p = k->right;//访问右孩子 
			}
			else
			{
				k = ppop(s);
				visit(k);
				pre = k;
			}
		}
	}
}
int main()
{
	int n;
	char x, fx;
	BTree ro = NULL;
	scanf("%d", &n);
	getchar();
	scanf("%c", &x);
	ro = initTree(x);
	for (int i = 2; i <= n; i++)
	{
		getchar();
		scanf("%c %c %d", &x, &fx, &flag);
		insert(ro, x, fx, flag);
	}

	PostOrder(ro);




	return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值