链式二叉树的 遍历 及 常用接口(带源码)

本文详细介绍了如何创建链式二叉树,包括节点结构、节点创建和链接。接着,文章探讨了四种遍历方法(前序、中序、后序和层序)及其递归实现。此外,还提供了计算二叉树节点个数、叶节点个数、高度以及第K层节点个数的算法。所有关键代码均有展示。
摘要由CSDN通过智能技术生成

1:创建链式二叉树

用简单的方法,创建简易的链式二叉树

1.1节点基本结构

链式二叉树的节点基本结构一个数据变量,两个指向节点的指针组成

typedef int HeapData;
typedef struct Heap
{
	HeapData data;//数据
	struct Heap* LeftNode;//左孩子
	struct Heap* RightNode;//右孩子
}HP;

1.2节点的创建

由于咱是链表,需要用malloc从堆上申请一个节点空间,并将数据赋值,左右孩子在申请时默认指向NULL。

HP* CreateHeap(HeapData x)
{
	HP* cur = (HP*)malloc(sizeof(HP));
	if (NULL == cur)//如果内存申请失败
	{
		perror("malloc");
		exit(-1);
	}
	cur->data = x;//给节点数据变量赋值
	cur->LeftNode = cur->RightNode = NULL;//将指针置空
	return cur;//把节点指针返回
}

1.3节点之间的链接

选一个节点做根,并把其他节点根据自身要求连接即可

int main()
{
	HP *p1, * p2, * p3, * p4, * p5, * p6, * p7, * p8;
	p1 = CreateHeap(10);//创建节点
	p2 = CreateHeap(20);//创建节点
	p3 = CreateHeap(30);//创建节点
	p4 = CreateHeap(40);//创建节点
	p5 = CreateHeap(50);//创建节点
	p6 = CreateHeap(60);//创建节点
	p7 = CreateHeap(70);//创建节点
	p8 = CreateHeap(80);//创建节点
	p1->LeftNode = p2;//p1节点左孩子指针,指向p2
	p1->RightNode = p3;//p1节点右孩子指针,指向p3
	p2->LeftNode = p4;//p2节点左孩子指针,指向p4
	p2->RightNode = p5;
	p3->LeftNode = p6;
	p3->RightNode = p7;
	p4->LeftNode = p8;
	return 0;
}

上述代码我创建的链式二叉树如下图创建节点时默认赋值左右孩子为NULL,所以创建好后只管链接就好了没链接的默认指向NULL表示结束了

注意:下面代码演示也是用下图二叉树测试哦
在这里插入图片描述

2:遍历二叉树

遍历二叉树分为前序遍历,中序遍历,后续遍历 及 层序遍历
四种方式的访问节点数据顺序分别为
前序:根(数据) --> 左子树 --> 右子树;
中序:左子树 --> 根(数据) --> 右子树;
后序:左子树 --> 右子树 --> 根(数据);
层序:从根开始自上而下每层从左到右访问;

递归的要点

由于本篇用的递归比较多,我简要总结了递归函数的俩个要点
1:单层递归的主要逻辑
2:递归结束条件
比如
前序遍历的逻辑:先访问根再访问左孩子,然后访问右孩子
前序遍历结束条件:遇到NULL结束

2.1前序遍历

由于数据访问顺序是根(数据) --> 左子树 --> 右子树,
所以遍历结果是:10 20 40 80 NULL NULL NULL 50 NULL NULL 30 60 NULL NULL 70 NULL NULL

//前序遍历
void FrontHeap(HP* head)
{
	if (NULL == head)//遍历完了
	{
		printf(" NULL");
		return;
	}
	printf(" %d", head->data);//根(访问数据)
	FrontHeap(head->LeftNode);//左子树
	FrontHeap(head->RightNode);//右子树
}

2.2中序遍历

由于数据访问顺序是左子树 --> 根(数据) --> 右子树;
所以遍历结果是:NULL 80 NULL 40 NULL 20 NULL 50 NULL 10 NULL 60 NULL 30 NULL 70 NULL

void MiddleHeap(HP* head)//中序遍历
{
	if (NULL == head)
	{
		printf(" NULL");
		return;
	}
	MiddleHeap(head->LeftNode);//左子树
	printf("%d ", head->data);//根(访问数据)
	MiddleHeap(head->RightNode);//右子树
}

2.3后序遍历

由于数据访问顺序是左子树 --> 右子树 --> 根(数据);
所以遍历结果是:NULL NULL 80 NULL 40 NULL NULL 50 20 NULL NULL 60 NULL NULL 70 30 10

void TailHeap(HP* head)//后序遍历
{
	if (NULL == head)
	{
		printf(" NULL");
		return;
	}
	TailHeap(head->LeftNode);//左子树
	TailHeap(head->RightNode);//右子树
	printf(" %d", head->data);//根(访问数据)
}

2.4层序遍历

层序遍历的实现需要用队的先进先出思想,所以层序遍历需要用队来辅助实现
实现思想:
1:将链式二叉树的根入队
2:每次将队头出队时先打印,然后判断左右孩子是否为空,不为空则将左右孩子入队。最后将对头出队,依此循环;
3:如果队为空时则结束循环

层序:从根开始自上而下每层从左到右访问;
所以遍历结果是:10 20 30 40 50 60 70 80

//层序遍历
void StrataHeap(HP* head)
{
	assert(head);
	Team Tm;//创建一个队结构
	TmInit(&Tm);//初始化队
	TmPus(&Tm, head);//将链式二叉树的根入队
	while (!TmEmpty(&Tm))//队内不为空(有数据)则进入循环
	{
		HP* cur = TmHead(&Tm);
		printf("%d ", cur->data);//打印队头数据
		if (cur->LeftNode != NULL)//如果对头数据左孩子不为空
		{
			TmPus(&Tm, cur->LeftNode);//左孩子入队
		}
		if (cur->RightNode != NULL)//如果对头数据右孩子不为空
		{
			TmPus(&Tm, cur->RightNode);//右孩子入队
		}
		TmPop(&Tm);//出队
	}
}

这里只展示了层序遍历函数,此文章用到的所有函数完整源代码放在文章的结尾

3 链式二叉树常用外部接口

1:求链式二叉树节点个数
2:求链式二叉树叶节点个数
3:求链式二叉树的高度(有几层)
4:求链式二叉树第K层节点个数

3.1求链式二叉树节点个数

依然为递归实现:如果该节点为空则不是节点返回0,不为空则返回左节点个数加右节点个数 ,加 1(加的是当前节点)

//求二叉树节点个数
int HeapSize(HP* head)
{
	if (NULL == head)//如果为空则不是节点返回0
	{
		return 0;
	}
	return HeapSize(head->LeftNode) 
	+ HeapSize(head->RightNode) + 1;
	//返回 左节点个数加右节点个数 ,加 1(加的是当前节点)
}

3.2求链式二叉树叶节点个数

依然为递归实现
主要思想:如果该节点为空返回0,如果该节点左右孩子都为NULL则他为叶节点所以返回1 ,递归左右孩子并相加。

//求二叉树叶子节点个数
int HeapLeafSize(HP* head)
{
	if (head == NULL)
	{
		return 0;
	}
	if (head->LeftNode == NULL && head->RightNode == NULL)
	{
		return 1;//如果为叶节点则返回1
	}
	return HeapLeafSize(head->LeftNode) 
	+ HeapLeafSize(head->RightNode);//递归左右孩子并相加
}

3.3求链式二叉树的高度(有几层)

依然为递归实现
主要思想:取左右节点节点较多的一方

//求二叉树高度
int Heaplen(HP* head)
{
	if (head == NULL)
	{
		return 0;
	}
	int left, right;
	left = Heaplen(head->LeftNode)+1;
	right = Heaplen(head->RightNode)+1;
	return left > right ? left : right;
}

3.4求链式二叉树第K层节点个数

依然为递归实现
主要思想:K为5时,由于每次递归的深度为1所以每次递归传k-1,当 k 等于1时说明已经到达第层。如不为空则返回1;

int HeapLevelSize(HP* head, int k )//求二叉树第K层节点个数
{
	if (NULL == head)
	{
		return 0;
	}
	if (k == 1)//说明到达第k层了
	{
		return 1;
	}
	//如果K>1
	return HeapLevelSize(head->LeftNode, k - 1) + HeapLevelSize(head->RightNode, k - 1);
}

本文章所用代码

Heap.h

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int HeapData;
typedef struct Heap
{
	HeapData data;//数据
	struct Heap* LeftNode;//左孩子
	struct Heap* RightNode;//右孩子
}HP;

HP* CreateHeap(HeapData x);//创建二叉树节点

void FrontHeap(HP* head);//前序遍历
void MiddleHeap(HP* head);//中序遍历
void TailHeap(HP* head);//后序遍历
void StrataHeap(HP* head);//层序遍历
int HeapSize(HP* head);//求二叉树节点个数
int HeapLeafSize(HP* head);//求二叉树叶子节点个数
int Heaplen(HP* head);//求二叉树高度
int HeapLevelSize(HP* head , int k);//求二叉树第K层节点个数

Team.h

#pragma once
#include"Heap.h"

typedef HP* TmData;

typedef struct TeamNode
{
	TmData data;//队的数据
	struct TeamNode* next;//链表指针
}TmNode;

typedef struct Team
{
	TmNode* head;//队头
	TmNode* tail;//队尾
	int size;//队内数据个数
}Team;
//由于单链表尾删不太方便,所以咱采用头删尾插的思路
void TmInit(Team* Tm);//初始化
void TmDestroy(Team* Tm);//销毁
void TmPus(Team* Tm,TmData data);//入队
void TmPop(Team* Tm);//出队
TmData TmHead(Team* Tm);//获取头部数据
TmData TmTail(Team* Tm);//获取尾部数据
int TmSize(Team* Tm);//获取队中数据个数
bool TmEmpty(Team* Tm);//判断队是否为空

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
#include"Team.h"
HP* CreateHeap(HeapData x)
{
	HP* cur = (HP*)malloc(sizeof(HP));
	if (NULL == cur)//如果内存申请失败
	{
		perror("malloc");
		exit(-1);
	}
	cur->data = x;
	cur->LeftNode = cur->RightNode = NULL;
	return cur;
}
//前序遍历
void FrontHeap(HP* head)
{
	if (NULL == head)//遍历完了
	{
		printf(" NULL");
		return;
	}
	printf(" %d", head->data);//根
	FrontHeap(head->LeftNode);//左子树
	FrontHeap(head->RightNode);//右子树
}

void MiddleHeap(HP* head)//中序遍历
{
	if (NULL == head)
	{
		printf(" NULL");
		return;
	}
	MiddleHeap(head->LeftNode);//左子树
	printf(" %d", head->data);//根(访问数据)
	MiddleHeap(head->RightNode);//右子树
}
void TailHeap(HP* head)//后序遍历
{
	if (NULL == head)
	{
		printf(" NULL");
		return;
	}
	TailHeap(head->LeftNode);//左子树
	TailHeap(head->RightNode);//右子树
	printf(" %d", head->data);//根
}
//层序遍历
void StrataHeap(HP* head)
{
	assert(head);
	Team Tm;//创建一个队结构
	TmInit(&Tm);//初始化队
	TmPus(&Tm, head);//将链式二叉树的根入队
	while (!TmEmpty(&Tm))//队内不为空(有数据)则进入循环
	{
		HP* cur = TmHead(&Tm);
		printf("%d ", cur->data);//打印队头数据
		if (cur->LeftNode != NULL)//如果对头数据左孩子不为空
		{
			TmPus(&Tm, cur->LeftNode);//左孩子入队
		}
		if (cur->RightNode != NULL)//如果对头数据右孩子不为空
		{
			TmPus(&Tm, cur->RightNode);//右孩子入队
		}
		TmPop(&Tm);//出队
	}
}

求二叉树节点个数
//int HeapSize(HP* head)
//{
//	return head == NULL ? 0 : 
//		HeapSize(head->LeftNode) + HeapSize(head->RightNode) + 1;
//}
//求二叉树节点个数
int HeapSize(HP* head)
{
	if (NULL == head)//如果为空则不是节点返回0
	{
		return 0;
	}
	return HeapSize(head->LeftNode) + HeapSize(head->RightNode) + 1;
	//返回 左节点个数加右节点个数 ,加 1(加的是当前节点)
}

//求二叉树叶子节点个数
int HeapLeafSize(HP* head)
{
	if (head == NULL)
	{
		return 0;
	}
	if (head->LeftNode == NULL && head->RightNode == NULL)//如果为叶节点
	{
		return 1;
	}
	return HeapLeafSize(head->LeftNode) + HeapLeafSize(head->RightNode);//递归左右孩子并相加
}
//求二叉树高度
int Heaplen(HP* head)
{
	if (head == NULL)
	{
		return 0;
	}
	int left, right;
	left = Heaplen(head->LeftNode)+1;
	right = Heaplen(head->RightNode)+1;
	return left > right ? left : right;
}
int HeapLevelSize(HP* head, int k )//求二叉树第K层节点个数
{
	if (NULL == head)
	{
		return 0;
	}
	if (k == 1)//说明到达第k层了
	{
		return 1;
	}
	//如果K>1
	return HeapLevelSize(head->LeftNode, k - 1) + HeapLevelSize(head->RightNode, k - 1);
}


Team.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Team.h"


void TmInit(Team* Tm)//初始化
{
	assert(Tm);
	Tm->size = 0;
	Tm->head = NULL;
	Tm->tail = NULL;
}
void TmDestroy(Team* Tm)//销毁
{
	assert(Tm);
	TmNode* cur = Tm->head;
	while (cur)
	{
		TmNode* pp = cur;
		cur = cur->next;
		free(pp);//可不置空因为别人访问不到pp
	}
	Tm->head = NULL;
	Tm->tail = NULL;
}
void TmPus(Team* Tm, TmData data)//入队
{
	assert(Tm);
	TmNode* newNode = (TmNode*)malloc(sizeof(TmNode));
	if (newNode == NULL)//如果申请失败
	{
		perror("malloc");
		exit(-1);
	}
	newNode->data = data;
	newNode->next = NULL;
	if (Tm->head == NULL)
	{
		Tm->head = newNode;
		Tm->tail = newNode;
	}
	else
	{
		Tm->tail->next = newNode;
		Tm->tail = newNode;
	}
	Tm->size++;
}
bool TmEmpty(Team* Tm)//判断队是否为空
{
	assert(Tm);
	return !Tm->size;//队内无数据返回真
}
void TmPop(Team* Tm)//出队
{
	assert(Tm);
	if (TmEmpty(Tm))
	{
		printf("队内无数据\n");
			return;
	}
	TmNode* cur = Tm->head;
	Tm->head = Tm->head->next;//头删
	free(cur);
	if (Tm->head == NULL)//如果已经删完了
	{
		Tm->tail = NULL;
	}
	Tm->size--;
}
TmData TmHead(Team* Tm)//获取头部数据
{
	assert(Tm);
	if (TmEmpty(Tm))
	{
		printf("队内无数据\n");
		return NULL;
	}
	return Tm->head->data;

}
TmData TmTail(Team* Tm)//获取尾部数据
{
	assert(Tm);
	if (TmEmpty(Tm))
	{
		printf("队内无数据\n");
		return -1;
	}
	return Tm->tail->data;
}
int TmSize(Team* Tm)//获取队中数据个数
{
	assert(Tm);
	return Tm->size;
}

main.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Heap.h"

int main()
{
	HP *p1, * p2, * p3, * p4, * p5, * p6, * p7, * p8;
	p1 = CreateHeap(10);//创建节点
	p2 = CreateHeap(20);
	p3 = CreateHeap(30);
	p4 = CreateHeap(40);
	p5 = CreateHeap(50);
	p6 = CreateHeap(60);
	p7 = CreateHeap(70);
	p8 = CreateHeap(80);
	p1->LeftNode = p2;
	p1->RightNode = p3;
	p2->LeftNode = p4;
	p2->RightNode = p5;
	p3->LeftNode = p6;
	p3->RightNode = p7;
	p4->LeftNode = p8;
	FrontHeap(p1);
	printf("\n");
	MiddleHeap(p1);
	printf("\n");
	TailHeap(p1);
	printf("\n");
	StrataHeap(p1);
	printf("\n");
	printf("节点个数=>%d ", HeapSize(p1));
	printf("\n叶节点个数=>%d ", HeapLeafSize(p1));
	printf("\n树的高度=>%d ", Heaplen(p1)); 
	printf("\n第K层节点个数=>%d ", HeapLevelSize(p1,2));
	return 0;
}

希望这篇博客能够对你有所帮助,我们下期再见~

/* * 二叉树节点ADT接口 */ package dsa; public interface BinTreePosition extends Position { //判断是否有父亲(为使代码描述简洁) public boolean hasParent(); //返回当前节点的父节点 public BinTreePosition getParent(); //设置当前节点的父节点 public void setParent(BinTreePosition p); //判断是否为叶子 public boolean isLeaf(); //判断是否为左孩子(为使代码描述简洁) public boolean isLChild(); //判断是否有左孩子(为使代码描述简洁) public boolean hasLChild(); //返回当前节点的左孩子 public BinTreePosition getLChild(); //设置当前节点的左孩子(注意:this.lChild和c.parent都不一定为空) public void setLChild(BinTreePosition c); //判断是否为右孩子(为使代码描述简洁) public boolean isRChild(); //判断是否有右孩子(为使代码描述简洁) public boolean hasRChild(); //返回当前节点的右孩子 public BinTreePosition getRChild(); //设置当前节点的右孩子(注意:this.rChild和c.parent都不一定为空) public void setRChild(BinTreePosition c); //返回当前节点后代元素的数目 public int getSize(); //在孩子发生变化后,更新当前节点及其祖先的规模 public void updateSize(); //返回当前节点的高度 public int getHeight(); //在孩子发生变化后,更新当前节点及其祖先的高度 public void updateHeight(); //返回当前节点的深度 public int getDepth(); //在父亲发生变化后,更新当前节点及其后代的深度 public void updateDepth(); //按照中序遍历的次序,找到当前节点的直接前驱 public BinTreePosition getPrev(); //按照中序遍历的次序,找到当前节点的直接后继 public BinTreePosition getSucc(); //断绝当前节点与其父亲的父子关系 //返回当前节点 public BinTreePosition secede(); //将节点c作为当前节点的左孩子 public BinTreePosition attachL(BinTreePosition c); //将节点c作为当前节点的右孩子 public BinTreePosition attachR(BinTreePosition c); //前序遍历 public Iterator elementsPreorder(); //中序遍历 public Iterator elementsInorder(); //后序遍历 public Iterator elementsPostorder(); //层次遍历 public Iterator elementsLevelorder(); }
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值