目录
链表
链表介绍
创建和遍历链表
链表节点插入和删除
双链表
链表总结——复杂度分析
哈希表(散列表)
哈希表介绍
哈希冲突
哈希表实现
哈希表应用
树
树
树的示例——模拟文件系统
二叉树
二叉树的链式存储
二叉树的遍历
二叉搜索树
插入
查询
删除
AVL树
旋转
插入
链表
链表介绍
链表是由一系列节点组成的元素集合,每个节点包含两部分,数据域item和指向下一个节点的指针next。通过节点之间的相互连接,最终串联成一个链表。
节点定义:
class Node(object):
def __init__(self, item):
self.item = item
self.next = None
创建和遍历链表
头插法/尾插法
class Node:
def __init__(self, item):
self.item = item
self.next = None
# 创建链表(头插法)
def crweate_linklist_head(li):
head = Node(li[0])
for element in li[1:]:
node = Node(element)
node.next = head
head = node
return head
# 创建链表(尾插法)
def crweate_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
print()
链表节点插入和删除
插入是创建一个节点,将该节点指向插入位置的下一个节点,上一个节点指向该节点。
删除是将上一个节点的next指向下一个节点。
双链表
双链表每个节点有两个指针:一个指向后一个节点,另一个指向前一个节点。
节点定义:
class Node(object):
def __init__(self, item=None):
self.item = item
self.next = None
self.prior = None
链表总结——复杂度分析
- 按元素值查找:O(n)
- 按下标查找:O(n)
- 在某元素后插入:O(1)
- 删除某元素:O(1)
链表在插入和删除的操作上明显快于顺序表
链表的内存可以更灵活分配
链表这种链式存储的数据结构对树和图的结构有很大启发性
哈希表(散列表)
哈希表介绍
介绍:哈希表是一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
- insert(key, value):插入键值对(key, value)
- get(key):如果存在键为key的键值对,返回其valve,否则返回空值
- delete(key):删除键为key的键值对
哈希表是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数构成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。
哈希冲突
哈希冲突:由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况。
1.开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
- 线性探查:如果位置i被占用,则探查i+1, i+2......
- 二次探查:如果位置i被占用,则探查
- 二度哈希:有n个哈希函数,当使用第一个哈希函数h1发生冲突时,则尝试使用h2,h3......
2.拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
常见哈希函数:
- 除法哈希法:h(k) = k % m
- 乘法哈希法:h(k) = floor(m*(A*key%1))
- 全域哈希法: 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 # 当遍历结束时抛出StopIteration异常
def __iter__(self):
return self # 返回迭代器自身
# 初始化链表
def __init__(self, iterable=None):
self.head = None # 头节点
self.tail = None # 尾节点
if iterable:
self.extend(iterable) # 如果传入可迭代对象,则调用extend方法进行扩展
# 在链表尾部添加节点
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 # 返回True
else:
return False # 找不到目标元素时返回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 _ 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()
for i in range(200):
ht.insert(i)
# print(",".join(map(str, ht.T)))
print(ht.find(203))
哈希表应用
python的字典,集合都是哈希表
md5算法
树
树
介绍:树是一种数据结构
- 树是一种可以递归定义的数据结构
- 树是由n个结点组成的集合
- 如果n=0,那这是一棵空树
- 如果n>0,那存在一个结点作为树的根结点,其他结点可以分为m个集合,每个集合本身又是一棵树
一些概念:
- 根结点、叶子结点
- 树的深度/高度
- 树的度
- 孩子结点、父结点
- 子树
树的示例——模拟文件系统
# 节点
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())
tree.cd("bin")
tree.mkdir("python")
print(tree.ls())
tree.cd("../")
print(tree.ls())
二叉树
二叉树就是度不超过2的树
- 每个结点最多有两个孩子结点
- 两个孩子结点被区分为左孩子结点和右孩子结点
完全二叉树
- 满二叉树:一个二叉树,如果一个层的节点数都达到最大值,则这个二叉树就是满二叉树
- 完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
二叉树的存储方式(表示方式)
- 链式存储方式
- 顺序存储方式(堆排序用这个)——用列表来存
- 父结点和左孩子结点的编号下标的关系
0-1 1-3 2-5 3-7 4-9
i -> 2i+1
- 父结点和右孩子结点的编号下标的关系
0-2 1-4 2-6 3-8 4-10
i -> 2i+2
之前学过线性存储,这里是链式存储
二叉树的链式存储
将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来连接。
节点定义:
class BiTreeNode:
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
二叉树的遍历
二叉树的遍历方式:
- 前序遍历:如果不空,先访问根,再访问左子树,最后访问右子树。
- 中序遍历:如果不空,先访问左子树,再访问根,最后访问右子树。
- 后序遍历:如果不空,先访问左子树,再访问右子树,最后访问根。
- 层次遍历:一层一层访问。
# 前序遍历
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:
in_order(root.lchild)
in_order(root.rchild)
print(root.data, end=",")
# 层次遍历
from compileall import queue
def level_order():
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)
二叉搜索树
是一颗二叉树,并且满足左子树的根比根小,右子树的根比根大。
二叉搜索树的操作:查询、插入、删除
插入
pass
查询
pass
删除
pass
AVL树
旋转
pass
插入
pass
代码自己手动敲一遍理解更深哦!