5 数据结构基础
数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。比如:列表、集合与字典等都是一种数据结构。
5.1数据结构的分类
数据结构按照其逻辑结构可分为线性结构、树结构、图结构。
- 线性结构:数据结构中的元素存在一对一的相互关系。
- 树结构:数据结构中的元素存在一对多的相互关系。
- 图结构:数据结构中的元素存在多对多的相互关系。
5.2 栈
栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。
1.栈的特点:后进先出LIFO(last-in,first-out)
2.栈的概念:栈顶、栈底
3.栈的基本操作:
- 进栈(压栈):push
- 出栈:pop
- 取栈顶:gettop
class Stack:
def __init__(self):
self.stack = []
def push(self,element):
self.stack.append(element)
def pop(self):
return self.stack.pop()
def gettop(self):
if len(self.stack) > 0:
return self.stack[-1]
else:
return None
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())
4.栈的实现:使用一般的列表结构即可实现栈。
- 进栈:li.append
- 出栈:li.pop
- 取栈顶:li[-1]
5.3 栈的应用——括号匹配问题
括号匹配问题:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。
例如:
- ()()[]{} 匹配
- ([{()}]) 匹配
- []( 不匹配
- [(]) 不匹配
class Stack:
def __init__(self):
self.stack = []
def push(self,element):
self.stack.append(element)
def pop(self):
return self.stack.pop()
def gettop(self):
if len(self.stack) > 0:
return self.stack[-1]
else:
return None
def is_empty(self):
return len(self.stack) == 0
def brace_match(s):
match = {'}':'{',']':'[',')':'('}
stack = Stack()
for ch in s:
if ch in {'(','[','{'}:
stack.push(ch)
else: #ch in {'}',']',')'}
if stack.is_empty():
return False
elif stack.gettop() == match[ch]:
stack.pop()
else: # stack.gettop() != match[ch]
return False
if stack.is_empty():
return True
#else:
# return False
print(brace_match('[{()}(){()}[]({}){}]'))
print(brace_match('[{]}{}}'))
5.4 队列
队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。进行插入的一端称为队尾(rear),插入动作称为进队或入队。进行删除的一端称为队头(front),删除动作称为出队。
1.队列的性质:先进先出FIFO(First-in,First-out)
2.队列的实现方式——环形队列
- 环形队列:当队尾指针rear == Maxsize - 1时,再前进一个位置就自动到0.
- 队首指针前进1:front = (front + 1) % MaxSize
- 队尾指针前进1:rear = (rear + 1) % MaxSize
- 队空条件:rear == front
- 队满条件:(rear + 1) % MaxSize == front
class Queue:
def __init__(self,size=100):
self.queue = [0 for _ in range(size)]
self.size = size
self.rear = 0 #队尾指针
self.front = 0 #队首指针
def push(self,element):
if not self.is_filled():
self.rear = (self.rear + 1) % self.size
self.queue[self.rear] = element
else:
raise IndexError("Queue is filled.")
def pop(self):
if not self.is_empty():
self.front = (self.front + 1) % self.size
return self.queue[self.front]
else:
raise IndexError("Queue is empty.")
#判断队空
def is_empty(self):
return self.rear == self.front
#判断队满
def is_filled(self):
return (self.rear + 1) % self.size == self.front
q = Queue(5)
for i in range(4):
q.push(i)
print(q.is_filled())
print(q.pop())
print(q.is_filled())
q.push(4)
print(q.is_filled())
3.双向队列
双向队列的两端都支持进队和出队操作。
双向队列的基本操作:
- 队首进队
- 队首出队
- 队尾进队
- 队尾出队
4.Python队列的内置模块
使用方法:from collections import deque - 创建队列:queue = deque()
- 进队:append()
- 出队:popleft()
- 双向队列队首进队:appendleft()
- 双向队列队尾出队:pop()
from collections import deque
q = deque()
q.append(1)
print(q.popleft())
q = deque([1,2,3,4,5],5)
q.append(6) # 队尾进队
print(q.popleft()) # 队首出队
#用于双向队列
q.appendleft(1) # 队首进队
print(q.pop()) # 队尾出队
5.5 栈和队列的应用——迷宫问题
1.栈——深度优先搜索
回溯法
思路:从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。
使用栈存储当前路径。
maze = [
[1,1,1,1,1,1,1,1,1,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,1,0,0,0,1,0,1],
[1,0,0,0,0,1,1,0,0,1],
[1,0,1,1,1,0,0,0,0,1],
[1,0,0,0,1,0,0,0,0,1],
[1,0,1,0,0,0,1,0,0,1],
[1,0,1,1,1,0,1,1,0,1],
[1,1,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1]
]
dirs = [
lambda x,y:(x+1,y),# 上
lambda x,y:(x-1,y),# 下
lambda x,y:(x,y-1),# 左
lambda x,y:(x,y+1)# 右
]
def maze_path(x1,y1,x2,y2):
stack = []
stack.append((x1,y1))
while(len(stack)>0):
curNode = stack[-1]# 当前的节点
if curNode[0] == x2 and curNode[1] == y2:
#走到终点了
for p in stack:
print(p)
return True
# x,y 四个方向 xx-1,y; x+1,y; x,y-1; x,y+1
for dir in dirs:
nextNode = dir(curNode[0],curNode[1])
# 如果下一个节点能走
if maze[nextNode[0]][nextNode[1]] == 0:
stack.append(nextNode)
maze[nextNode[0]][nextNode[1]] = 2 #2表示为已经走过
break
else:
maze[nextNode[0]][nextNode[1]] = 2
stack.pop()
else:
print("没有路")
return False
maze_path(1,1,8,8)
2.队列——广度优先搜索
思路:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。
使用队列存储当前正在考虑的节点。
from collections import deque
maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
dirs = [
lambda x,y:(x+1,y),# 上
lambda x,y:(x-1,y),# 下
lambda x,y:(x,y-1),# 左
lambda x,y:(x,y+1)# 右
]
def print_r(path):
curNode = path[-1]
realpath = []
while curNode[2] != -1:
realpath.append(curNode[0:2])
curNode = path[curNode[2]]
realpath.append(curNode[0:2]) # 起点
realpath.reverse()
for node in realpath:
print(node)
def maze_path_queue(x1,y1,x2,y2):
queue = deque()
queue.append((x1,y1,-1))
path = []
while len(queue) > 0:
curNode = queue.popleft()
path.append(curNode)
if curNode[0] == x2 and curNode[1] == y2:
#终点
print_r(path)
return True
for dir in dirs:
nextNode = dir(curNode[0],curNode[1])
if maze[nextNode[0]][nextNode[1]] == 0:
queue.append((curNode[0],curNode[1],len(path)-1)) # 后续节点进队,记录哪个节点带它来的
maze[nextNode[0]][nextNode[1]] = 2 #标记为已经走过
else:
print("没有路")
return False
#不知道是哪儿的问题,运行结果是 没有路
maze_path_queue(1,1,8,8)
5.6 链表
链表是由一系列节点组成的元素集合。每个节点包含两部分,数据域item和指向下一个节点的指针next。通过节点之间的相互连接,最终串联成一个链表。
class Node:
def __init__(self,item):
self.item = item
self.next = None
a = Node(1)
b = Node(2)
c = Node(3)
a.next = b
b.next = c
print(a.next.item)
print(a.next.next.item)
1.创建链表
- 头插法
- 尾插法
class Node:
def __init__(self,item):
self.item = item
self.next = None
#头插法---倒序
def create_linklist_head(li):
head = Node(li[0])
for element in li[1:]:
node = Node(element)
node.next = head
head = node
return head
#尾插法---顺序
def create_linklist_tail(li):
head = Node(li[0])
tail = head
for element in li[1:]:
node = Node(element)
tail.next = node
tail = node
return head
#链表的遍历——打印链表
def print_linklist(lk):
while lk:
print(lk.item,end=',')
lk = lk.next
lk = create_linklist_head([1,2,3])
print_linklist(lk)
print()
lk = create_linklist_tail([1,2,3,6,8])
print_linklist(lk)
2.链表的插入和删除
(1)插入节点:
- p.next = curNode.next
- curNode.next = p
(2)删除节点: - curNode.next = p.next(curNode.next = curNode.next.next)
- del p
3.双链表
双链表的每个节点有两个指针:一个指向后一个节点,另一个指向前一个节点。
(1)双链表节点的插入 - p.next = curNode.next
- curNode.next.prior = p
- p.prior = curNode
- curNode.next = p
(2)双链表节点的删除 - curNode.next = p.next
- p.next.prior = curNode
- del p
5.7 哈希表
一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
- insert(key,value):插入键值对(key,value)
- get(key):如果存在键为key的键值对则返回其value,否则返回空值
- delete(key):删除键为key的键值对
1.直接寻址表
当关键字的全域U比较小时,直接寻址是一种简单而有效的方法。
直接寻址技术缺点 - 当域U很大时,需要消耗大量内存,很不实际;
- 如果域U很大而实际出现的key很少,则大量空间被浪费;
- 无法处理关键字不是数字的情况。
2.哈希
直接寻址表:key为k的元素放到k位置上
改进直接寻址表:哈希(Hashing) - 构建大小为m的寻址表T
- key为k的元素放到h(k)位置上
- h(k)是一个函数,其将域U映射到表T[0,1,···,m-1]
3.哈希表
哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数组成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。
假设有一个长度为7的哈希表,哈希函数h(k)=k%7.元素集合{14,22,3,5}的存储方式如下图。
5.7.1 哈希冲突
由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突。
比如h(k)=k%7,h(0)=h(7)=h(14)=···
1.解决哈希冲突——开放寻址法
开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来储存这个值。
- 线性探查:如果位置i被占用,则探查i+1,i+2,······
- 二次探查:如果位置i被占用,则探查i+12,i-12,i+22,i-22,······
- 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2,h3,······
2.解决哈希冲突——拉链法
哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
5.7.2常见哈希函数
- 除法哈希法:h(k) = k % m
- 乘法哈希法:h(k) = floor(m*(A*key%1))
- 全域哈希法:h(k) = ((a*key + b) mod p) mod m a,b = 1,2,···,p-1
class LinkList:
class Node:
def __init__(self,item=None):
self.item = item
self.next = None
class LinkListIterator:
def __init__(self,node):
self.node = node
def __next__(self):
if self.node:
cur_node = self.node
self.node = cur_node.next
return cur_node.item
else:
raise StopIteration
def __iter__(self):
return self
def __init__(self,iterable=None):
self.head = None
self.tail = None
if iterable:
self.extend(iterable)
def append(self,obj):
s = LinkList.Node(obj)
if not self.head:
self.head = s
self.tail = s
else:
self.tail.next = s
self.tail = s
def extend(self,iterable):
for obj in iterable:
self.append(obj)
def find(self,obj):
for n in self:
if n == obj:
return True
else:
return False
def __iter__(self):
return self.LinkListIterator(self.head)
def __repr__(self):
return "<<"+",".join(map(str,self))+">>"
#类似于集合的结构
class HashTable:
def __init__(self,size=101):
self.size = size
self.T = [LinkList() for i in range(self.size)]
def h(self,k):
return k % self.size
def insert(self,k):
i = self.h(k)
if self.find(k):
print("Duplicated Insert.")
else:
self.T[i].append(k)
def find(self,k):
i = self.h(k)
return self.T[i].find(k)
ht = HashTable()
ht.insert(0)
ht.insert(1)
ht.insert(3)
ht.insert(102)
ht.insert(508)
print(",".join(map(str,ht.T)))
print(ht.find(203))
5.8 哈希表的应用——集合与字典
字典和集合都是通过哈希表来实现的。
a = {‘name’:‘Alex’,‘age’:18,‘gender’:‘Man’}
使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假设h(‘name’) = 3,h(‘age’) = 1,h(‘gender’) = 4,则哈希表存储为[None,18,None,‘Alex’,‘Man’]
如果发生哈希冲突,则通过拉链法或开发寻址法解决。
5.9 树的概念
树是一种数据结构,比如:目录结构。
树是一种可以递归定义的数据结构。
树是由n个节点组成的集合:
如果n=0,那这是一棵空树;
如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
一些概念:
根节点(最根上的节点)、叶子结点(没有分叉的节点)
树的深度(高度)(有多少层)
树的度(=最大的节点的度)
孩子节点/父节点(相对关系)
子树
5.10 树的实例:模拟文件系统
class Node:
def __init__(self,name,type='dir'):
self.name = name
self.type = type #"dir" or "file"
self.children = []
self.parent = None
#链式存储
def __repr__(self):
return self.name
class FileSystemTree:
def __init__(self):
self.root = Node("/")
self.now = self.root
def mkdir(self,name):
# name 以 / 结尾
if name[-1] != "/":
name += "/"
node = Node(name)
self.now.children.append(node)
node.parent = self.now
def ls(self):
return self.now.children
def cd(self, name):
if name[-1] != "/":
name += "/"
if name == "../":
self.now = self.now.parent
return
for child in self.now.children:
if child.name == name:
self.now = child
return
raise ValueError("invalid dir")
tree = FileSystemTree()
tree.mkdir("var/")
tree.mkdir("bin/")
tree.mkdir("usr/")
print(tree.ls()) #root
tree.cd("bin/")
tree.mkdir("python/")
print(tree.ls()) #bin目录下的python文件
tree.cd("../")
print(tree.ls()) #回到root
5.11 二叉树
二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")
e.lchild = a
e.rchild = g
g.rchild = f
a.rchild = c
c.lchild = b
c.rchild = d
root = e
print(e.lchild.rchild.data)
5.12 二叉树的遍历
- 前序遍历:EACBDGF
- 中序遍历:ABCDEGF
- 后序遍历:BDCAFGE
- 层次遍历:EAGCFBD
from collections import deque
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
a = BiTreeNode("A")
b = BiTreeNode("B")
c = BiTreeNode("C")
d = BiTreeNode("D")
e = BiTreeNode("E")
f = BiTreeNode("F")
g = BiTreeNode("G")
e.lchild = a
e.rchild = g
g.rchild = f
a.rchild = c
c.lchild = b
c.rchild = d
root = e
#前序遍历
def pre_order(root):
if root:
print(root.data,end=',')
pre_order(root.lchild)
pre_order(root.rchild)
#中序遍历
def in_order(root):
if root:
in_order(root.lchild)
print(root.data,end=',')
in_order(root.rchild)
#后序遍历
def post_order(root):
if root:
post_order(root.lchild)
post_order(root.rchild)
print(root.data,end=',')
#层次遍历
def level_order(root):
queue = deque()
queue.append(root)
while len(queue) > 0: #只要队不空
node = queue.popleft()
print(node.data,end=',')
if node.lchild:
queue.append(node.lchild)
if node.rchild:
queue.append(node.rchild)
pre_order(root)
print()
in_order(root)
print()
post_order(root)
print()
level_order(root)
5.13 二叉搜索树
二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key <= x.key;如果y是x右子树的一个节点,那么y.key >= x.key.
二叉搜索树的操作:查询、插入、删除
删除操作
- 如果要删除的节点是叶子节点:直接删除
- 如果要删除的节点只有一个孩子:将此节点的父亲与孩子连接,然后删除该节点。
- 如果要删除的节点有两个孩子:将其右子树的最小节点(该节点最多有一个右孩子)删除,并替换当前节点。
import random
class BiTreeNode:
def __init__(self,data):
self.data = data
self.lchild = None #左孩子
self.rchild = None #右孩子
self.parent = None
class BST:
def __init__(self,li=None):
self.root = None
if li:
for val in li:
self.insert_no_rec(val)
def insert(self,node,val):
if not node:
node = BiTreeNode(val)
elif val < node.data:
node.lchild = self.insert(node.lchild,val)
node.lchild.parent = node
elif val > node.data:
node.rchild = self.insert(node.rchild,val)
node.rchild.parent = node
return node
def insert_no_rec(self,val):
p = self.root
if not p: #空树
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild:
p = p.lchild
else: #左孩子不存在
p.lchild = BiTreeNode(val)
p.lchild.parent = p
return
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
return
else:
return
def query(self,node,val):
if not node:
return None
if node.data < val:
return self.query(node.rchild,val)
elif node.data > val:
return self.query(node.lchild,val)
else:
return node
def query_no_rec(self,val):
p = self.root
while p:
if p.data < val:
p = p.rchild
elif p.data > val:
p = p.lchild
else:
return p
return None
# 前序遍历
def pre_order(self,root):
if root:
print(root.data, end=',')
self.pre_order(root.lchild)
self.pre_order(root.rchild)
# 中序遍历
def in_order(self,root):
if root:
self.in_order(root.lchild)
print(root.data, end=',')
self.in_order(root.rchild)
# 后序遍历
def post_order(self,root):
if root:
self.post_order(root.lchild)
self.post_order(root.rchild)
print(root.data, end=',')
def __remove_node_1(self,node):
#情况1:node是叶子节点
if not node.parent: #只有一个节点的树
self.root = None
if node == node.parent.lchild:
node.parent.lchild = None #node是它父亲的左孩子
else: #node是它父亲的右孩子
node.parent.rchild = None
def __remove_node_21(self,node):
#情况2.1:node只有一个左孩子
if not node.parent: #node是根节点
self.root = node.lchild
node.lchild.parent = None
elif node == node.parent.lchild: #node是它父亲的左孩子
node.parent.lchild = node.lchild
node.lchild.parent = node.parent
else: #node是它父亲的右孩子
node.parent.rchild = node.lchild
node.lchild.parent = node.parent
def __remove_node_22(self,node):
#情况2.2:node只有一个右孩子
if not node.parent:
self.root = node.rchild
node.rchild.parent = None
elif node == node.parent.lchild:
node.parent.lchild = node.rchild
node.rchild.parent = node.parent
else:
node.parent.rchild = node.rchild
node.rchild.parent = node.parent
def delete(self, val):
if self.root: #不是空树
node = self.query_no_rec(val)
if not node: #不存在
return False
if not node.lchild and not node.rchild: #1.叶子节点
self.__remove_node_1(node)
elif not node.rchild: #2.1 只有一个左孩子
self.__remove_node_21(node)
elif not node.lchild: #2.2 只有一个右孩子
self.__remove_node_22(node)
else: #3. 两个孩子都有
min_node = node.rchild
while min_node.lchild:
min_node = min_node.lchild
node.data = min_node.data
# 删除min_node
if min_node.rchild:
self.__remove_node_22(min_node)
else:
self.__remove_node_1(min_node)
#插入测试
li = list(range(500))
random.shuffle(li)
tree = BST([4,6,7,9,2,1,3,5,8])
#tree = BST(li)
tree.insert_no_rec(11)
tree.pre_order(tree.root)
print("")
tree.in_order(tree.root)
print("")
tree.post_order(tree.root)
print("")
#查询测试
li = list(range(0,500,2))
random.shuffle(li)
tree = BST(li)
print(tree.query_no_rec(4).data)
print(tree.query_no_rec(3))
#删除测试
tree = BST([1,4,2,5,3,8,6,9,7])
tree.in_order(tree.root)
print("")
tree.delete(4)
tree.delete(1)
tree.delete(8)
tree.in_order(tree.root)
5.14 AVL树
AVL树是一棵自平衡的二叉搜索树。
AVL树具有以下性质:
- 根的左右子树的高度之差的绝对值不能超过1;
- 根的左右子树都是平衡二叉树
1.AVL树——插入
插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正。
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两棵子树的高度差2。
不平衡的出现可能有4种情况: - 不平衡是由于对k的右孩子的右子树插入导致的:左旋
- 不平衡是由于对k的左孩子的左子树插入导致的:右旋
- 不平衡是由于对K的右孩子的左子树插入导致的:右旋-左旋
- 不平衡是由于对K的左孩子的右子树插入导致的:左旋-右旋
#有的函数没有听懂,代码有些问题,直接放弃
from bst import BiTreeNode,BST
class AVLNode(BiTreeNode):
def __init__(self,data):
BiTreeNode.__init__(self,data)
self.bf = 0
class AVLTree(BST):
def __init__(self,li=None):
BST.__init__(self,li)
def rotate_left(self,p,c):
s2 = c.lchild
p.rchild = s2
if s2:
s2.parent = p
c.lchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c
def rotate_right(self,p,c):
s2 = c.rchild
p.lchild = s2
if s2:
s2.parent = p
c.rchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c
def rotate_right_left(self,p,c):
g = c.lchild
s3 = g.rchild
c.lchild = s3
if s3:
s3.parent = c
g.rchild = c
c.parent = g
s2 = g.lchild
p.rchild = s2
if s2:
s2.parent = p
g.lchild = p
p.parent = g
#更新bf
if g.bf > 0:
p.bf = -1
c.bf = 0
elif g.bf < 0:
p.bf = 0
c.bf = 1
else: #插入的是g
p.bf = 0
c.bf = 0
return g
def rotate_left_right(self,p,c):
g = c.rchild
s2 = g.lchild
c.rchild = s2
if s2:
s2.parent = c
g.lchild = c
c.parent = g
s3 = g.rchild
p.lchild = s3
if s3:
s3.parent = p
g.rchild = p
p.parent = g
# 更新bf
if g.bf < 0:
p.bf = 1
c.bf = 0
elif g.bf > 0:
p.bf = 0
c.bf = -1
else: # 插入的是g
p.bf = 0
c.bf = 0
return g
def insert_no_rec(self,val):
# 1. 和BST一样,插入
p = self.root
if not p:
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild:
p = p.lchild
else: #左孩子不存在
p.lchild = BiTreeNode(val)
p.lchild.parent = p
node = p.lchild #node存储的就是插入的节点
break
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
node = p.rchild
break
else: #val == p.data
return
#2. 更新balance factor
while node.parent: #node.parent不空
if node.parent.lchild == node: #传递是从左子树来的,左子树更沉了
#更新node.parent的bf -= 1
if node.parent.bf < 0: #原来node.parent.bf == -1,更新后变成-2
#做旋转
#看node哪边沉
g = node.parent.parent #为了连接旋转之后的子树
x = node.parent #旋转之前子树的根
if node.bf > 0:
n = self.rotate_left_right(node.parent,node)
else:
n = self.rotate_right(node.parent,node)
#记得:把n和g连起来
elif node.parent.bf > 0: #原来node.parent.bf = 1,更新之后变成0
node.parent.bf = 0
break
else: # 原来node.parent.bf = 0,更新之后变成-1
node.parent.bf = -1
node = node.parent
continue
else: #传递是从右子树来的,右子树更沉了
#更新node.parent.bf += 1
if node.parent.bf > 0: #原来node.parent.bf == 1,更新后变成2
#做旋转
#看node哪边沉
g = node.parent.parent # 为了连接旋转之后的子树
x = node.parent # 旋转之前子树的根
if node.bf < 0: #node.bf = 1
n = self.rotate_right_left(node.parent,node)
else: #node.bf = -1
n = self.rotate_left(node.parent,node)
# 记得连起来
elif node.parent.bf < 0: #原来node.parent.bf = -1,更新之后变成0
node.parent.bf = 0
break
else: #原来node.parent.bf = 0,更新之后变成1
node.parent.bf = 1
node = node.parent
continue
#连接旋转之后的子树
n.parent = g
if g: #g不是空
if x == g.lchild:
g.lchild = n
else:
g.rchild = n
break
else:
self.root = n
break
tree = AVLTree([9,8,7,6,5,4,3,2,1])
tree.pre_order(tree.root)
print("")
tree.in_order(tree.root)