首页 > 其他分享 >树 Tree

树 Tree

时间:2024-03-26 15:33:50浏览次数:23  
标签:node 结点 right data Tree root 节点

2024.3.21
芝士wa
参考视频: 数据结构-树
“种一棵树,最好的时间是十年前,其次是现在”

树的定义

树是由 n (n ≥ 0) 个结点组成的有限集合。如果 n = 0,称为空树;如果 n > 0,则有一个特定的称之为根(root)的结点,它只有直接后继,但没有直接前驱;除根以外的其他结点划分为 m (m ≥ 0) 个 互不相交的有限集合T0, T1, …, Tm-1,每个集合又是一棵树,并且称之为根的子树。

上述定义过于抽象。

树是一种非线性的数据结构,可用来存储层次机构的数据。

树的结构

树的结构

一些繁杂的概念:

  • 考虑结点K。根A到结点K的唯一路径上的任意结点,称为结点K的祖先。如结点B是结点K的祖先,而结点K是结点B的子孙。路径上最接近结点K的结点E称为K的双亲,而K为结点E的孩子。根A是树中唯一没有双亲的结点。有相同双亲的结点称为兄弟,如结点K和结点L有相同的双亲E,即K和L为兄弟。

  • 树中一个结点的孩子个数称为该结点的度,树中结点的最大度数称为树的度。如结点B的度为2,结点D的度为3,树的度为3。

  • 度大于0的结点称为分支结点(又称非终端结点);度为0(没有子女结点)的结点称为叶子结点(又称终端结点)。在分支结点中,每个结点的分支数就是该结点的度。

  • 结点的深度、高度和层次。

  • 结点的层次从树根开始定义,根结点为第1层,它的子结点为第2层,以此类推。双亲在同一层的结点互为堂兄弟,图中结点G与E,F,H,I,J互为堂兄弟。

  • 结点的深度是从根结点开始自顶向下逐层累加的。

  • 结点的高度是从叶结点开始自底向上逐层累加的。

  • 树的高度(或深度)是树中结点的最大层数。图中树的高度为4。

  • 有序树和无序树。树中结点的各子树从左到右是有次序的,不能互换,称该树为有序树,否则称为无序树。假设图为有序树,若将子结点位置互换,则变成一棵不同的树。

  • 路径和路径长度。树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的,而路径长度是路径上所经过的边的个数。

  • 注意:由于树中的分支是有向的,即从双亲指向孩子,所以树中的路径是从上向下的,同一双亲的两个孩子之间不存在路径。

  • 森林。森林是m (m≥0)棵互不相交的树的集合。森林的概念与树的概念十分相近,因为只要把树的根结点删去就成了森林。反之,只要给m棵独立的树加上一个结点,并把这m棵树作为该结点的子树,则森林就变成了树。


二叉树 Binary Search Tree

定义

每一个节点最多有两个子节点的树。

  • 只有一个根,也是二叉树。
  • 如果一个树除了最底层之外全被填充,并且所有节点都向左对齐,我们称之为完全二叉树。
  • 二叉树的实现有两种方式,一是动态创建节点,二是采用数组的方式

二叉搜索树

  • 二叉搜索树是一种二叉树,对其中的每一个节点,它的所有左子树上的节点值都比该节点小。

  • 二叉搜索树基于二分查找的原理,在平均情况下,查找、插入或者删除的时间复杂度为O(logn)。

  • 二叉搜索树的平衡十分重要,应该尽可能保持平衡

  • 两种遍历方式:深度优先,广度优先

    广度优先:按层访问,先访问同层的所有元素,再跳转到下一层

    深度优先:有三种方式

    • 前序遍历:<root><left><right>
    • 中序遍历:<left><root><right>
    • 后序遍历:<left><right><root>

    例如,对于下面这样一棵二叉树
    二叉树的不同遍历方式

    前序遍历:F D B A C E J G I H K

    中序遍历:A B C D E F G H I J K

    后序遍历:A C B E D H I G K J F

二叉搜索树的层级访问

按层访问,可以使用队列,首先将root节点放入队列中,然后在出队之前,将root节点的左右子节点存入队列中,出队,此时队列中有root-left和root-right,在root-left出队之前,把root-left的左右子节点存入队列中,依次操作,即可实现按层遍历。


二叉搜索树的前中后序访问

采用递归的思想。代码如下

    void BST::PreOrder(BstNode* root)
    {
    	if (root == NULL) {
    		return;
    	}
    	cout << root->data<<"\t";
    	PreOrder(root->left);
    	PreOrder(root->right);
    }


    void BST::InOrder(BstNode* root)
    {
    	if (root == NULL) {
    		return;
    	}

    	InOrder(root->left);
    	cout << root->data << "\t";
    	InOrder(root->right);
    }

    void BST::PostOrder(BstNode* root)
    {
    	if (root == NULL) {
    		return;
    	}

    	PostOrder(root->left);
    	PostOrder(root->right);
    	cout << root->data << "\t";
    }

判断一个树是否为二叉搜索树

所有方法均采用递归的思想。

方法一:
对每个节点,检测其值是否大于左子树的最大值,是否小于右子树的最小值。
该方法十分繁琐,重复查找的操作大大增加了时间复杂度,不建议使用。
代码如下:

int maxValue(BstNode* root)
{
    int max = root->data;
    if(root->left != NULL)
    {
        int maxLeft = maxValue(root->left);
        max = max > maxLeft ? max : maxLeft;
    }
    if(root->right != NULL)
    {
        int maxRight = maxValue(root->right);
        max = max > maxRight ? max : maxRight;
    }
    return max;
}

int minValue(BstNode* root)
{
    int min = root->data;
    if(root->left != NULL)
    {
        int minLeft = maxValue(root->left);
        min = min < minLeft ? min : minLeft;
    }
    if(root->right != NULL)
    {
        int minRight = maxValue(root->right);
        min = min < minRight ? min : minRight;
    }
    return min;
}

bool isBST(BstNode* root)
{
    if(root == NULL)
        return true;
    if(root->left != NULL && maxValue(root->left) > root->data)
        return false;
    if(root->right != NULL && minValue(root->right) < root->data)
        return false;
    return isBST(root->left) && isBST(root->right);
}

方法二:中序遍历
二叉搜索树的中序遍历是一个递增序列,所以我们只需要把这个中序遍历保存下来,然后判断这是个递增序列即可。
代码如下:

void BST::InOrder(BstNode* root, vector<int>& v)
{
	if (root == NULL) {
		return;
	}

	InOrder(root->left,v);
	v.push_back(root->data);
	InOrder(root->right,v);
}

bool BST::isBST(BstNode* root)
{
    vector<int> inorder;
	InOrder(root,inorder);
	for (int i = 1; i < inorder.size(); ++i)
		if (inorder[i - 1] >= inorder[i])
			return false;
	return true;
}

使用静态变量可以简化上述代码,省略栈的空间

bool isBST(BstNode* root)
{
    static BstNode* prev;
    if(root != NULL)
    {
        if(!isBST(root->left))
            return false;
        if(prev != NULL && root->data < prev->data)
            return false;
        prev = root;
        if(!isBST(root->right))
            return false;
    }
    return true;
}

删除特定节点

采用递归的思想
删除节点一共有三种情况:

  • 该节点没有子节点:直接对其进行删除即可,并将其指针归零
  • 该节点有一个子节点:保存其子节点,然后修改祖先的指向,最后删除该节点
  • 该节点有两个子节点:找到其中序后继,将其copy到该节点的位置,然后删除中序后继所在的节点,将问题转换成第二张情况

代码如下:

   void BST::deleteNode(BstNode*& node, int data)
  {
  	if (node == nullptr) {
  		return; // 如果树为空,直接返回
  	}

  	if (data < node->data) {
  		deleteNode(node->left, data); // 在左子树中寻找要删除的节点
  	}
  	else if (data > node->data) {
  		deleteNode(node->right, data); // 在右子树中寻找要删除的节点
  	}
  	else {
  		// 找到了要删除的节点
  		if (node->left == nullptr && node->right == nullptr) {
  			delete node;
  			node = nullptr; // 删除叶子节点
  		}
  		else if (node->left == nullptr) {
  			BstNode* temp = node;
  			node = node->right;
  			delete temp; // 删除只有右子节点的节点
  		}
  		else if (node->right == nullptr) {
  			BstNode* temp = node;
  			node = node->left;
  			delete temp; // 删除只有左子节点的节点
  		}
  		else {
  			// 删除有两个子节点的节点
  			BstNode* successor = node->right;
  			//找到右子树中最小的值
  			while (successor->left != nullptr) {
  				successor = successor->left;
  			}
  			node->data = successor->data; // 将后继节点的值复制到要删除的节点
  			deleteNode(node->right, successor->data); // 递归删除后继节点
  		}
  	}
  }

中序后继

要找到一个节点的中序后继,可以执行中序遍历,将所有节点打印出来,进而找到中序后继,时间复杂度为O(n),因为要访问所有节点。

下面采用更简单的方式查找,即利用二叉搜索树查找元素的时间复杂度为O(h)的特点,h为高度

BstNode* findSuccessor(BstNode* root, BstNode* node)
{
    if (node->right != nullptr) {
        // 如果节点有右子树,后继节点为右子树中的最小节点
        return findMin(node->right);
    }

    BstNode* successor = nullptr;
    while (root != nullptr) {
        if (node->data < root->data) {
            successor = root;
            root = root->left;
        } else if (node->data > root->data) {
            root = root->right;
        } else {
            break;
        }
    }

    return successor;
}

树的难度较高,需要加深理解。

标签:node,结点,right,data,Tree,root,节点
From: https://www.cnblogs.com/cheese-wa/p/18088071

相关文章

  • 我什么时候应该使用TreeMap 而不是 PriorityQueue?反之亦然?
    引子之前周赛(第390场周赛记录-快手)时遇到一题(题干描述见下图,实现代码见周赛记录),需要保持容器元素的动态有序(即随着插入删除操作后列表始终是有序的)。尝试过很多数据结构或方案,如列表存储然后手动调用Arrays.sort()进行排序、使用优先队列实现大/小根堆的方式,但无一例外全部超时......
  • TreeMap从添加第二个元素开始,需要进行排序,原始类继承Comparable<Student>接口实现comp
    重写compareTo方法,关于o的理解@OverridepublicintcompareTo(Studento){//关于o,是红黑树中从第二个开始进入的元素,需//要和已存在的元素比较,该o是在第二个add//调用时,传入这里的Student对象。//根据题设,先用年龄排序in......
  • LeetCode 834. Sum of Distances in Tree
    原题链接在这里:https://leetcode.com/problems/sum-of-distances-in-tree/description/题目:Thereisanundirectedconnectedtreewith n nodeslabeledfrom 0 to n-1 and n-1 edges.Youaregiventheinteger n andthearray edges where edges[i]=[a......
  • Tree Cutting
    这道题目的代码的写法非常新,可以学习首先我们从\(x=1\)开始想起,此时显然一条边都不用切然后是\(x=2\),我们发现所有叶子节点都不能分离开来了,我们把所有叶子节点与其父亲节点弄成一个连通块,显然这里切掉是最优的,在考虑剩下的树,仍然对叶子节点实施这个操作,直到最后没有剩下的树为......
  • CF1923E 一个无需 DSU On Tree 的解法(?
    在地铁上口胡了一下。不知道对不对。考虑记录每一个点\(i\)离他最远的一个祖先使得祖先到\(i\)的路径上没有\(a_i\)。设他为\(\text{lst}_i\)。然后如果两个\(u,v\)的\(\text{lst}\)相等,那么这条路径就是好的。每种颜色枚举即可。八成假了(?),欢迎Hack。PS:全对了,确实能......
  • tree --help 最新版 v2.1.1
    tree--helpusage:tree[-acdfghilnpqrstuvxACDFJQNSUX][-Llevel[-R]][-H baseHREF]    [-Ttitle][-ofilename][-Ppattern][-Ipattern][--gitignore]    [--gitfile[=]file][--matchdirs][--metafirst][--ignore-case]    [--nolinks]......
  • Kinetic Tournament Tree
    考虑这样一个问题:\(n\)个一次函数\(k_ix_i+b_i\),每个一次函数初始有\(x_i=0\);区间对\(x_i\)加正数\(x\),区间查询\(\max\limits_{i=l}^rk_ix_i+b_i\)。考虑每个点维护当\(x_i=0\)时值最大的函数,然后额外维护一个阈值\(t\),表示当\(x\)增大到\(t\)时这个......
  • C. Anji's Binary Tree
    网站:https://codeforces.com/problemset/problem/1900/C这里比较容易搞混的就是各个节点的关系,不要用2*n来表示左孩子!!以及记得添加快读快写ios::sync_with_stdio(false);cin.tie(nullptr);加在intmain()里即可这里还有一个priority_queue的小技巧:结构体内部定义运算符,好像和......
  • npm 错误,ERESOLVE unable to resolve dependency tree 解决方案
    参考:https://blog.csdn.net/qq_42055933/article/details/132098617 背景:当在使用npminstall时遇到“ERESOLVEunabletoresolvedependencytree”错误时,这通常是由于项目的依赖关系发生了冲突或不兼容问题。 方案一:在命令中增加 --legacy-peer-dep 选项或者--force......
  • vue2/3 - element组件库el-tree树形控件实现一键全选/一键反选取消/全部收起/全部折叠
    效果图在vue2、vue3|element饿了么组件库中,详细使用el-tree树状组件完成功能按钮组,支持全部选中节点、反选取消节点、对所有树节点进行折叠收起、是否上下级联动等等!提供详细示例代码教程,一键复制开箱即用~~示例代码请看下方代码及技术点介绍。<template><div......