【数据结构】树和二叉树

目录

树的基本概述

树的概念

二叉树的概念及结构

二叉树的程序实现

Tree.h

Tree.c

Test.c

树与二叉树oj练习题

二叉树的前序遍历

二叉树的最大深度

平衡二叉树

二叉树的遍历


树的基本概述

树的概念

节点的度:一个节点含有子树的个数,A的度为6

叶节点:度为0的节点,B,H,I都为叶节点

父节点:如果一个节点含有子节点,那么这个节点就是子节点的父节点

子节点:如果一个节点含有的子数的根节点就为该节点的子节点

兄弟节点:具有相同父节点的节点互为兄弟节点

树的度:这棵树内所有节点中的度的最大值

二叉树的概念及结构

概念:树的度不超过2的树就叫做二叉树

结构

1. 顺序存储:只有在树为完全二叉树时,才适用于顺序存储,否则会造成空间的浪费

2. 链式存储:由分为二叉链表和三叉链表。二叉链表中,每个节点中包含两个指针,一个指向左孩子,另一个指向右孩子。而三叉链表中,还设置有一个指向父节点的指针。

性质

1.规定根节点的层数是1,则一棵非空二叉树的第i层最多有2^{i-1}个节点

2.规定根节点的层数是1,则深度为h的二叉树最大节点数为2^h{}-1

3.对于任意一棵二叉树,如果度为0的节点个数为n0,度为2的节点个数为n2,则满足n0=n2+1

4.规定根节点的层数为1,则n个节点的满二叉树的深度为\log_{2}n+1

遍历:

以下图的二叉树为例

1.深度优先遍历

前序根节点        左子树        右子树A        B        D        E        C
中序左子树        根节点        右子树D        B        E        A        C
后序左子树        右子树        根节点D        E        B        C        A

2.广度优先遍历

核心思路:利用了队列“先进先出”的特点,父节点出队的时候带动子节点入队

        1.先将根节点入队

        2.父节点出栈的时候带动子节点入队,即A出队,BC入队

         3.如此反复(B出队 C出队)

 所以,遍历的顺序为A        B        C        D        E

满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。

完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

二叉树的程序实现

Tree.h

#pragma once
#include<stdio.h>
#include<stdlib.h>

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

void PrevOrder(BTNode* root);
void InOrder(BTNode* root);
void PostOrder(BTNode* root);
void TreeSize1(BTNode* root,int* psize);
int TreeSize2(BTNode* root);
int TreeLeafSize(BTNode* root);
void LevelOrder(BTNode* root);

Tree.c

1.深度优先遍历

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	else
	{
		printf(" % c", root->data);
		PrevOrder(root->left);
		PrevOrder(root->right);
	}
}

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	else
	{
		InOrder(root->left);
		printf(" % c", root->data);
		InOrder(root->right);
	}
}

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	else
	{
		PostOrder(root->left);
		PostOrder(root->right);
		printf(" % c", root->data);
	}
}

2.广度优先遍历(注意要链接队列的头文件)

void LevelOrder(BTNode* root)
{
	//核心思路:上一层出栈的时候带下一层入栈
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q,root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->data);
		QueuePop(&q);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	
}

3. 计算二叉树节点个数

        法一:递归遍历,注意用于计数的变量size,应当传入size的地址psize,才能在多次递归调用函数中正确计数。

//遍历思想
void TreeSize1(BTNode* root, int* psize)
{
	if (root == NULL)
	{
		return;
	}
	else
	{
		(*psize)++;
	}
	TreeSize1(root->left, psize);
	TreeSize1(root->right, psize);
}

        法二:分治思想,二叉树节点的个数,等于其左子树的节点数 + 右子树的节点数 + 1(根节点)

//分治思想
int TreeSize2(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	else
	{
		return TreeSize2(root->left) + TreeSize2(root->right) + 1; 
	}
}

4. 计算二叉树叶子节点个数

分治思想:叶子节点数 = 左子树的叶子节点数 + 右子树叶子节点数 (+ 1)(需判断当前节点是否为叶子节点)

int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	else
	{
		if (root->left == NULL && root->right == NULL)
		{
			return TreeLeafSize(root->left) + TreeLeafSize(root->right) + 1;
		}
		else
		{
			return TreeLeafSize(root->left) + TreeLeafSize(root->right);
		}
	}
}

5.二叉树的销毁 

后序遍历销毁,先遍历到叶子节点,再进行销毁节点

void DestoryTree(BTNode* root)
{
	//后序销毁
	if (root == NULL)
	{
		return;
	}
	else
	{
		DestoryTree(root->left);
		DestoryTree(root->right);
		free(root);
		root = NULL;
	}
}

Test.c

因为二叉树没有之前数据结构的增删查改等基本操作,我们可以通过动态内存分配malloc出我们想要的节点,从而组建成我们想要测试的二叉树

#define _CRT_SECURE_NO_WARNINGS
#include"Tree.h"
#include"Queue.h"

void TestTree()
{
	BTNode* A = (BTNode*)malloc(sizeof(BTNode));
	A->data = 'A';
	A->left = NULL;
	A->right = NULL;

	BTNode* B = (BTNode*)malloc(sizeof(BTNode));
	B->data = 'B';
	B->left = NULL;
	B->right = NULL;

	BTNode* C = (BTNode*)malloc(sizeof(BTNode));
	C->data = 'C';
	C->left = NULL;
	C->right = NULL;

	BTNode* D = (BTNode*)malloc(sizeof(BTNode));
	D->data = 'D';
	D->left = NULL;
	D->right = NULL;

	BTNode* E = (BTNode*)malloc(sizeof(BTNode));
	E->data = 'E';
	E->left = NULL;
	E->right = NULL;

	A->left = B;
	A->right = C;
	B->left = D;
	B->right = E;

	PrevOrder(A);
	printf("\n");
	InOrder(A);
	printf("\n");
	PostOrder(A);
	printf("\n");

	int Asize = 0;
	TreeSize1(A, &Asize);
	printf("%d\n", Asize);
	printf("%d\n", TreeSize2(A));

	int Bsize = 0;
	TreeSize1(B, &Bsize);
	printf("%d\n", Bsize);
	printf("%d\n", TreeSize2(B));

	printf("The number of leaf is: %d\n", TreeLeafSize(A));

	LevelOrder(A);
}
int main()
{
	TestTree();
	
	return 0;
}

树与二叉树oj练习题

二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

 

示例 1:


输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:

输入:root = []
输出:[]
示例 3:

输入:root = [1]
输出:[1]
示例 4:


输入:root = [1,2]
输出:[1,2]
示例 5:


输入:root = [1,null,2]
输出:[1,2]
 

提示:

树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/binary-tree-preorder-traversal

基本思路和之前的代码相似,与之不同的是需要将节点的值放入一个数组中,并且返回数组的地址和长度(输出参数)。

1.由于我们无法得知二叉树节点的个数,所以开辟多大的空间成为了我们面对的第一个问题。我们需要写一个TreeSize函数,得到我们想要的节点个数(即输出参数)

2.由于preorderTraversal的函数开辟了一个内存空间存放数组,所以如果递归该函数,会导致多次开辟空间,所以我们在这再定义了一个子函数_preorderTraversal。通过递归该子函数以达成前序遍历的效果。

int TreeSize(struct TreeNode* root)
{
    if (!root)
    {
        return 0;
    }
    else
    {
        return TreeSize(root->left) + TreeSize(root->right) + 1;
    }
}
int* _preorderTraversal(struct TreeNode* root, int* a, int* pi)
{
    if (root == NULL)
    {
        return;
    }
    else
    {
        a[(*pi)] = root->val;
        (*pi)++;
        _preorderTraversal(root->left, a, pi);
        _preorderTraversal(root->right, a, pi);
    }
}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    int size = TreeSize(root);
    int* a = (int*)malloc(size * sizeof(int*));
    int i = 0;
    _preorderTraversal(root, a, &i);
    *returnSize = size;
    return a;

}

二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree

基本思路分治思想

int maxDepth(struct TreeNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    else
    {
        //保存maxDepth(root->left)的值,减少计算量
        int left = maxDepth(root->left);
        int right = maxDepth(root->right);
        return left > right ? left + 1 : right + 1;
    }
}

平衡二叉树

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:


输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:


输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:

输入:root = []
输出:true
 

提示:

树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/balanced-binary-tree

 基本思路:分治思想

 注意:需要调用前一题最大深度的函数

 int maxDepth(struct TreeNode* root)
{
    if (root == NULL)
    {
        return 0;
    }
    else
    {
        int left = maxDepth(root->left);
        int right = maxDepth(root->right);
        return left > right ? left + 1 : right + 1;
    }
}

bool isBalanced(struct TreeNode* root)
{
    if (root == NULL)
    {
        return true;
    }
    else
    {
        int left = maxDepth(root->left);
        int right = maxDepth(root->right);
        return abs(left - right) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }

}

二叉树的遍历

描述

编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

输入描述:

输入包括1行字符串,长度不超过100。

输出描述:

可能有多组测试数据,对于每组数据, 输出将输入字符串建立二叉树后中序遍历的序列,每个字符后面都有一个空格。 每个输出结果占一行。

示例1

输入:

abc##de#g##f###

复制输出:

c b e g d f a 

来源:牛客网
链接:二叉树遍历_牛客题霸_牛客网

这一题的关键在于,用输入的字符串构建一棵二叉树。

先序构建二叉树:遍历字符串,如果是 '#' ,则说明是空树;如果不是 '#',则二叉树可以分解为,根节点,左子树,右子树三个部分。

#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode
{
    struct TreeNode* left;
    struct TreeNode* right;
    char val;
}TNode;
TNode* CreateTree(char *a,int *pi)
{
    if (a[*pi] == '#')
    {
        (*pi)++;
        return NULL;
    }
    else
    {
        TNode* root = (TNode*)malloc(sizeof(TNode*));
        if (root == NULL)
        {
            printf("malloc fail\n");
            exit(-1);
        }
        root->val = a[*pi];
        (*pi)++;
        root->left = CreateTree(a, pi);
        root->right = CreateTree(a, pi);
        return root;
    }
}

void InOrder(TNode *root)
{
    if (root == NULL)
    {
        return;
    }
    else
    {
        InOrder(root->left);
        printf("%c ", root->val);
        InOrder(root->right);
    }
}
int main()
{
    char str[100] = {};
    scanf("%s",str);
    int i = 0;
    TNode* root = CreateTree(str,&i);
    InOrder(root);
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值