【数据结构 树:二叉树分类、红黑树讲解】

在这里插入图片描述

 

常用术语

节点:每一个小圈圈  

父节点 : 下面有节点的节点

子节点 : 有父节点的节点

叶子节点  :没有子节点的节点

节点的权  :节点中的值

路径  :从根节点(root节点)到该节点的路线

层: A为一层  BCD为一层  上述有4层

高度:最大层数

森林 :多个树构成森林

度   :

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


二叉树

1、每个节点最多只有两个节点,明确分为左右子节点。


满二叉树

二叉树的所有叶子节点都在最后一层,所有节点数满足2^n-1,n为层数。


完全二叉树

二叉树的所有叶子节点都在倒数第一层或第二层,而且最后一层在左边连续,倒数第二层在右边连续

 平衡二叉树

是一棵空树或它的任意节点的左右两个子树的高度差的绝对值不超过1

在这里插入图片描述

二叉排序树 又称二叉查找树、二叉搜索树

🏐2.构建二叉排序树
假设我们有以下数据,我们按从左到右的顺序来构建二叉排序树:

 

首先,将8作为根节点
插入3,由于3小于8,作为8的左子树
插入10,由于10大于8,作为8的右子树
插入1,由于1小于8,进入左子树3,1又小于3,则1为3的左子树
插入6,由于6小于8,进入左子树3,6又大于3,则6为3的右子树
插入14,由于14大于8,进入右子树10,14又大于10,则14为10的右子树
插入4,由于4小于8,进入左子树3,4又大于3,进入右子树6,4还小于6,则4为6的左子树
插入7,由于7小于8,进入左子树3,7又大于3,进入右子树6,7还大于于6,则7为6的右子树
插入13,由于13大于8,进入右子树10,又13大于10,进入右子树14,13小于14,则13为14的左子树
经过以上的逻辑,这棵二叉排序树构建完成。

 

我们可以看出:

只要左子树为空,就把小于父节点的数插入作为左子树
只要右子树为空,就把大于父节点的数插入作为右子树
如果不为空,就一直往下去搜索,直到找到合适的插入位置
 

我们对这棵二叉树进行中序遍历,看看会发生什么?你自己试一试!

没错,这棵二叉树中序遍历结果为:

🏀3.二叉排序树的查找操作
它既然也叫二叉查找树,那想必会非常方便我们查找吧!它的操作并不是把中序遍历的结果存入数组,然后在有序数组里查找,而是直接在树上查找。其操作与二分查找非常相似,我们来查找7试一试?(这里要说明以下:在正常的数据结构中,由于数据量很大,所以我们也不知道我们想要的元素在不在里面;同时也不知道每个元素具体是多少,只知道他们的大小关系。我们是在此基础上进行查找)

首先,访问根节点8
根据性质,7比8小,所以如果7存在,那应该在8的左子树那边,访问8的左子树
访问到了3,根据第2步的思想,访问3的右子树
访问到了6,继续访问6的右子树
访问到了7,刚好找到啦!

🥎4.二叉排序树的删除

那么删除就稍微比查找与插入复杂一点,因为需要分类讨论了。

1.被删除结点为叶子结点

直接从二叉排序中删除即可,不会影响到其他结点。例如删去7:

2.被删除结点D仅有一个孩子

如果只有左孩子,没有右孩子,那么只需要把要删除结点的左孩子连接到要删除结点的父亲结点,然后删除D结点;
如果只有右孩子,没有左孩子,那么只要将要删除结点D的右孩子连接到要删除结点D的父亲结点,然后删除D结点。
以D=14为例:它没有右孩子,只有左孩子。(先把10指向14的右指针移动,去指向13,然后再删除14)

再以D=10为例,它没有左孩子,只有右孩子。(先把8指向10的右指针移动,去指向14,然后再删除10)

3.被删除结点左右孩子都在

这种情况就要复杂很多了。但没有关系,依然会讲的很清楚。

我们先假设删除根节点8,看看会发生什么?

我们的目标依然是要保证删除结点8后,再次中序遍历它,仍不改变其升序的排列方式。 那么我们只有用7或者10来替换8原来的位置

我们先看7来顶替位置

 

 

此时7从叶子结点“升迁”到了根节点(只是刚好要删除的结点为根节点,如果删除3,就替换3的位置)

我们再看10来顶替位置

 

 

这时候我们就应该会产生两个问题:

为什么是7或者10来替换8的位置?

显然,7与10是挨着8的,如果用其他元素替换则会打扰其顺序。

那7和10怎么在二叉排序树中找到呢?

显然,7在8左子树的“最右边”,10在8右子树的“最左边”。根据二叉排序树的插入方式,比8小的元素一定在左子树,而我们又要找到比8小的最大的数,这样才能保证他们俩在顺序上是挨着的,所以它又会在8的左子树的最右边。同理也可以找到10.

int delete_node(Node* node, int key)
{
	if (node == NULL)
	{
		return -1;
	}
	else
	{
		if (node->data == key)
		{
			//当我执行删除操作 需要先定位到删除结点的前一个结点(父节点)
			Node* tempNode = prev_node(root, node, key);
			Node* temp = NULL;
			
			//如果右子树为空,只需要重新连接结点(包含叶子结点),直接删除
			if (node->right == NULL)
			{
				temp = node;
				node = node->left;
				/*判断待删除结点是前一个结点的左边还是右边*/
				if (tempNode->left->data == temp->data)
				{
					Node* free_node = temp;
					tempNode->left = node;
					free(free_node);
					free_node = NULL;
				}
				else
				{
					Node* free_node = temp;
					tempNode->right = node;
					free(free_node);
					free_node = NULL;
				}
			}
			else if (node->left == NULL)
			{
				temp = node;
				node = node->right;
				if (tempNode->left->data == temp->data)
				{
					Node* free_node = temp;
					tempNode->left = node;
					free(free_node);
					free_node = NULL;
				}
				else
				{
					Node* free_node = temp;/
					tempNode->right = node;
					free(free_node);
					free_node = NULL;
				}
			}
			else//左右子树都不为空
			{
				temp = node;
				/*往左子树 找最大值*/
				Node* left_max = node;//找最大值的临时指针
				left_max = left_max->left;//先到左孩子结点
				while (left_max->right != NULL) 
				{
					temp = left_max;
					left_max = left_max->right;
				}
				node->data = left_max->data;
				if (temp != node)
				{
					temp->right = left_max->left;
					free(left_max);
					left_max = NULL;
				}
				else
				{
					temp->left = left_max->left;
					free(left_max);
					left_max = NULL;
				}
			}
			
		}
		else if(key < node->data)
		{
			delete_node(node->left, key);
		}
		else if (key > node->data)
		{
			delete_node(node->right, key);
		}
	}
}


————————————————
版权声明:本文为CSDN博主「~在下小吴」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_54186646/article/details/124412656


 


二叉树遍历

前序:父左右

中序:左父右

后序:左右父

分析:

 

建立二叉树、根据遍历方式

前:输出父节点  判断左节点 不为空向下递归,空了向右递归

中:同理

后:同理

代码实现  此实现将当前节点作为父节点使用

public class TreeDemo {
    public static void main(String[] args) {
        //手动创建  正常用递归创建
        Node node=new Node(1,"宋江");
        Node node1=new Node(2,"吴用");
        Node node2=new Node(3,"卢俊义");
        Node node3=new Node(4,"林冲");
        node.setLeft(node1);
        node.setRight(node2);
        node2.setRight(node3);
        BinaryTree binaryTree=new BinaryTree(node);
        binaryTree.preOrder();
        System.out.println("oooooooooooooooooooo");
        binaryTree.infixOrder();
        System.out.println("oooooooooooooooooooo");
        binaryTree.postOrder();
    }
}
//创建节点
class Node{
    private int id;
    private String Name;
    private Node left;
    private Node right;

    public Node(int id, String name) {
        this.id = id;
        Name = name;

    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", Name='" + Name + '\'' +
                '}';
    }
    //前序
    public void preOrder(){
        System.out.println(this);
        if (this.left!=null){
            this.left.preOrder();
        }
        if (this.right!=null){
            this.right.preOrder();
        }
    }
    //中序
    public void infixOrder(){

        if (this.left!=null){
            this.left.infixOrder();
        }
        System.out.println(this);
        if (this.right!=null){
            this.right.infixOrder();
        }
    }
    //后序
    public void postOrder(){

        if (this.left!=null){
            this.left.postOrder();
        }

        if (this.right!=null){
            this.right.postOrder();
        }
        System.out.println(this);
    }


}
//创建树
class BinaryTree{
    private Node root;

    public BinaryTree(Node root) {
        this.root = root;
    }

    /*此用来调用  节点类的方法用来递归操作*/

    //前序遍历:
    public void preOrder(){
        if (root!=null){
            this.root.preOrder();
        }else {
            System.out.println("为空");
        }
    }
    //中序
    public void infixOrder(){
        if (root!=null){
            this.root.infixOrder();
        }else {
            System.out.println("为空");
        }
    }

    //中序
    public void postOrder(){
        if (root!=null){
            this.root.postOrder();
        }else {
            System.out.println("为空");
        }
    }
}

查找指定节点

 Node类里面   没有写后序查找

//前序查找
    public Node preOrderFind(int id){
        //如果当前节点等于  返回
        if (this.id==id){
            return this;
        }
        //定义局部变量  返回值
        Node node=null;
        if (this.left!=null){
            //左不为空  递归查找
            node=this.left.preOrderFind(id);
        }
        //左边找到了
        if (node!=null){
            return node;
        }
        //左边没有找到
        if (this.right!=null){
            node=this.right.preOrderFind(id);
        }
        return node;
    }
    //中序查找
    public Node infixOrderFind(int id){
        //临时变量
        Node node=null;
        //如果当前节点等于  返回
        if (this.left!=null){
            node = this.left.infixOrderFind(id);
        }
        if (node !=null){
            return node;
        }
        if (this.id==id){
            return this;
        }

        //左边没有找到
        if (this.right!=null){
            node=this.right.infixOrderFind(id);
        }
        return node;
    }

红黑树: (1条消息) 漫画:什么是红黑树?(整合版)_程序员小灰的博客-CSDN博客



插入数据的方式  使第一个节点为黑色  后面所有插入的节点为红色。。旋转变色得到符合红黑树的定义即可得到完整的红黑树。(1条消息) 红黑树的插入过程(图解)_灵雨lxf的博客-CSDN博客_红黑树插入

 上面链接的评论里里面有动态展示。。

链表转红黑树就是根据这个来的。。 

 Red/Black Tree Visualization

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值