赞
踩
目录
链表是由一系列节点组成的元素集合,每个节点包含两部分,数据域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
链表在插入和删除的操作上明显快于顺序表
链表的内存可以更灵活分配
链表这种链式存储的数据结构对树和图的结构有很大启发性
介绍:哈希表是一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
哈希表是一种线性表的存储结构。哈希表由一个直接寻址表和一个哈希函数构成。哈希函数h(k)将元素关键字k作为自变量,返回元素的存储下标。
哈希冲突:由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况。
1.开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
2.拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
常见哈希函数:
- 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算法
介绍:树是一种数据结构
一些概念:
- # 节点
- 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:
- post_order(root.lchild)
- post_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)
是一颗二叉树,并且满足左子树的根比根小,右子树的根比根大。
二叉搜索树的操作:查询、插入、删除
二叉搜索树的效率:
解决方案:
两种方式:递归和非递归
- 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.lchild, 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
三种情况:
- # 情况1:node是叶子节点
- def __remove_node_1(self, node):
- if not node.parent: # 如果是根节点
- self.root = None
- elif node == node.parent.lchild: # node是它父亲的左孩子
- node.parent.lchild = None
- else: # 是右孩子
- node.parent.rchild = None
-
- # 情况2.1:node只有一个左孩子
- def __remove_node_21(self, node):
- if not node.parent: # 如果是根节点
- self.root = node.lchild
- node.lchild.parent = None
- elif node == node.parent.lchild: # 是它父亲的左孩子
- node.parent.lchild = node.lchild
- node.lchild.parent = node.parent
- else: # 是它父亲的右孩子
- node.parent.rchild = node.lchild
- node.lchild.parent = node.parent
-
- # 情况2.2:node只有一个右孩子
- def __remove_node_22(self, 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
-
- # 情况3:左右节点都有
- def delete(self, val):
- if self.root: # 不是空树再删
- node = self.query_no_rec(val)
- if not node: # 如果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_21(min_node)
AVL树:AVL树是一棵自平衡的二叉搜索树
AVL树具有以下性质:
旋转是修正出现不平衡的树的操作
这里的旋转是服务于插入的
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K——K的两棵子树的高度差2.
插入不平衡的出现可能有四种情况:
- from bst import BiTreeNode, BST
-
- class AVLNode(BiTreeNode): # 继承
- def __init__(self):
- 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
-
- # 右旋
- 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
-
- # 右旋左旋
- 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:
- p.bf = 0
- c.bf = 0
-
- # 左旋右旋
- 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:
- p.bf = 0
- c.bf = 0
插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正。
- def insert_no_rec(self, val): # 重新定义,覆盖原先的方法
- # 第一步:和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
-
- # 第二步:更新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:
- n = self.rotate_right_left(node.parent, node)
- else:
- 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
代码自己手动敲一遍理解更深哦!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。