2.双向链表的介绍
双向链表
既可以从头遍历到尾, 又可以从尾遍历到头
链表相连的过程是双向的.
一个节点既有向前连接的引用, 也有一个向后连接的引用.
双向连接的图解:
3.双向链表的创建
#创建节点类
class Node:
def __init__(self,el):
self.prev=None
self.next=None
self.el=el
#创建双向链表
class double_link_list:
#定义属性
def __init__(self):
self.__head=None
self.__tail=None
self.length=0
-
代码解析:
-
基本思路和单向链表比较相似, 都是创建节点结构函数以及定义一些属性和方法.
-
只是Node中添加了一个self.prev属性, 该属性用于指向上一个节点.
-
另外属性中添加了一个self.tail属性, 该属性指向末尾的节点
-
3.1 尾部追加数据
def append(self,el):
#把传入的数据封装为创建的新节点
node = Node(el)
#判断链表是不是空的
# if self.length==0:
if self.is_empty():
# 是空的 把head和tail指向新节点
self.__head = node
self.__tail = node
else:
#不是空的 把tail指向的节点的next属性指向新节点 新节点的pre属性指向tail指向的节点 tail指向新节点
self.__tail.next=node
node.prev=self.__tail
self.__tail=node
self.length+=1
3.2 任意位置插入
def insert(self,position,el):
#判断越界问题
if position<0 or position>self.length:
return False
else:
#把传入的数据封装为一个节点
node=Node(el)
#插入到头部
if position==0:
self.__head.prev=node
node.next=self.__head
self.__head=node
#插入到尾部
elif position==self.length:
self.__tail.next=node
node.prev=self.__tail
self.__tail=node
#插入到中间
else:
current=self.__head
previous=None
index=0
while(index<position):
previous=current
current=current.next
index+=1
previous.next=node
node.prev=previous
current.prev=node
node.next=current
self.length+=1
return True
3.3 通过位置移除数据
def removeAt(self,position):
#判断是否越界
if position<0 or position>=self.length:
return False
#不越界
else:
# 移除中间的
current = self.__head
previous = None
index = 0
if self.length==1:
self.__head = None
self.__tail = None
#移除第一个
if position==0:
current=self.__head
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:
while(index<position):
previous=current
current=current.next
index+=1
# current
previous.next=current.next
current.next.prev=previous
self.length-=1
return current.el
3.4 获取位置元素
# 根据元素获取再链表中第一个出现的位置, 没有返回 - 1
def indexOf(self,element):
#1.定义变量保存信息
current = self.head
index = 0
#2.查找正确的信息
while current:
if current.element == element:
return index
index+=1
current = current.next
#3.来到这个位置, 说明没有找到, 则返回-1
return -1
3.5 根据元素删除
def remove(self,el):
index=self.indexOf(el)
return self.removeAt(index)
3.6 其他方法
# 判断是否为空
def isEmpty(self):
return self.length == 0
# 获取链表长度
def size(self):
return self.length
# 获取第一个元素
def getHead(self):
return self.head.element
# 获取最后一个元素
def getTail(self):
return self.tail.element
树
树的结构:
-
树的定义:
-
树(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.二叉树
2.1 二叉树的概念
-
二叉树的定义
-
二叉树可以为空, 也就是没有结点.
-
若不为空,则它是由根结点和称为其左子树TL和右子树TR的两个不相交的二叉树组成。
-
2.2 二叉树的特性
二叉树有几个比较重要的特性, 在笔试题中比较常见:
-
一个二叉树第 i 层的最大结点数为:2^(i-1), i >= 1;
-
深度为k的二叉树有最大结点总数为: 2^k - 1, k >= 1;
-
对任何非空二叉树 T,若n0表示叶结点的个数、n2是度为2的非叶结点个数,那么两者满足关系n0 = n2 + 1。
2.3 特殊的二叉树
-
完美二叉树(Perfect Binary Tree) , 也称为满二叉树(Full Binary Tree)
-
在二叉树中, 除了最下一层的叶结点外, 每层节点都有2个子结点, 就构成了满二叉树.
-
-
完全二叉树(Complete Binary Tree)
-
除二叉树最后一层外, 其他各层的节点数都达到最大个数.
-
且最后一层从左向右的叶结点连续存在, 只缺右侧若干节点.
-
完美二叉树是特殊的完全二叉树.
-
2.5 二叉树的存储
-
二叉树的存储常见的方式是链表.
-
链表存储:
-
二叉树最常见的方式还是使用链表存储.
-
每个结点封装成一个Node, Node中包含存储的数据, 左结点的引用, 右结点的引用.
-
3.二叉搜索树
3.1 什么是二叉搜索树?
-
二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树
-
二叉搜索树是一颗二叉树, 可以为空;如果不为空,满足以下性质:
-
非空左子树的所有键值小于其根结点的键值。
-
非空右子树的所有键值大于其根结点的键值。
-
左、右子树本身也都是二叉搜索树。
-
下面哪些是二叉搜索树, 哪些不是?
-
- 二叉搜索树的特点:
-
查找效率非常高, 这也是二叉搜索树中, 搜索的来源.
-
那么利用这个特点, 我们可以做什么事情呢?
-
二叉搜索树的特点就是相对较小的值总是保存在左结点上, 相对较大的值总是保存在右结点上.
-
3.2、二叉搜索树的操作
-
二叉搜索树有哪些常见的操作呢?
-
insert(key)
:向树中插入一个新的键。 -
search(key)
:在树中查找一个键,如果结点存在,则返回true
;如果不存在,则返回false
。 -
preOrderTraverse
:通过先序遍历方式遍历所有结点。 -
inOrderTraverse
:通过中序遍历方式遍历所有结点。 -
postOrderTraverse
:通过后序遍历方式遍历所有结点。 -
min
:返回树中最小的值/键。 -
max
:返回树中最大的值/键。 -
remove(key)
:从树中移除某个键。
-
3.3 二叉树的创建
3.3.1 创建二叉搜索树
class Node:
def __init__(self,el):
self.el=el
self.left=None
self.right=None
class binary_serach_tree:
def __init__(self):
self.root=None
3.3.2 插入数据
def insert_node(self,node_tree,new_node):
if node_tree.el<=new_node.el:
if node_tree.right is None:
node_tree.right=new_node
else:
self.insert_node(node_tree.right,new_node)
else:
if node_tree.left is None:
node_tree.left=new_node
else:
self.insert_node(node_tree.left,new_node)
def insert(self,key):
#把传入的key封装为node
node=Node(key)
#判断是否为空树
if self.root is None:
self.root=node
else:
self.insert_node(self.root,node)
# current=self.root
# last=None
# while(current):
# last=current
# if current.el<key:
# current = current.right
# else:
# current = current.left
# if last.el<key:
# last.right=node
# else:
# last.left=node
3.3.3 遍历二叉搜索树
#先序遍历
def preOrderTranversalNode(self,node,cb):
cb(node.el)
if node.left:
self.preOrderTranversalNode(node.left,cb)
if node.right:
self.preOrderTranversalNode(node.right, cb)
#中序遍历
def inOrderTranversalNode(self,node,cb):
if node.left:
self.inOrderTranversalNode(node.left,cb)
cb(node.el)
if node.right:
self.inOrderTranversalNode(node.right, cb)
#后序遍历
def postOrderTranversalNode(self,node,cb):
if node.left:
self.postOrderTranversalNode(node.left,cb)
if node.right:
self.postOrderTranversalNode(node.right, cb)
cb(node.el)
def TranversalNode(self,cb):
# print(self.root.el)
# cb(self.root.el)
if self.root:
self.preOrderTranversalNode(self.root,cb)#先序
# self.inOrderTranversalNode(self.root, cb) # 中序
# self.postOrderTranversalNode(self.root, cb) # 后序
3.3.4 搜索特定值
def min(self):
if self.root is None:
return False
current=self.root
while (current.left):
current=current.left
return current.el
def max(self):
if self.root is None:
return False
current=self.root
while (current.right):
current=current.right
return current.el
def searchNode(self,node,el):
if node is None:
return False
if node.el>el:
return self.searchNode(node.left,el)
elif node.el<el:
return self.searchNode(node.right,el)
else:
return True
def search(self,el):
return self.searchNode(self.root,el)
# 找后继的方法
def getSuccessor(self, delNode):
# 1.使用变量保存临时的节点
successorParent = delNode
successor = delNode
current = delNode.right # 要从右子树开始找
# 2.寻找节点
while current is not None:
successorParent = successor # while循环完毕-后继的父节点
successor = current # while循环完毕-要删除的节点的右子树种最left节点:后继
current = current.left # while循环完毕-None
# 3.如果是删除图中15的情况, 还需要如下代码
if successor != delNode.right:
successorParent.left = successor.right
successor.right = delNode.right
return successor
3.3.6 二叉搜索树的删除
def remove(self,el):
current=self.root
previous=None
isleft=True
while current:
if current.el>el:
previous=current
current=current.left
isleft=True
elif current.el<el:
previous = current
current=current.right
isleft = False
else:
#current就是要删除的节点
#previous就是要删除的节点的父节点
#情况一 current是叶节点
if current.left is None and current.right is None:
if self.root == current:
self.root=None
return True
if isleft:
# 把current的后代放在previous的left
previous.left=None
else:
# 把current的后代放在previous的right
previous.right = None
# 情况二 current只有一个儿子
# 只有左儿子并且没有右儿子
elif current.left and current.right is None:
if self.root == current:
self.root = current.left
return True
#如果删除的元素是它父节点的左边
if isleft:
# 把current的left后代放在previous的left
previous.left = current.left
else:
# 把current的left后代放在previous的right
previous.right = current.left
#只有右儿子并且没有左儿子
elif current.right and current.left is None:
if self.root == current:
self.root = current.right
return True
# 如果删除的元素是它父节点的左边
if isleft:
# 把current的right后代放在previous的left
previous.left = current.right
else:
# 把current的right后代放在previous的right
previous.right = current.right
# 情况三
#左右儿子都有值
else:
# 3.3.1.获取后继节点
successor = self.getSuccessor(current)
# 3.3.2. 判断是否是根节点
if current == self.root:
self.root = successor
elif isleft:
previous.left = successor
else:
previous.right = successor
# 3.3.3.将删除节点的左子树赋值给successor
successor.left = current.left
return False