数据结构(树)

数中的每一个元素称之为节点或结点,因为在英文中叫Node,翻译不同,所以俩个叫法都行

专业名词:
度:每一个节点的子节点数量
树高:树的总层数
根节点:最顶层的节点
左子节点:左下方的节点
右子节点:右下方的节点
根节点的左子树:其左半分支(别的节点也有子树)
根节点的右子树:其右半分支(别的节点也有子树)

每一个节点会存储:父节点地址、值、左子节点地址值、右子节点地址值
如果该节点没有父节点或者子节点,则其对应的地址值记为null


二叉树

二叉树:任意节点的度<=2称之为二叉树

普通的二叉树:

        没有规律的存储数据,没有什么意义

二叉查找树

特点:
1.每一个节点上最多有两个子节点
2.任意节点左子树上的值都小于当前节点
3.任意节点右子树上的值都大于当前节点

添加


添加规则:
1.小的存左边
2.大的存右边
3.一样的不存

例如以下这组数据:7、10、4、5

1.首先存放7到根节点

2.判断10与7的关系,10 > 7所以10存放在7的右子节点

3.判断4与7的关系,4 < 7所以4存放在7的左子节点

4.判断5与7的关系,5 < 7所以5存放在7的左子节点,因为5的左子节点已经有4了,所以再次比较5与4的关系,5 > 4所以5存放在4的右子节点

查找

查找:

如果查找5:

        1.让5与7进行比较,5 < 7所以查找7的左子树

        2.让5与4比较,5 > 4所以查找4的右子树

        3.此时找到5

如果查找12:

        1.让12与7比较, 12 > 7所以查找7的右子树

        2.让12与10比较,12 > 10所以查找10的右子树

        3.让12与11比较,12 > 11所以查找11的右子树

        4.此时找到12

如果查找30(二叉树中不存在的值):

        1.让30与7比较, 30 > 7所以查找7的右子树

        2.让30与10比较,30 > 10所以查找10的右子树

        3.让30与11比较,30 > 11所以查找11的右子树

        4.让30与12比较,30 > 12所以查找12的右子树

        5.此时没有节点了,首先判定该二叉树中没有30这个值

二叉树的遍历方式

所有二叉树都能按照这个方式遍历

1.前序遍历

从根节点开始,然后按照当前节点,左子节点,右子节点的顺序遍历

例:

遍历顺序:20-18-16(此时没有左子节点)-19(此时没有右子节点)23-22-24

2.中序遍历

从最左边的子节点开始,然后按照左子节点,当前节点,右子节点(左-中-右)的顺序遍历

例:

1.20-18-16找到最左边的子节点16

2.开始遍历(左-中-右方式遍历):

16-18-19-20-22-23-24

特点:遍历出来的数据是从小到大的

3.后序遍历

从最左边的子节点开始,然后按照左子节点,右子节点,当前节点(左-右-中)的顺序遍历

例:

1.20-18-16找到最左边的子节点

2.开始遍历:

16-19-18-22-24-23-20

4.层序遍历

从根节点开始一层一层遍历

开始遍历:20-18-23-16-19-22-24

弊端

 例如此时要存放7、10、11、12、13

此时根节点的左子树和右子树差的太多了,这样查询效率太低,此时就需要平衡二叉树

平衡二叉树

在二叉查找树的基础上保持二叉树的平衡

规则:任意节点左右子树高度差不不超过1

判断下方二叉树是否为平衡二叉树:

1.判断7的左子树高度为3,右子树高度为4,相差<=1

2.判断4的左子树高度为2,右子树高度为2,相差<=1

3.......

4.判断10的左子树高度为0,右子树高度为3,相差=3>1

所以答案:否

下面示范俩个是平衡二叉树的例子:

平衡二叉树是怎么让它们左右子树高度差不超过1?

通过旋转机制

旋转机制

规则1:左旋

规则2:右旋

触发时机:当添加一个节点时,该树不再是一个平衡二叉树

左旋

例:

此时下面这个二叉树它是一个平衡二叉树

如果添加一个12节点,那么此时就破坏了平衡,要开始旋转

旋转首先:

1.确定支点:从添加的节点开始,不断的往父节点去找,找到第一个不平衡的点,以它为支点

2.再以支点(此时为10)为基准,判断左旋还是右旋(此时10的右子树比左子树多,所以判断为左旋操作)

步骤:

1.以不平衡的点作为支点

2.把支点左旋降级,变成左子节点

3.晋升原来的右子节点

此时再来看一种相对复杂的情况,建议在上上张图的基础上先增加9节点再增加12节点:

1.确定支点:从12开始找,找到支点为7

步骤:

1.以不平衡的点为支点

2.将根节点的右侧往左拉

3.原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级了的根节点当作右子节点

右旋

例:

参考左旋,结果:

需要旋转的四种情况

1.左左

2.左右

3.右右

4.右左

左左

当根节点左子树的左子树有节点插入,导致二叉树不平衡

理解,例:

此时根节点的左子树就是4、2、5这三个节点,这个左子树的左子树就是2这个节点

在2这个节点插入节点(图1-2)导致二叉树不平衡(图1-2,此时节点7左右子树的高度差=2>1)

1-1

1-2

旋转:

右旋:

此时恢复平衡

结论:左左导致的不平衡,一次右旋就能解决

左右

当根节点的左子树的右子树有节点插入,导致的不平衡

理解:例: 

此时根节点的左子树就是4、2、5三个节点组成的子树,这个子树的右子树为5

在5这个节点插入节点(图2-2)导致二叉树不平衡(图2-2,此时7的左右子树层数=2>1)

2-1

2-2

错误示范

旋转:

右旋:此时依旧不平衡(4的左右子树层数=2>1)

此时高度差依旧为2,表面左右不能通过一次右旋达成平衡二叉树

正确示范:

旋转:

局部左旋:先将4、2、5、6这块子树左旋,将左右的情况变成左左

右旋:

此时恢复平衡

结论:左右导致的不平衡,先局部左旋,再整体右旋

右右

当根节点右子树的右子树有节点插入,导致二叉树不平衡

理解:例:

此时根节点的右子树为10、9、11,该子树的右子树为11,此时添加节点12(图3-2),导致二叉树不平衡(7的左右子树层数差=2>1)

3-1

3-2

旋转:

左旋:

此时恢复平衡

结论:右右导致的不平衡,一次左旋就能解决

右左

当根节点的右子树的左子树有节点插入,导致二叉树不平衡

理解,例:

此时根节点的右子树为10、9、11,该子树的左子树为9

此时添加节点8(图4-1)导致二叉树不平衡(4-2,根节点左右子树层数差=2>1)

4-1

4-2

错误示范:

旋转:

左旋:

此时高度差依旧为2,表面右左不能通过一次左旋达成平衡二叉树

正确示范:

旋转:

局部左旋:将右左变成右右

整体左旋:

此时恢复平衡

结论:右左导致的不平衡,先局部左旋,再整体左旋

弊端

由于添加节点需要旋转,会造成添加节点的时间浪费

红黑树

红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构

1972年出现,当时被称之为平衡二叉B树。后来,1978年被修改为如今的“红黑树”

它是一种特殊的二叉查找树,红黑树的每一个节点上都有存储位表示节点的颜色

每一个节点可以是红或者黑;红黑树不是高度平衡的,它的平衡是通过“红黑规则”进行实现的

红黑树每个节点中存储:父节点地址值、值、左子节点地址值、右子节点地址值、颜色(新增)

平衡二叉树和红黑树对比:

红黑树:                                平衡二叉树:

是一个二叉查找树                  高度平衡                  

但是不是高度平衡的              当左右子树高度差超过1时,通过旋转保持平衡

条件:特有的红黑规则

红黑规则

1.每一个节点或是红色的,或是黑色的

2.根节点必须是黑色

3.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

4.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现俩个红色节点相连的情况)

5.对每一个节点,从该节点到其所有后代(该节点的所有子树节点)叶节点(Nil节点)的简单路径(例如下图13-8-11-Nil就是一条简单路径,核心:不能往回走)上,均包含相同数目的黑色节点

例:下图就是一个红黑树

存储:

此时拿6这个节点举例,其内存中存储了父节点地址值,6,左和右子节点地址值为空,颜色为红

添加规则

默认颜色:添加节点默认是红色的(效率高)

为什么效率高?

例如此时要添加20、18、23三个节点,且默认为黑色的话

首先添加20:满足红黑规则

再添加18:此时就已经违背了红黑规则第五条

需要将18改成红色,才能满足红黑规则:

再添加23:此时又违背了红黑规则第五条

需要将23变成红色,才能满足红黑规则:

结论:如果默认添加元素是黑色,那么添加三个节点需要调整两次

现在演示这三个节点默认为红色的情况:

首先添加23:此时违背红黑规则第二条

需要将20变成黑色,才能满足红黑规则:

再添加18节点:满足红黑规则

再添加23节点:满足红黑规则

结论:添加三个节点,只需要调整一次,所以默认颜色为红色效率更高

除了默认颜色外,红黑树还有很多添加节点的规则

例:

此时需要添加20、18、23、22、17、25、19、15、14/16

1.首先添加20:此时违反红黑规则第二条

解决方案:将20改为黑色,则满足红黑规则

2.添加18:满足红黑规则(非根-父黑色-不需要操作)

3.添加23:满足红黑规则(非根-父黑色-不需要操作)

4.添加22:违背红黑规则第四条

解决方案:非根-父红色-叔叔红色(此时父为23-叔叔为18)

1.将父变成黑、叔叔变成黑。

2.将祖父设为红色

3.如果祖父为根,再将祖父设为黑(true)

   如果祖父非根,将祖父设置为当前节点再进行其他判断(false)

5.添加17:满足红黑规则(非根-父黑色-不需要操作)

6.添加24和19时,同上述5(非根-父黑色-不需要操作)

7.添加15节点:此时违反红黑规则第四条

解决方案:非根-父红色-叔叔红色(此时父为17,叔叔为19)

1.将父变成黑、叔叔变成黑。

2.将祖父设为红色

3.如果祖父为根,再将祖父设为黑(false)

   如果祖父非根,将祖父设置为当前节点再进行其他判断(true)

以18为当前节点判断(非根-父黑色-不需要操作)

8.添加14节点:违背红黑规则第四条

解决方案:非根-父红色-叔叔黑色-当前节点为父节点的左子节点(此时父为15,叔叔为Nil)

1.将父设为黑色

2.将祖父设为红色

3.以祖父为支点进行右旋

9.假设没有添加8步骤中的14,而是添加16:此时违背红黑规则第四条

解决方案:非根-父红色-叔叔黑色-当前节点是父节点的右子节点(此时父为15,叔叔为Nil)

1.把父作为当前节点并左旋,再进行判断

此时15为当前节点(非根-父红色-叔叔红色-当前节点为父节点的左子节点)

1.将父设为黑色

2.将祖父设为红色

3.以祖父为支点进行右旋

此时就彻底解决!!!

总结

红黑树增删改查的性能都很好

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python中的数据结构可以通过使用类和对象来实现。在Python中,我们可以使用节点类来表示的节点,并使用类来操作和管理的结构。 首先,我们可以创建一个节点类,其中包含节点的值和左右子节点的引用。节点的值可以是任意类型的数据。然后,我们可以创建一个类,其中包含根节点的引用和一些方法来操作。 下面是一个示例的Python代码来实现数据结构: ``` class TreeNode: def __init__(self, value): self.value = value self.left = None self.right = None class Tree: def __init__(self): self.root = None def insert(self, value): if self.root is None: self.root = TreeNode(value) else: self._insert_recursive(self.root, value) def _insert_recursive(self, node, value): if value < node.value: if node.left is None: node.left = TreeNode(value) else: self._insert_recursive(node.left, value) else: if node.right is None: node.right = TreeNode(value) else: self._insert_recursive(node.right, value) def search(self, value): return self._search_recursive(self.root, value) def _search_recursive(self, node, value): if node is None or node.value == value: return node if value < node.value: return self._search_recursive(node.left, value) else: return self._search_recursive(node.right, value) def inorder_traversal(self): nodes = [] self._inorder_traversal_recursive(self.root, nodes) return nodes def _inorder_traversal_recursive(self, node, nodes): if node is not None: self._inorder_traversal_recursive(node.left, nodes) nodes.append(node.value) self._inorder_traversal_recursive(node.right, nodes) ``` 在上面的代码中,我们创建了一个`TreeNode`类来表示的节点,每个节点有一个值和左右子节点的引用。然后,我们创建了一个`Tree`类来操作和管理的结构。`Tree`类包括了插入节点、搜索节点和中序遍历的方法。 使用这些类,我们可以轻松地创建一个二叉搜索,并对其进行插入、搜索和中序遍历等操作。 例如,我们可以创建一个对象并插入一些节点: ``` tree = Tree() tree.insert(5) tree.insert(3) tree.insert(7) tree.insert(2) tree.insert(4) ``` 然后,我们可以搜索特定的值: ``` node = tree.search(3) if node is not None: print("Found:", node.value) else: print("Not found") ``` 最后,我们可以使用中序遍历方法打印的节点值: ``` nodes = tree.inorder_traversal() print("Inorder traversal:", nodes) ``` 这是一个简单的例子来演示如何在Python中实现数据结构。通过使用节点类和类,我们可以方便地创建和操作的结构。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值