【C/C++ 数据结构】-就这一篇博客让你玩爆二叉树的各种遍历问题!!!

news2024/9/21 22:31:58

作者:学Java的冬瓜
冬瓜的主页:☀冬瓜的主页🌙
专栏:【C/C++ 数据结构与算法】
分享:被苦难淬炼过的人,内心真诚。——都靓评宋濂
主要内容:二叉树的递归前序遍历、中序遍历、后序遍历、层序遍历。以及非递归的前序、中序、后序、层序遍历。线索二叉树的创建和线索化和中序遍历。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文章目录

  • 一、前序创建二叉树
  • 二、二叉树递归遍历
    • 1、前序递归遍历
    • 2、中序递归遍历
    • 3、后序递归遍历
    • 4、层序遍历递归
  • 三、二叉树非递归遍历
    • 1、前序遍历非递归
    • 2、中序遍历非递归
    • 3、后序遍历非递归
    • 4、层序遍历非递归
  • 四、线索二叉树创建、线索化、遍历
    • 1、前序创建线索二叉树
    • 2、中序线索化线索二叉树
    • 3、线索二叉树中序遍历
  • 五、完整代码
    • 1、Traversal.h
    • 2、Queue_Stack.c
    • 3、orderTraversal.c
  • 六、成果演示
    • 1、创建二叉树
      • 1.1、递归层序遍历
      • 1.2、非递归前序遍历
      • 1.3、非递归后序遍历
    • 2、创建并线索化线索二叉树
      • 2.1、线索二叉树中序遍历
  • 七、总结
    • 1、二叉树的创建、遍历
      • 1.1、关于前序创建二叉树:
      • 1.2、关于递归遍历:
      • 1.3、关于非递归遍历:
    • 2、线索二叉树创建、线索化、遍历
      • 1.1、关于线索二叉树创建:
      • 1.2、关于线索二叉树的线索化:
      • 1.3、关于线索二叉树中序遍历:

一、前序创建二叉树

// 前序创建二叉树
TreeNode* createTree(char* str, int* pi) {
	if (str[*pi] == '#') {
		(*pi)++;
		return NULL;
	}

	TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
	if (root == NULL) {
		exit(-1);
	}
	root->val = str[(*pi)++];

	root->left = createTree(str, pi);
	root->right = createTree(str, pi);

	return root;
}

二、二叉树递归遍历

1、前序递归遍历

// 前序遍历递归
void prevOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	printf("%c ", root->val);
	prevOrder(root->left);
	prevOrder(root->right);
}

2、中序递归遍历

// 中序遍历递归
void inOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	inOrder(root->left);
	printf("%c ", root->val);
	inOrder(root->right);
}

3、后序递归遍历

// 后序遍历递归
void postOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	postOrder(root->left);
	postOrder(root->right);
	printf("%c ", root->val);
}

4、层序遍历递归

// 层次遍历递归
// 函数1
// 打印层序遍历的每一层(递归实现打印)
void printGivenLevel(TreeNode* root, int level) {
	if (root == NULL) {
		return;
	}
	if (level == 1) {
		printf("%c ", root->val);
	}
	else {
		printGivenLevel(root->left, level - 1);
		printGivenLevel(root->right, level - 1);
	}

}
// 函数2
// 求整棵树的高度
int hight(TreeNode* root) {
	if (root == NULL) {
		return 0;
	}
	int lhight = hight(root->left);
	int rhight = hight(root->right);
	return lhight > rhight ? lhight + 1 : rhight + 1;
}
// 函数3
// 层序遍历
void levelOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	int h = hight(root);
	for (int i = 1; i <= h; i++) {
		printGivenLevel(root, i);
	}
}

三、二叉树非递归遍历

1、前序遍历非递归

//前序遍历非递归
void prevOrderTraversal(TreeNode* root) {
	// 1、空树
	if (root == NULL) {
		return;
	}
	// 2、创建并初始化栈
	Stack st;
	StackInit(&st);
	// 3、设置一个访问指针,把根节点给它
	TreeNode* cur = root;

	// 4、操作
	while (cur !=NULL || !StackEmpty(&st)) {
		if (cur != NULL) {
			printf("%c ", cur->val);
			StackPush(&st, cur);
			cur = cur->left;
		}
		else{
			cur = StackPop(&st);
			cur = cur->right;
		}
	}
	// 销毁栈
	StackDestroy(&st);
}

2、中序遍历非递归

//中序遍历非递归
void inOrderTraversal(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	Stack st;
	StackInit(&st);
	TreeNode* cur = root;
	while (cur != NULL || !StackEmpty(&st)) {
		if (cur != NULL) {
			StackPush(&st, cur);
			cur = cur->left;
		}
		else {
			cur = StackPop(&st);
			printf("%c ", cur->val);
			cur = cur->right;
		}
	}
	StackDestroy(&st);
}

3、后序遍历非递归

//后序遍历非递归
void postOrderTraversal(TreeNode* root) {
	// 0、创建栈等准备工作
	if (root == NULL) {
		return;
	}
	Stack st;
	StackInit(&st);
	TreeNode* cur = root;

	// 前一个访问的节点
	TreeNode* prev = NULL;

	while (cur || !StackEmpty(&st)) {
		// 1、访问左节点直到遇到空
		if (cur != NULL) {
			StackPush(&st, cur);
			cur = cur->left;
		}
		else {
			// 2、此时cur=NULL,取出栈顶元素
			TreeNode* top = StackTop(&st);
			// 3、若弹出的当前栈顶元素右边为NULL或者已经被访问,
			//    则说明该子树的左右均访问完,打印该子树的根节点
			if (top->right == NULL || top->right == prev) {
				printf("%c ", top->val);
				StackPop(&st);

				// 把栈顶节点赋值给当前节点cur
				cur = top;
				// 将当前节点设置为下一次的前一个访问节点
				prev = top;
				// cur置空,标志以当前节点为根节点的树已经被访问
				cur = NULL;
			}
			else {
				cur = top->right;
			}
		}
	}
	StackDestroy(&st);
}

4、层序遍历非递归

// 层次遍历非递归
void levelOrderTraversal(TreeNode* root) {
	// 空树
	if (root == NULL) {
		return;
	}
	// 树非空
	Queue qu;
	// 1、初始化队列
	QueueInit(&qu);
	// 2、把根节点入队
	QueuePush(&qu, root);
	// 3、队列不空,则把当前节点队头弹出,打印
	//再把这个节点的左右子树的非空根,入队列
	while (!QueueEmpty(&qu)) {
		TreeNode* out = QueuePop(&qu);
		printf("%c ", out->val);

		if (out->left != NULL) {
			QueuePush(&qu, out->left);
		}
		if (out->right != NULL) {
			QueuePush(&qu, out->right);
		}
	}
	// 5、销毁队列
	QueueDestroy(&qu);
}

四、线索二叉树创建、线索化、遍历

注意:这里的线索二叉树我用了带头的方式。
二叉树的线索链表有个头节点head,
这个head的lchild指向二叉树的根节点,head的rchild指向中序遍历时访问的最后一个节点(除掉这个根节点的最后一个)
同时,二叉树中序遍历的第一个节点的lchild和最后一个节点的rchild都指向head

1、前序创建线索二叉树

// 前序创建线索二叉树
ThreadNode* createThreadTree(char* str, int* pi) {
	if (str[*pi] == '#') {
		(*pi)++;
		return NULL;
	}

	ThreadNode* root = (ThreadNode*)malloc(sizeof(ThreadNode));
	if (root == NULL) {
		exit(-1);
	}
	root->val = str[(*pi)++];
	root->ltag = Link;
	root->rtag = Link;

	root->lchild = createThreadTree(str, pi);
	root->rchild = createThreadTree(str, pi);

	return root;
}

2、中序线索化线索二叉树

// 中序线索化二叉树
// 函数1
void inThreading(ThreadNode** root, ThreadNode** pre) {
	if ((*root) == NULL) {   
		return;
	}
	
	inThreading((&(*root)->lchild), pre);  // 左子树线索化
	if ((*root)->lchild == NULL) {  // 前继线索
		(*root)->ltag = Thread;
		(*root)->lchild = (*pre);
	}
	if ((*pre)->rchild == NULL) {  // 后继线索
		(*pre)->rtag = Thread;
		(*pre)->rchild = (*root);
	}
	(*pre) = (*root);    // 保存pre指向root的前驱
	inThreading((&(*root)->rchild), pre);  // 右子树线索化
}
// 函数2
// 二叉树的线索链表有个头节点head,
//  这个head的lchild指向二叉树的根节点,head的rchild指向中序遍历时访问的最后一个节点(除掉这个根节点的最后一个)
//  同时,二叉树中序遍历的第一个节点的lchild和最后一个节点的rchild都指向head
void inOrderThread(ThreadNode** head, ThreadNode** root) {
	// 创建头节点
	(*head) = (ThreadNode*)malloc(sizeof(ThreadNode));
	if ((*head) == NULL) {
		exit(-1);
	}
	(*head)->ltag = Link; //表示lchild为指针
	(*head)->rtag = Thread; //表示rchild为线索
	(*head)->rchild = (*head);   //右线索回指

	if ((*root) == NULL) {
		(*head)->lchild = (*head); // 若二叉树为空,则左线索回指
	}
	else {
		(*head)->lchild = (*root);
		ThreadNode* pre = (*head);
		inThreading(root,&pre);

		pre->rchild = (*head);
		pre->rtag = Thread;   // 最后一个节点线索化
		(*head)->rchild = pre;
	}
}

3、线索二叉树中序遍历

// 线索二叉树中序遍历
void inOrderThreadTraversal(ThreadNode* head) {
	ThreadNode* cur = head->lchild;
	while (cur != head) {
		while (cur->ltag == Link) {
			cur = cur->lchild;
		}
		printf("%c ", cur->val);
		while (cur->rtag == Thread && cur->rchild != head) {
			cur = cur->rchild;
			printf("%c ", cur->val);
		}
		cur = cur->rchild;
	}
}

五、完整代码

1、Traversal.h

#pragma once


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

// 树的节点部分
typedef char TreeDataType;
typedef struct BinaryTreeNode {
	TreeDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}TreeNode;


// 线索树的节点部分

typedef enum PointerTag { 
	Link, Thread 
}PointerTag;  //Link=0,代表指针;Thread=1,代表线索
typedef struct ThreadNode {
	TreeDataType val;
	struct ThreadNode* lchild, * rchild;
	PointerTag ltag, rtag;
}ThreadNode;



// 队列部分
typedef TreeNode QDataType;
//链表的节点
typedef struct QNode
{
	QDataType data;
	struct QNode* next;
}QNode;
//存储head和tail两个指针,用来连接链表
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;
//队列初始化
void QueueInit(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
//队尾入队(尾插)
QNode* QueuePush(Queue* pq, TreeNode* x);
//队头出队(头删)
QNode* QueuePop(Queue* pq);
//获取队列有效数据的个数
int QueueSize(Queue* pq);
//判断队列是否为空
bool QueueEmpty(Queue* pq);



// 栈的部分
typedef TreeNode* STDataType;
typedef struct Stack
{
	STDataType* data;
	int top;
	int capacity;
}Stack;
//初始化栈
void StackInit(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
//压栈
STDataType StackPush(Stack* ps, STDataType x);
//出栈
STDataType StackPop(Stack* ps);
//获取栈顶数据
STDataType StackTop(Stack* ps);
//判断栈是否为空
bool StackEmpty(Stack* ps);

2、Queue_Stack.c

#define _CRT_SECURE_NO_WARNINGS

#include "Traversal.h"

//关于队列函数
//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = NULL;
	pq->tail = NULL;
}
//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur != NULL)
	{
		QNode* next = cur->next;

		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}
//队尾入队(尾插)
QNode* QueuePush(Queue* pq, TreeNode x)
{
	assert(pq);

	//注意1:创建新节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//1、空间申请失败
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	//2、空间申请成功
	newnode->data = x;
	newnode->next = NULL;


	//注意2:连接链表
	//3、处理队列链表头节点
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//4、处理其它节点
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	return newnode;
}
//队头出队(头删)
QNode* QueuePop(Queue* pq)
{
	assert(pq);
	//注意1:若队列中没有数据了,就不能出队了,会中止程序
	assert(pq->head);


	//重点:注意2:要把只有一个节点单独提出来,否则tail始终指向最后一个节点,它变成野指针
	if (pq->head->next == NULL)
	{
		QNode* ret = pq->head;
		pq->head = pq->tail = NULL;
		return ret;
	}
	else
	{
		//注意3:free()前,记录第一个节点的下一个节点
		QNode* next = pq->head->next;

		QNode* ret = pq->head;
		pq->head = next;
		return ret;
	}
}
// 获取队列有效数据的个数
int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;

	while (cur != NULL)
	{
		size++;
		cur = cur->next;
	}

	return size;
}
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	return pq->head == NULL;
}



// 关于栈部分函数
//初始化栈
void StackInit(Stack* ps)
{
	assert(ps);

	//注意:动态申请数组空间
	STDataType* tmp = (STDataType*)malloc(4 * sizeof(STDataType));
	//1、申请失败
	if (tmp == NULL)
	{
		printf("realloc fail\n");
		exit(-1);
	}
	//2、申请成功
	else
	{
		ps->data = tmp;
		ps->capacity = 4;
		ps->top = 0;
	}
}
//销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->top = 0;
}
//压栈/入栈
STDataType StackPush(Stack* ps, STDataType x)
{
	//1、断言,确保ps不等于NULL。
	assert(ps);

	//2、判断空间是否已满,满了就先增容
	if (ps->capacity == ps->top)
	{
		STDataType* tmp = (STDataType*)realloc(ps->data, 2 * ps->capacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			ps->data = tmp;
			ps->capacity *= 2;
		}
	}

	//3、尾插
	ps->data[ps->top] = x;
	ps->top++;
	return x;
}
//出栈
STDataType StackPop(Stack* ps)
{
	assert(ps);
	//注意:确保top不越界,栈空时,直接终止程序报错
	assert(ps->top > 0);

	ps->top--;
	return ps->data[ps->top];
}
//获取栈顶数据
STDataType StackTop(Stack* ps)
{
	assert(ps);
	//注意:若栈中没有数据了,ps->top=0,没有下面这步断言,会导致数组越界
	assert(ps->top > 0);

	return ps->data[ps->top - 1];
}
//判断栈是否为空
bool StackEmpty(Stack* ps)
{
	assert(ps);

	return ps->top == 0;
}

3、orderTraversal.c

#define _CRT_SECURE_NO_WARNINGS

#include "Traversal.h"

void menu() {
	printf("**********************************************************************\n");
	printf("************   1.前序创建二叉树                      *****************\n");
	printf("************   递归:                                 *****************\n");
	printf("************   2.前序遍历递归     3.中序遍历递归     *****************\n");
	printf("************   4.后序遍历递归     5.层序遍历递归     *****************\n");
	printf("************   非递归:                               *****************\n");
	printf("************   6.前序遍历非递归   7.中序遍历非递归   *****************\n");
	printf("************   8.后序遍历非递归   9.层序遍历非递归   *****************\n");
	printf("**********************************************************************\n");
	printf("************   10.前序创建线索二叉树并中序线索化     *****************\n");
	printf("************   11.线索二叉树中序遍历                 *****************\n");
	printf("**********************************************************************\n");
	printf("************   0.退出                                *****************\n");
	printf("**********************************************************************\n");

	

}



// 前序遍历递归
void prevOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	printf("%c ", root->val);
	prevOrder(root->left);
	prevOrder(root->right);
}
// 中序遍历递归
void inOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	inOrder(root->left);
	printf("%c ", root->val);
	inOrder(root->right);
}
// 后序遍历递归
void postOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	postOrder(root->left);
	postOrder(root->right);
	printf("%c ", root->val);
}

// 层次遍历递归
// 函数1
// 打印层序遍历的每一层(递归实现打印)
void printGivenLevel(TreeNode* root, int level) {
	if (root == NULL) {
		return;
	}
	if (level == 1) {
		printf("%c ", root->val);
	}
	else {
		printGivenLevel(root->left, level - 1);
		printGivenLevel(root->right, level - 1);
	}

}
// 函数2
// 求整棵树的高度
int hight(TreeNode* root) {
	if (root == NULL) {
		return 0;
	}
	int lhight = hight(root->left);
	int rhight = hight(root->right);
	return lhight > rhight ? lhight + 1 : rhight + 1;
}
// 函数3
// 层序遍历
void levelOrder(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	int h = hight(root);
	for (int i = 1; i <= h; i++) {
		printGivenLevel(root, i);
	}
}





//前序遍历非递归
void prevOrderTraversal(TreeNode* root) {
	// 1、空树
	if (root == NULL) {
		return;
	}
	// 2、创建并初始化栈
	Stack st;
	StackInit(&st);
	// 3、设置一个访问指针,把根节点给它
	TreeNode* cur = root;

	// 4、操作
	while (cur !=NULL || !StackEmpty(&st)) {
		if (cur != NULL) {
			printf("%c ", cur->val);
			StackPush(&st, cur);
			cur = cur->left;
		}
		else{
			cur = StackPop(&st);
			cur = cur->right;
		}
	}
	// 销毁栈
	StackDestroy(&st);
}
//中序遍历非递归
void inOrderTraversal(TreeNode* root) {
	if (root == NULL) {
		return;
	}
	Stack st;
	StackInit(&st);
	TreeNode* cur = root;
	while (cur != NULL || !StackEmpty(&st)) {
		if (cur != NULL) {
			StackPush(&st, cur);
			cur = cur->left;
		}
		else {
			cur = StackPop(&st);
			printf("%c ", cur->val);
			cur = cur->right;
		}
	}
	StackDestroy(&st);
}
//后序遍历非递归
void postOrderTraversal(TreeNode* root) {
	// 0、创建栈等准备工作
	if (root == NULL) {
		return;
	}
	Stack st;
	StackInit(&st);
	TreeNode* cur = root;

	// 前一个访问的节点
	TreeNode* prev = NULL;

	while (cur || !StackEmpty(&st)) {
		// 1、访问左节点直到遇到空
		if (cur != NULL) {
			StackPush(&st, cur);
			cur = cur->left;
		}
		else {
			// 2、此时cur=NULL,取出栈顶元素
			TreeNode* top = StackTop(&st);
			// 3、若弹出的当前栈顶元素右边为NULL或者已经被访问,
			//    则说明该子树的左右均访问完,打印该子树的根节点
			if (top->right == NULL || top->right == prev) {
				printf("%c ", top->val);
				StackPop(&st);

				// 把栈顶节点赋值给当前节点cur
				cur = top;
				// 将当前节点设置为下一次的前一个访问节点
				prev = top;
				// cur置空,标志以当前节点为根节点的树已经被访问
				cur = NULL;
			}
			else {
				cur = top->right;
			}
		}
	}
	StackDestroy(&st);
}

// 层次遍历非递归
void levelOrderTraversal(TreeNode* root) {
	// 空树
	if (root == NULL) {
		return;
	}
	// 树非空
	Queue qu;
	// 1、初始化队列
	QueueInit(&qu);
	// 2、把根节点入队
	QueuePush(&qu, root);
	// 3、队列不空,则把当前节点队头弹出,打印
	//再把这个节点的左右子树的非空根,入队列
	while (!QueueEmpty(&qu)) {
		TreeNode* out = QueuePop(&qu);
		printf("%c ", out->val);

		if (out->left != NULL) {
			QueuePush(&qu, out->left);
		}
		if (out->right != NULL) {
			QueuePush(&qu, out->right);
		}
	}
	// 5、销毁队列
	QueueDestroy(&qu);
}



// ABD##E##CF##G##
// 前序创建二叉树
TreeNode* createTree(char* str, int* pi) {
	if (str[*pi] == '#') {
		(*pi)++;
		return NULL;
	}

	TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
	if (root == NULL) {
		exit(-1);
	}
	root->val = str[(*pi)++];

	root->left = createTree(str, pi);
	root->right = createTree(str, pi);

	return root;
}


// 前序创建线索二叉树
ThreadNode* createThreadTree(char* str, int* pi) {
	if (str[*pi] == '#') {
		(*pi)++;
		return NULL;
	}

	ThreadNode* root = (ThreadNode*)malloc(sizeof(ThreadNode));
	if (root == NULL) {
		exit(-1);
	}
	root->val = str[(*pi)++];
	root->ltag = Link;
	root->rtag = Link;

	root->lchild = createThreadTree(str, pi);
	root->rchild = createThreadTree(str, pi);

	return root;
}

// 中序线索化二叉树
// 函数1
void inThreading(ThreadNode** root, ThreadNode** pre) {
	if ((*root) == NULL) {   // 不能这样截至递归,会造成叶子节点后继无法线索化
		return;
	}
	
	inThreading((&(*root)->lchild), pre);  // 左子树线索化
	if ((*root)->lchild == NULL) {  // 前继线索
		(*root)->ltag = Thread;
		(*root)->lchild = (*pre);
	}
	if ((*pre)->rchild == NULL) {  // 后继线索
		(*pre)->rtag = Thread;
		(*pre)->rchild = (*root);
	}
	(*pre) = (*root);    // 保存pre指向root的前驱
	inThreading((&(*root)->rchild), pre);  // 右子树线索化
}
// 函数2
// 二叉树的线索链表有个头节点head,
//  这个head的lchild指向二叉树的根节点,head的rchild指向中序遍历时访问的最后一个节点(除掉这个根节点的最后一个)
//  同时,二叉树中序遍历的第一个节点的lchild和最后一个节点的rchild都指向head
void inOrderThread(ThreadNode** head, ThreadNode** root) {
	// 创建头节点
	(*head) = (ThreadNode*)malloc(sizeof(ThreadNode));
	if ((*head) == NULL) {
		exit(-1);
	}
	(*head)->ltag = Link; //表示lchild为指针
	(*head)->rtag = Thread; //表示rchild为线索
	(*head)->rchild = (*head);   //右线索回指

	if ((*root) == NULL) {
		(*head)->lchild = (*head); // 若二叉树为空,则左线索回指
	}
	else {
		(*head)->lchild = (*root);
		ThreadNode* pre = (*head);
		inThreading(root,&pre);

		pre->rchild = (*head);
		pre->rtag = Thread;   // 最后一个节点线索化
		(*head)->rchild = pre;
	}

}

// 线索二叉树中序遍历
void inOrderThreadTraversal(ThreadNode* head) {
	ThreadNode* cur = head->lchild;
	while (cur != head) {
		while (cur->ltag == Link) {
			cur = cur->lchild;
		}
		printf("%c ", cur->val);
		while (cur->rtag == Thread && cur->rchild != head) {
			cur = cur->rchild;
			printf("%c ", cur->val);
		}
		cur = cur->rchild;
	}
}

int main()
{
	TreeNode* root = NULL;

	ThreadNode* threadRoot = NULL;
	ThreadNode* threadHead = NULL;
	int input = 0;

	do {
		menu();
		scanf("%d", &input);

		switch (input) {
		case 0:
			break;
		case 1:
			printf("请根据前序输入你要创建的二叉树,'NULL'用'#'表示\n");
			char str1[100];
			scanf("%s", str1);
			int i1 = 0;
			root = createTree(str1, &i1);
			break;
		case 2:
			prevOrder(root);
			printf("\n");
			break;
		case 3:
			inOrder(root);
			printf("\n");
			break;
		case 4:
			postOrder(root);
			printf("\n");
			break;
		case 5:
			levelOrder(root);
			printf("\n");
			break;
		case 6:
			prevOrderTraversal(root);
			printf("\n");
			break;
		case 7:
			inOrderTraversal(root);
			printf("\n");
			break;
		case 8:
			postOrderTraversal(root);
			printf("\n");
			break;
		case 9:
			levelOrderTraversal(root);
			printf("\n");
			break;
		case 10:
			printf("请根据前序输入你要创建的二叉树,'NULL'用'#'表示\n");
			char str2[100];
			scanf("%s", str2);
			int i2 = 0;
			threadRoot = createThreadTree(str2, &i2);     // 创建二叉树
			inOrderThread(&threadHead, &threadRoot);       // 线索化二叉树
			break;
		case 11:
		{
			inOrderThreadTraversal(threadHead);
			printf("\n");
			break;
		}
		default:
			break;
		}
			

	} while (input);


	return 0;
}

六、成果演示

在这里我就只选部分遍历或者代码较为复杂的部分,给出成果展示:

1、创建二叉树

在这里插入图片描述

1.1、递归层序遍历

在这里插入图片描述

1.2、非递归前序遍历

在这里插入图片描述

1.3、非递归后序遍历

在这里插入图片描述

2、创建并线索化线索二叉树

在这里插入图片描述

2.1、线索二叉树中序遍历

在这里插入图片描述

七、总结

1、二叉树的创建、遍历

1.1、关于前序创建二叉树:

  1. 给了一个字符串,和取地址的i(防止递归时出错,确保按照字符顺序一个一个访问),先判断当前节点是否为空,空则返回,不空则申请空间,并给它赋值,然后进入左子树,右子树递归创建二叉树。

1.2、关于递归遍历:

  1. 二叉树的递归遍历的前序,中序、后序模板一模一样,最简单。
  2. 二叉树的递归遍历的层序遍历,在levelOrder函数中,可以用hight求树的高度,再根据层数,用printGivenLevel打印指定层的节点

1.3、关于非递归遍历:

  1. 前序、中序、后序的非递归都用到栈,层序非递归用到队列

  2. 前序非递归和中序非递归:它们的模板一样,cur!=NULL || !StackEmpty(&st),满足两者中的一个就继续进入循环操作。其中前序非递归是先cur!=NULL时,并打印。而中序遍历则是刚好cur==NULL时打印。且前序非递归和中序非递归都是满足cur!=NULL时入栈,cur=NULL时出栈。

  3. 后序非递归:要增加一个prev指针来判断是否已经访问右孩子。它同样要 cur!=NULL || !StackEmpty(&st) 才循环。具体操作是:先cur!=NULL一直入栈。当cur=NULL时,用StackTop取出栈顶元素top,如果top->right=NULL(右孩子为空)或者top->right=prev(右孩子已经被访问),那就出栈并打印栈顶元素top。否则(top->right!=NULL && top->right!=prev),那就访问右孩子,即cur=top->right。

  4. 层序遍历非递归:先把根节点入队列,满足!QueueEmpty,即队列不空时反复循环。进入循环时,先把队头出队并打印,再判断这个节点有没有左孩子,有则入队,再判断有没有有孩子,有则入队。

2、线索二叉树创建、线索化、遍历

1.1、关于线索二叉树创建:

  1. 给了一个字符串,和取地址的i(防止递归时出错,确保按照字符顺序一个一个访问)
  2. 先判断当前节点是否为空,空则返回,不空则申请空间,并给它赋值,注意:赋值时,把当前节点的前驱标志(ltag)和后继标志(rtag)都赋值为Link(设置了一个枚举,Link为第一个元素,故Link=0)
  3. 然后进入左子树,右子树递归创建二叉树。

1.2、关于线索二叉树的线索化:

  1. 注意使用二级指针,因为创建了树之后,线索化就是还要改变节点,因此需要二级指针去操作。
  2. 这里也需要设置一个pre,用来方便线索化前驱后继的操作。
  3. 在inOrderThread函数里,先创建头节点并给它赋值。再判断将要线索化的树是否为空,如果空则头节点head左lchild和rchild都指向自己。不空则head的左指针指向树的根节点。然后用pre记录第一个head这个头节点。
  4. 再一步就是进入中间部分的线索化,用inThreading中序递归去实现中间部分的前驱后继的线索化。
  5. 最后就把中序的最后一个节点右(pre)指针指向head,head的右指针指向最后一个节点(pre),完成线索化

1.3、关于线索二叉树中序遍历:

  1. 当cur!=head时,进入第一个循环
  2. 然后当cur->ltag=Link时,循环一直访问左孩子,直到退出里面一层循环(此时cur->ltag=head),先打印。这个里面的循环是打印每棵子树最左边的节点。
  3. 然后判断cur->rtag=Thread && cur->rchild!=head cur=cur->rchild,打印cur的值。这个循环是打印每棵子树的根节点
  4. 然后再cur=cur->rchild,看cur的右边是否还有孩子,有的话继续返回大循环操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/99231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

你还会想起这道题吗(another version)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 众所周知&#xff0c;原神是由米哈游自主研发的一款全新开放世界冒险游戏。游戏发生在一个被称作「提瓦特」的幻想世界&#xff0c;在这里&#xff0c;被神选中的人将被授予「神之眼…

“价格+产品+服务”:京东手机开启“复合竞赛”

市场唯一不变的就是变化&#xff0c;那些最终生存下来的企业往往是最能适应变化的。 11月份&#xff0c;京东再次强调“低价”策略的重要性&#xff0c;背后无疑是其对消费环境变化做出的及时反应。“消费者对价格更为敏感&#xff0c;对高性价比商品的需求依旧是主流”&#…

YOLO-V5 算法和代码解析系列 —— 学习路线规划综述

目录标题为什么学习 YOLO-V5 &#xff1f;博客文章列表面向对象开源项目学习方法预备知识项目目录结构为什么学习 YOLO-V5 &#xff1f; 算法性能&#xff1a;与YOLO系列&#xff08;V1&#xff0c;V2&#xff0c;V3&#xff0c;V4&#xff09;相比&#xff0c;YOLO-V5效果最好…

【花雕动手做】有趣好玩的音乐可视化系列项目(33)---核酸托盘灯

偶然心血来潮&#xff0c;想要做一个音乐可视化的系列专题。这个专题的难度有点高&#xff0c;涉及面也比较广泛&#xff0c;相关的FFT和FHT等算法也相当复杂&#xff0c;不过还是打算从最简单的开始&#xff0c;实际动手做做试验&#xff0c;耐心尝试一下各种方案&#xff0c;…

RosonQt140——Qt Charts模块介绍和Qt绘制图表

Qt图表概述 Qt Charts能够创建时尚、互动、以数据为中心的用户界面。Qt Charts使用图形视图框架&#xff0c;便于集成。图表组件可以作为QWidget或QGraphicsWidget对象或QML类型使用。 QChart类管理不同类型的系列和其他图表相关对象的图形表示&#xff0c;如图例和坐标轴。QC…

33-Vue之ECharts高级-设置主题

ECharts高级-设置主题前言内置主题自定义主题前言 本篇来学习下ECharts中如何设置图表主题 内置主题 ECharts 中默认内置了两套主题: light dark var chart echarts.init(dom, light) var chart echarts.init(dom, dark)<!DOCTYPE html> <html lang"en&quo…

前缀树介绍,定义,图文详解分析——Java/Kotlin双版本代码

前缀树 前缀树&#xff0c;又称作字典树&#xff0c;用一个树状的数据结构储存字典中的所有单词。 列&#xff0c;一个包含can、cat、come、do、i、in、inn的前缀树如下图所示&#xff1a; 前缀树是一个多叉树&#xff0c;一个节点可能存在多个节点。除根节点外&#xff0c;每…

Spring5框架总结学习(从入门到进阶)

文章目录Spring51、如何创建一个Spring项目&#xff08;idea版&#xff09;2、 IOC容器1、XML解析工厂模式反射2、bean管理1、总述2、基于XML创建对象3、基于XML注入属性4、基于XML注入属性&#xff0c;属性值为空或特殊符号5、基于XML注入属性&#xff0c;外部bean6、基于XML注…

【第十二章 MVCC(多版本并发控制),隐藏字段,undolog(版本链),readview,原理分析(RC,RR)】

第十二章 MVCC&#xff08;多版本并发控制&#xff09;&#xff0c;隐藏字段&#xff0c;undolog&#xff08;版本链&#xff09;&#xff0c;readview&#xff0c;原理分析&#xff08;RC&#xff0c;RR&#xff09; 1.基本概念: &#xff08;1&#xff09; 当前读&#xff1…

hevc 基于均值的RDO模式删减

1 在获得粗略模式候选列表L4之后&#xff0c;对尺寸为4x4和8x8的PU保留8种预测模式&#xff0c; 对尺寸16x16&#xff0c;32x32, 64x64的PU保留3种预测模式&#xff0c;如果可以跳过部分模式的RDO计算&#xff0c;则可以进一步减少编码时间。 2 由于HCost计算是RDO计算的一种较…

springBoot+Cache(自定义有效时间配置)

一、背景 sprinbBoot的cache是不是支持动态设置缓存注解的&#xff0c;因此本次自己实现一个可以动态设置缓存时间的配置。 源码&#xff1a;示例地址 二、步骤 1、pom.xml添加依赖配置 <dependency><groupId>org.springframework.boot</groupId><arti…

Linux系统编程(续)

静态库制作及使用步骤&#xff1a; 1.将.c生成.o文件 gcc -c add.c -o add.o 2.使用ar工具制作静态库 ar rcs lib自定义库名.a 后面需要的.c文件 3.编译静态库到可执行文件中 gcc test.c 自制的库.a -o test 注意:如果程序中没有函数声明&#xff0c;编译器会自动给个隐式声明…

Java+MySQL基于SSM的在线投票系统

随着社会的发展,人们在处理一些问题的时候不同意见越来越多,这源于人们对思想的解放和对社会的认识。所以在处理同一问题上,为了征求不同人的意见在线投票系统诞生了。 基于SSM的在线投票系统以钦州学院为背景,运用在校所学习的软件开发原理,采用Spring&#xff1a;SpringMVC&a…

如何在网页画一个旋转的粉色圣诞树(含源代码)

0 效果 做法&#xff1a; 创建三个文件tree.html、tree.js、tree.css&#xff0c;放在同一个目录下按1、2、3小节填充这三个文件浏览器打开tree.html文件 1 .HTML-基本布局 搞一个.html文件&#xff0c;内容如下 <!DOCTYPE html> <html lang"en"><…

【OpenCV-Python】教程:7-1 理解 kNN (k-Nearest Neighbour)

OpenCV Python 理解kNN &#xff08;k-Nearest Neighbour&#xff09; 【目标】 理解 kNN 算法的基本概念 【理论】 kNN是监督学习中最简单的分类算法之一。其思想是在特征空间中搜索与测试数据最接近的匹配。我们将用下图来研究它。 在图像中&#xff0c;有两个"家族…

一文看懂 InnoDB 的内存淘汰逻辑(LRU)

InnoDB淘汰的逻辑是怎样的呢&#xff1f; InnoDB 内存管理用的是最近最少使用 (Least Recently Used, LRU) 算法&#xff0c;这个算法的核心就是淘汰最久未使用的数据。 下图是一个 LRU 算法的基本模型。 InnoDB 管理 Buffer Pool 的 LRU 算法&#xff0c;是用链表来实现的。…

Go项目实战:02-微服务micro services

1、微服务&#xff08;micro services&#xff09; 单体式架构服务 过往大家熟悉的服务器。 特性&#xff1a; 1、复杂性随着开发越来越高&#xff0c;遇到问题解决困难。2、技术债务逐渐上升。3、耦合度高&#xff0c;维护成本大。 - 1、出现bug&#xff0c;不容易排查 - 2…

[ 数据结构 -- 手撕排序算法第六篇 ] 快速排序

文章目录前言一、常见的排序算法二、快速排序的基本思想三、快速排序的不同实现1.hoare版本2. 挖坑法3. 前后指针法4.三种版本单趟排序结果5.快速排序三数取中优化6.小区间优化四、快速排序的特性总结前言 手撕排序算法第六篇&#xff1a;快速排序&#xff01; 从本篇文章开始…

JavaSE面试题(二)

1&#xff1a;说一说八大基本数据类型 2&#xff1a;面向对象 面向对象的核心&#xff0c;就是类和对象。Java中的面向对象的思想&#xff1a;万物皆对象。 类&#xff1a;是对一类事物的描述&#xff0c;是抽象的&#xff0c;看不见&#xff0c;摸不着。 对象&#xff1a;是实…

week 7 吴恩达 调参 ,batch norm,softmax

文章目录前言7.1调整参数的过程 Turing progress7.2、scale7.3 如果在实践中探寻超参数7.4 batch normalization 批量归一化7.5 将BN算法拟合到神经网络中7.6 为什么 BN有效&#xff1f;7.7测试时的BN7.8 7.9 softmax regression7.10深度学习的框架前言 7.1调整参数的过程 Turi…