数据结构:二叉树链式结构的实现

文章目录

  • 前言
  • 一、普通二叉树的结构
    • [1.1 二叉树的遍历](#1.1 二叉树的遍历)
      • [1.1.1 前序遍历](#1.1.1 前序遍历)
      • [1.1.2 中序遍历](#1.1.2 中序遍历)
      • [1.1.3 后序遍历](#1.1.3 后序遍历)
      • [1.1.4 层序遍历](#1.1.4 层序遍历)
  • 二、链式二叉树的实现
    • [2.1 二叉树的结构定义](#2.1 二叉树的结构定义)
    • [2.2 构造一棵二叉树](#2.2 构造一棵二叉树)
    • [2.3 前序遍历](#2.3 前序遍历)
    • [2.4 中序遍历](#2.4 中序遍历)
    • [2.5 后序遍历](#2.5 后序遍历)
    • [2.6 层序遍历](#2.6 层序遍历)
    • [2.7 二叉树结点个数](#2.7 二叉树结点个数)
    • [2.8 二叉树叶子结点个数](#2.8 二叉树叶子结点个数)
    • [2.9 二叉树的高度](#2.9 二叉树的高度)
    • [2.10 二叉树第k层结点个数](#2.10 二叉树第k层结点个数)
    • [2.11 二叉树查找值为x的节点](#2.11 二叉树查找值为x的节点)
    • [2.12 二叉树的销毁](#2.12 二叉树的销毁)

前言

完全二叉树是利用顺序结构(堆)来实现的,因为数组不适合实现非完全二叉树,数组实现非完全二叉树会浪费大量的空间。所以非完全二叉树是由链表来实现的

二叉树是空树或者是根节点+根节点的左子树+根节点的右子树

学习链式二叉树真正的意义是学会如何去控制、遍历二叉树的结构,为我们后期学习搜索二叉树做好铺垫。


一、普通二叉树的结构

普通二叉树的结构有:遍历、求高度、求节点个数、销毁等

1.1 二叉树的遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历

1.1.1 前序遍历

前序遍历(Preorder Traversal 亦称先序遍历)------访问根结点的操作发生在遍历其左右子树之前。即先访问根节点,然后访问左子树,最后访问右子树。

1.1.2 中序遍历

中序遍历(Inorder Traversal)------访问根结点的操作发生在遍历其左右子树之中(间)。即先访问左子树,在访问根节点,最后访问右子树

1.1.3 后序遍历

后序遍历(Postorder Traversal)------访问根结点的操作发生在遍历其左右子树之后。即先访问左子树,再访问右子树,最后访问根节点

1.1.4 层序遍历

非递归结构遍历

二、链式二叉树的实现

2.1 二叉树的结构定义

c 复制代码
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;//左孩子
	struct BinaryTreeNode* right;//右孩子
}BTNode;

2.2 构造一棵二叉树

c 复制代码
BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("Buynode::malloc");
		return;
	}
	node->data = x;
	node->left = node->right = NULL;
	return node;
}
BTNode* CreatBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

2.3 前序遍历

深度优先遍历(DFS)

c 复制代码
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

下图是前序遍历的一部分递归展开图

首先判断是不是空树,

2.4 中序遍历

c 复制代码
void MidOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	MidOrder(root->left);
	printf("%d ", root->data);
	MidOrder(root->right);
}

这是中序的一部分递归

2.5 后序遍历

c 复制代码
void LateOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	LateOrder(root->left);
	LateOrder(root->right);
	printf("%d ", root->data);
}

2.6 层序遍历

也叫做广度优先遍历(BFS)

c 复制代码
typedef struct BinaryTreeNode* QDataTpye;
typedef struct QueueNode
{
	QDataTpye val;
	struct QueueNode* next;
}QueueNode;


typedef struct Queue
{
	QueueNode* ptail;
	QueueNode* phead;
	int size;
}Queue;

//初始化
void QueueInit(Queue*pq);

//队尾插入
void QueuePush(Queue* pq, QDataTpye x);

//队头删除
void QueuePop(Queue* pq);

//取队头队尾的数据
QDataTpye QueueFront(Queue* pq);
QDataTpye QueueBack(Queue* pq);

//个数
int QueueSize(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//销毁
void QueueDestory(Queue* pq);


void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataTpye x)
{
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("QueuePush::malloc");
		return;
	}

	newnode->next = NULL;
	newnode->val = x;

	//空的
	if (pq->phead == NULL )
	{
		pq->phead = pq->ptail = newnode;
	}
	else//尾插
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	//一个节点
	if (pq->phead->next == NULL)
	{
		
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
		
	}
	else
	{
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}

	pq->size--;
	
}

QDataTpye QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}

QDataTpye QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

void QueueDestory(Queue* pq)
{
	assert(pq);
	QueueNode* cur = NULL;
	cur = pq->phead;
	while (cur)
	{
		QueueNode* tem = cur->next;
		free(cur);
		cur = tem;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}


void TreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
}

2.7 二叉树结点个数

比如说学校需要统计人数:第一种方法就是一个一个的统计,这样很繁琐,因此就有了分而治之的思想,也就是校长将任务分给每个学院的院长,然后院长再分配给辅导员,辅导员在分配给每个班级的班长,班长统计完加上他自己然后上交给辅导员,辅导员加上他自己在上交给院长,依次类推。

c 复制代码
int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

2.8 二叉树叶子结点个数

c 复制代码
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL )
		return 1;
	else
		return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);

}

2.9 二叉树的高度

c 复制代码
int BinaryTreeHigh(BTNode* root)
{
	if (root == NULL)
		return 0;
		
	return BinaryTreeHigh(root->left) > BinaryTreeHigh(root->right) ? BinaryTreeHigh(root->left) + 1 :BinaryTreeHigh(root->right) + 1;
}

对于上述的代码会有一个严重的问题,这个问题就是当一颗树足够大时,最后一句代码会重复算很多次。如下图所示,先判断左子树和右子树的大小,6它没有左右子树,就执行后面条件返回1后,然后3的右子树和左子树的高度相比较,但是它忘记了之前的右子树6所返回的高度,因此需要重新去调,然后得到右子树的高度1,然后与左子树相比较,比较的结果返回给2,然后2再比较,依次类推,它会重复很多次,因此我们需要记录他的左右子树的高度。

c 复制代码
int BinaryTreeHigh(BTNode* root)
{
	if (root == NULL)
		return 0;
	int lefthigh = BinaryTreeHigh(root->left);
	int righthigh = BinaryTreeHigh(root->right);

	return lefthigh > righthigh ? lefthigh + 1 : righthigh + 1;
}

2.10 二叉树第k层结点个数

c 复制代码
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	
		return BinaryTreeLevelKSize(root->left, k-1) + BinaryTreeLevelKSize(root->right, k-1);
}

2.11 二叉树查找值为x的节点

c 复制代码
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	  return NULL;
	
	if (root->data == x)
		return root;
	
	BTNode* ret1 = BinaryTreeFind(root->left, x);
	return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);
	return ret2;

	return NULL;
}

2.12 二叉树的销毁

c 复制代码
void Treedestory(BTNode*root)
{
  if(root == NULL)
    return;
  Treedestory(root->left);
  Treedestory(root->right);
  free(root);
}
相关推荐
雨落在了我的手上9 小时前
Java数据结构(十一):优先级队列(堆)
数据结构
带多刺的玫瑰14 小时前
Leecode#26刷题之删除有序数组中的重复项
数据结构·算法·leetcode
洋不写bug15 小时前
二叉树(二) 常见基础操作解析|结点数、树高、查找结点、判断完全二叉树
java·开发语言·数据结构·完全二叉树·二叉树结点数·树高·查找结点
化雨成烟16 小时前
C++之AVL树
数据结构
wabs66617 小时前
关于二叉树【429.N叉树的层序遍历的思考】
数据结构·c++·算法·leetcode·二叉树
C++ 老炮儿的技术栈17 小时前
MFC CPtrArray的用法
开发语言·数据结构·c++·算法·mfc·c
不会就选b17 小时前
算法日常・每日刷题--<贪心>6
数据结构·算法·leetcode
青山木18 小时前
Hot 100 --- 跳跃游戏 II
java·数据结构·算法·leetcode·贪心算法
神明不懂浪漫19 小时前
【第四章】索引——B+树、回表,加快数据库的查找能力的利器
开发语言·数据结构·数据库·经验分享·笔记·b树