双向链表
双向链表:可以从头遍历到尾,也可以从尾遍历到头,链表相连的过程是双向的。
单项链表的缺点:当我们需要回到前一个节点时很困难。
双向链表的缺点:
- 插入或者删除时我们需要处理的引用有四个,而不是单向节点的两个,相对麻烦一点
- 占用的空间更大
双向链表的图解
双向链表的常用功能实现:
is_empty:判断链表是否为空
append:尾部追加数据
insert:任意位置插入数据
remove_at:根据位置移除数据
index_of :获取元素位置
remove:根据元素删除数据
size:获取链表长度
class Node:
def __init__(self, element):
self.element = element
self.prev = None
self.next = None
class DoubleLinklist:
def __init__(self):
self.head = None
self.tail = None
self.length = 0
# 判断链表是否为空
def is_empty(self):
return self.length == 0
# 向链表的末尾添加数据
def append(self, element):
node = Node(element)
if self.is_empty():
self.head = node
self.tail = node
else:
self.tail.next = node
node.prev = self.tail
self.tail = node
self.length += 1
# 在任意位置插入数据
def insert(self, element, position):
node = Node(element)
# 判断是否越界
if position < 0 or position > self.length:
return False
# 在链表末尾插入
elif position == self.length:
self.append(element)
# 在链表第一个位置插入
elif position == 0:
if self.is_empty():
self.head = node
self.tail = node
# 在中间位置插入
else:
current = self.head
previous = None
for i in range(position):
previous = current
current = current.next
previous.next = node
current.prev = node
node.prev = previous
node.next = current
self.length += 1
return True
# 移除任意位置的数据并返回该值
def remove_at(self, position):
current = self.head
# 判断是否越界
if position < 0 or position > self.length:
return False
# 移除第一个位置
elif position == 0:
if self.length == 1:
self.head = None
self.tail = None
else:
self.head = self.head.next
self.head.prev = None
# 移除末尾的数据
elif position == self.length - 1:
current = self.tail
self.tail = self.tail.prev
self.tail.next = None
# 移除中间的数据
else:
previous = None
for i in range(position):
previous = current
current = current.next
previous.next = current.next
current.next.prev = previous
self.length -= 1
return current.element
# 获取元素位置
def index_of(self, element):
current = self.head
position = 0
while current:
if current.element == element:
return position
position += 1
current = current.next
return False
# 根据元素删除
def remove(self, element):
index = self.index_of(element)
return self.remove_at(index)
# 获取链表长度
def size(self):
return self.length
dlinklist = DoubleLinklist()
dlinklist.append("qwer")
dlinklist.append(123)
dlinklist.append("abcd")
dlinklist.append(456)
print(dlinklist.insert("efg", 1))
print(dlinklist.insert(234, 8))
print(dlinklist.index_of(123))
树
树也是一种非常常用的数据结构, 特别是二叉树.
二叉树是程序中一种非常重要的数据结构, 它的优势是前面介绍的数据结构所没有的.
1.树的定义
- 树(Tree): n(n≥0)个结点构成的有限集合。
- 当n=0时,称为空树;
- 对于任一棵非空树(n> 0),它具备以下性质:
- 树中有一个称为“根(Root)”的特殊结点,用 root 表示;
- 其余结点可分为m(m>0)个互不相交的有限集T1,T2,… ,Tm,其中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”
- 注意:
- 子树之间不可以相交
- 除了根结点外,每个结点有且仅有一个父结点;
- 一棵N个结点的树有N-1条边。
树的术语:
- 1.结点的度(Degree):结点的子树个数.
- 2.树的度:树的所有结点中最大的度数. (树的度通常为结点的个数N-1)
- 3.叶结点(Leaf):度为0的结点. (也称为叶子结点)
- 4.父结点(Parent):有子树的结点是其子树的根结点的父结点
- 5.子结点(Child):若A结点是B结点的父结点,则称B结点是A结点的子结点;子结点也称孩子结点。
- 6.兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟结点。
- 7.路径和路径长度:从结点n1到nk的路径为一个结点序列n1 , n2,… , nk, ni是 ni+1的父结点。路径所包含边的个数为路径的长度。
- 8.结点的层次(Level):规定根结点在1层,其它任一结点的层数是其父结点的层数加1。
- 9.树的深度(Depth):树中所有结点中的最大层次是这棵树的深度。
2.二叉树
二叉树的定义
- 二叉树可以为空, 也就是没有结点.
- 若不为空,则它是由根结点和称为其左子树TL和右子树TR的两个不相交的二叉树组成。
二叉树有几个比较重要的特性, 在笔试题中比较常见:
-
一个二叉树第 i 层的最大结点数为:2^(i-1), i >= 1;
-
深度为k的二叉树有最大结点总数为: 2^k - 1, k >= 1;
-
对任何非空二叉树 T,若n0表示叶结点的个数、n2是度为2的非叶结点个数,那么两者满足关系n0 = n2 + 1。
完美二叉树(Perfect Binary Tree) , 也称为满二叉树(Full Binary Tree)
- 在二叉树中, 除了最下一层的叶结点外, 每层节点都有2个子结点, 就构成了满二叉树.
完全二叉树(Complete Binary Tree)
- 除二叉树最后一层外, 其他各层的节点数都达到最大个数.
- 且最后一层从左向右的叶结点连续存在, 只缺右侧若干节点.
- 完美二叉树是特殊的完全二叉树.
2.二叉搜索树
-
二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树
-
二叉搜索树是一颗二叉树, 可以为空;如果不为空,满足以下性质:
- 非空左子树的所有键值小于其根结点的键值。
- 非空右子树的所有键值大于其根结点的键值。
- 左、右子树本身也都是二叉搜索树。
二叉搜索树有哪些常见的操作呢?
insert(key)
:向树中插入一个新的键。search(key)
:在树中查找一个键,如果结点存在,则返回true
;如果不存在,则返回false
。preOrderTraverse
:通过先序遍历方式遍历所有结点。inOrderTraverse
:通过中序遍历方式遍历所有结点。postOrderTraverse
:通过后序遍历方式遍历所有结点。min
:返回树中最小的值/键。max
:返回树中最大的值/键。remove(key)
:从树中移除某个键。
# 创建结点类
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
#创建BinarySearchTree
class BinarySerachTree:
def __init__(self):
self.root = None # 保存根的属性
# 向树中插入数据
def insert(self, key):
# 1.根据key创建对应的node
newNode = Node(key)
# 2.判断根结点是否有值
if self.root == None:
self.root = newNode
else:
self.insertNode(self.root, newNode)
def insertNode(self, node, newNode):
# 1.准备向左子树插入数据
if newNode.key < node.key:
if (node.left == None):
# 1.1.node的左子树上没有内容
node.left = newNode
else:
# 1.2.node的左子树上已经有了内容
self.insertNode(node.left, newNode)
# 2.准备向右子树插入数据
else:
if node.right == None:
# 2.1.node的右子树上没有内容
node.right = newNode
else:
# 2.2.node的右子树上有内容
self.insertNode(node.right, newNode)
# 先序遍历
def preOrderTraversal(self, handler):
self.preOrderTranversalNode(self.root, handler)
def preOrderTranversalNode(self, node, handler):
if node is not None:
# 1.打印当前经过的节点
handler(node.key)
# 2.遍历所有的左子树
self.preOrderTranversalNode(node.left, handler)
# 3.遍历所有的右子树
self.preOrderTranversalNode(node.right, handler)
# 中序遍历
def inOrderTraversal(self, handler):
self.inOrderTraversalNode(self.root, handler)
def inOrderTraversalNode(self, node, handler):
if node is not None:
# 1.遍历所有的左子树
self.inOrderTraversalNode(node.left, handler)
# 2.打印当前经过的节点
handler(node.key)
# 3.遍历所有的右子树
self.inOrderTraversalNode(node.right, handler)
# 后续遍历
def postOrderTraversal(self, handler):
self.postOrderTraversalNode(self.root, handler)
def postOrderTraversalNode(self, node, handler):
if node is not None:
# 1.遍历所有的左子树
self.postOrderTraversalNode(node.left, handler)
# 2.遍历所有的右子树
self.postOrderTraversalNode(node.right, handler)
# 3.打印当前经过的节点
handler(node.key)
# 获取最小值
def min(self):
node = self.root
while node.left is not None:
node = node.left
return node.key
# 获取最大值
def max(self):
node = self.root
while node.right is not None:
node = node.right
return node.key
# 搜搜特定的值
def search(self, key):
return self.searchNode(self.root, key)
def searchNode(self, node, key):
# 1.如果传入的node为None那么, 那么就退出递归
if node is None:
return False
# 2.判断node节点的值和传入的key大小
if node.key > key: # 2.1.传入的key较小, 向左边继续查找
return self.searchNode(node.left, key)
elif node.key < key: # 2.2.传入的key较大, 向右边继续查找
return self.searchNode(node.right, key)
else: # 2.3.相同, 说明找到了key
return True
# 找后继的方法
def getSuccessor(self, delNode):
# 1.使用变量保存临时的节点
successorParent = delNode
successor = delNode
current = delNode.right # 要从右子树开始找
# 2.寻找节点
while current is not None:
successorParent = successor
successor = current
current = current.left
# 3.如果是删除图中15的情况, 还需要如下代码
if successor != delNode.right:
successorParent.left = successor.right
successor.right = delNode.right
return successor
# 删除结点
def remove(self, key):
# 1.定义临时保存的变量
current = self.root
parent = None
isLeftChild = True
# 2.开始查找节点
while current.key != key:
parent = current
if key < current.key:
isLeftChild = True
current = current.left
else:
isLeftChild = False
current = current.right
# 如果发现current已经指向None, 那么说明没有找到要删除的数据
if current is None:
return False
# 3.找到了开始删除
# 3.1删除的结点是叶结点
if current.left is None and current.right is None:
if current == self.root:
self.root = None
elif isLeftChild:
parent.left = None
else:
parent.right = None
# 3.2删除有一个子节点的节点
elif current.right is None:
if current == self.root:
self.root = current.left
elif isLeftChild:
parent.left = current.left
else:
parent.right = current.left
elif current.left is None:
if current == self.root:
self.root = current.right
elif isLeftChild:
parent.left = current.right
else:
parent.right = current.right
# 3.3删除有两个子节点的节点
else:
# 3.3.1.获取后继节点
successor = self.getSuccessor(current)
# 3.3.2. 判断是否是根节点
if current == self.root:
self.root = successor
elif isLeftChild:
parent.left = successor
else:
parent.right = successor
# 3.3.3.将删除节点的左子树赋值给successor
successor.left = current.left
# 4.删除完毕返回True
return True
bst = BinarySerachTree()
bst.insert(11)
bst.insert(7)
bst.insert(15)
bst.insert(5)
bst.insert(3)
bst.insert(9)
bst.insert(8)
bst.insert(10)
bst.insert(13)
bst.insert(12)
bst.insert(14)
bst.insert(20)
bst.insert(18)
bst.insert(25)
bst.insert(6)
bst.preOrderTraversal(lambda key: print(key, end=" "))
#bst.inOrderTraversal(lambda key: print(key, end=" "))
#bst.postOrderTraversal(lambda key: print(key, end=" "))
print(bst.min())
print(bst.max())
print(bst.search(25))
print(bst.search(100))
bst.remove(15)
bst.inOrderTraversal(lambda key: print(key, end=" "))
st.insert(12)
bst.insert(14)
bst.insert(20)
bst.insert(18)
bst.insert(25)
bst.insert(6)
bst.preOrderTraversal(lambda key: print(key, end=" “))
#bst.inOrderTraversal(lambda key: print(key, end=” “))
#bst.postOrderTraversal(lambda key: print(key, end=” “))
print(bst.min())
print(bst.max())
print(bst.search(25))
print(bst.search(100))
bst.remove(15)
bst.inOrderTraversal(lambda key: print(key, end=” "))