Data Structure
数据结构
- 数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。
- 简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。
- 比如:列表、集合与字典等都是一种数据结构。
- N.Wirth: “程序=数据结构+算法”
分类
数据结构按照其逻辑结构可分为线性结构、树结构、图结构
- 线性结构:数据结构中的元素存在一对一的相互关系
- 树结构:数据结构中的元素存在一对多的相互关系
- 图结构:数据结构中的元素存在多对多的相互关系
一、列表/数组
- 内容:列表在内存中以一块连续的空间存放,列表中存放的是每个元素的引用
- 基本操作:
- 新增:insert O(n),append O(1)
- 删除:remove O(n),pop O(1)
- 修改:O(1)
- 遍历:O(n)
- 查找:O(1)
- 列表和数组区别:
- 数组:元素类型要相同
- 数组:长度固定
- 列表:元素类型可以不一样
- 列表:存的不是真实的值,是地址
- 32位机器上,一个整数占4个字节,一个地址占4个字节,内存最大4个G
二、栈
- 内容:栈(Stack)是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。
- 特点:后进先出(last-in, first-out)
- 其他概念:栈顶 栈底
- 基本操作:
- 进栈:push
- 出栈:pop
- 取栈顶元素:gettop
- 实现原理:
- 不需要自己定义,使用列表结构即可。
- 进栈函数:append
- 出栈函数:pop
- 查看栈顶函数:li[-1]
- 栈的应用:
- 括号匹配问题,给一个字符串中,其中包含(,[,{ 判断该字符串中的括号是否匹配.例 ()()[]{} √,([{()}]) √,[]( X,[(]) X
- 思路:创建一个空栈,循环字符串,将字符串中左括号放入栈中,如果找到相匹配的右括号,将栈顶的左括号出栈
class Stack: def __init__(self): self.stack = [] def push(self, element): self.stack.append(element) def pop(self): return self.stack.pop() def get_top(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.get_top() == match[ch]: stack.pop() else: # stack.get_top() != match[ch] return False if stack.is_empty(): return True else: return False print(brace_match('[{()}(){()}[]({}){}]')) print(brace_match('[]}'))
三、队列
- 内容:
- 队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除。
- 进行插入的一端称为队尾(rear),插入动作称为进队或入队
- 进行删除的一端称为队头(front),删除动作称为出队
- 特点:先进先出(First-in, First-out)
- 双向队列:
- 队列的两端都允许进行进队和出队操作。
- 实现原理:
- 初步设想:列表+两个下标指针
- 实现:创建一个列表和两个变量,front指向队首,rear指向队尾,初始都为0
- 进队操作:元素写到li[rear]的位置,rear自增1
- 出队操作:返回li[front]的位置,front自减1
- 最终:环形队列
- 环形队列:当队尾指针front == Maxsize - 1时,再前进一个位置就自动到0。
- 实现方式:求余数运算
- 队首指针前进1:front = (front + 1) % MaxSize
- 队尾指针前进1:rear = (rear + 1) % MaxSize
- 队空条件:rear == front 队满条件:(rear + 1) % MaxSize == front
- 内置模块:
- 使用方法: from collections import deque
- 创建队列: queue = deque()
- 进队:append
- 出队:popleft
- 双向队列队首进队:appendleft
- 双向队列队尾进队:pop
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.pop()) q.push(4)
from collections import deque # q = deque([1,2,3,4,5], 5) # q.append(6) # 队尾进队 # print(q.popleft()) # 队首出队 # 用于双向队列 # q.appendleft(1) # 队首进队 # q.pop() # 队尾出队 def tail(n): with open('test.txt', 'r') as f: q = deque(f, n) return q for line in tail(5): print(line, end='')
四、链表
1、单向链表
- 内容:
- 链表中每一个元素都是一个对象,每个对象称为一个节点,包含有数据域key和指向下一个节点的指针next。通过各个节点之间的相互连接,最终串联成一个链表。
- 头插法:
- node.next = head
- head = node
- 尾插法:
- tail = head
- tail.next = node
- tail = node
- 遍历链表:
- 链表节点的插入:
- p.next = curNode.next
- curNode.next = p
- 链表节点的删除:
- p = curNode.next
- curNode.next = curNode.next.next
- del p
2、双链表
- 内容:
- 双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点
- 双链表节点的插入:
- p.next = curNode.next
- curNode.next.prior = p
- p.prior = curNode
- curNode.next = p
- 双链表节点的删除:
- p = curNode.next
- curNode.next = p.next
- p.next.prior = curNode
- del p
- 链表总结:
- 插入元素的时间复杂度 O(1)
- 删除元素的时间复杂度 O(1)
- 修改元素的时间复杂度 O(n)
- 查看元素的时间复杂度 O(n)
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_tail([1,2,3,6,8]) print_linklist(lk)
#单链表 class Node(object): def __init__(self, item): self.item = item self.next = None #双链表 class Node(object): def __init__(self, item=None): self.item = item self.next = None self.prior = None
五、哈希表
- 内容:
- 哈希表(Hash Table,又称为散列表),是一种线性表的存储结构。哈希表由一个直接寻指表(顺序表数组)和一个哈希函数组成。哈希函数h(k)将元素k作为自变量,返回元素的存储下标。
- 例如:集合{1,6,7,9},假设哈希函数h(k)使得h(1) = 0,h(6)=2,h(7)=4,h(9)=5,那么哈希表被存储为[1,None,6,None,7,9],当我们查找元素6所在的位置 时,通过哈希函数获得该元素所在的下标,因此在2的位置可以找到该元素
- 集合和字典是基于哈希表来对元素进行查找
- 简单哈希函数:
- 除法哈希:h(k) = k mod m
- 乘法哈希:h(k) = floor(m(kA mod 1)) 0<A<1
- 哈希冲突:
- 由于哈希表的大小是有限的,而要存储的值的总数量是无限的,因此对于任何哈希函数,都会出现两个不同元素映射到同一个位置上的情况,这种情况叫做哈希冲突。 比如h(k)=k%7, h(0)=h(7)=h(14)=...
- 解决哈希冲突:开放寻址法
- 开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
- 线性探查:如果位置i被占用,则探查i+1, i+2,……
- 二次探查:如果位置i被占用,则探查i+12,i-12,i+22,i-22,……
- 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2,h3,……
- 解决哈希冲突:拉链法
- 拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后。
- 哈希表在Python中的应用
- 字典与集合都是通过哈希表来实现的
- 在Python中的字典: a = {'name': 'ctz', 'age': 18, 'gender': 'Man'}
- 使用哈希表存储字典,通过哈希函数将字典的键映射为下标。假设h(‘name’) = 3, h(‘age’) = 1, h(‘gender’) = 4,则哈希表存储为[None, 18, None, ’ctz’, ‘Man’]
- 在字典键值对数量不多的情况下,几乎不会发生哈希冲突,此时查找一个元素的时间复杂度为O(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))
六、树
- 内容:
- 树是一种数据结构 比如:目录结构
- 树是一种可以递归定义的数据结构 树是由n个节点组成的集合:
- 如果n=0,那这是一棵空树;
- 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
- 一些概念:
- 根节点
- 叶子节点
- 树的深度(高度)
- 树的度
- 孩子节点/父节点
- 子树
1、二叉树
- 度不超过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
二叉树的遍历:前序遍历 中序遍历 后序遍历 层次遍历
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 a.rchild = c c.lchild = b c.rchild = d g.rchild = f root = e #前序遍历 def pre_order(root): if root: print(root.data, end='') pre_order(root.lchild) pre_order(root.rchild) #EACBDGF #中序遍历 def in_order(root): if root: in_order(root.lchild)#a print(root.data, end='') in_order(root.rchild) #ABCDEGF #后序遍历 def post_order(root): if root: post_order(root.lchild) post_order(root.rchild) print(root.data, end='') #BDCAFGE 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) #EAGCFBD pre_order(root) print("") in_order(root) print("") post_order(root) print("") level_order(root) ''' EACBDGF ABCDEGF BDCAFGE EAGCFBD '''
二叉搜索树
二叉搜索树:是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key ≤ x.key;如果y是x右子树的一个节点,那么y.key ≥ x.key.
AVL树:
AVL树是一棵自平衡的二叉搜索树。
AVL树具有以下性质:
- 根的左右子树的高度之差的绝对值不能超过1
- 根的左右子树都是平衡二叉树
AVL的实现方式:
- 旋转
B树(B-Tree)
B树是一棵自平衡的多路搜索树。常用于数据库的索引。
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): # "/var/python/" 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/") tree.cd("bin/") tree.mkdir("python/") tree.cd("../") print(tree.ls())
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 a.rchild = c c.lchild = b c.rchild = d g.rchild = f 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) level_order(root)
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是它父亲的左孩子 node.parent.lchild = None else: #右孩子 node.parent.rchild = None def __remove_node_21(self, node): # 情况2.1: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 def __remove_node_22(self, node): # 情况2.2:node只有一个右孩子 if not node.parent: self.root = node.rchild 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) # # # 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)
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: p.bf = 0 c.bf = 0 return g def insert_no_rec(self, val): # 1. 和BST一样,插入 p = self.root if not p: # 空树 self.root = AVLNode(val) return while True: if val < p.data: if p.lchild: p = p.lchild else: # 左孩子不存在 p.lchild = AVLNode(val) p.lchild.parent = p node = p.lchild # node 存储的就是插入的节点 break elif val > p.data: if p.rchild: p = p.rchild else: p.rchild = AVLNode(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)
迷宫问题
给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。
方法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 四个方向 x-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:使用队列
思路:从一个节点开始寻找,寻找下面能继续走的点,继续寻找直到能找出出口
方法:创建一个空队列,将起点位置入队,在队列不空时循环,出队一次,如果相邻的位置为出口,则结束.否则找出4个相邻方块中可走的方块,全部入队,体现广度优先的思想
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.pop() 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((nextNode[0], nextNode[1], len(path) - 1)) # 后续节点进队,记录哪个节点带他来的 maze[nextNode[0]][nextNode[1]] = 2 # 标记为已经走过 else: print("没有路") return False maze_path_queue(1, 1, 8, 8)
约瑟夫问题
n个人围成一个圈,每个人分别标注为1、2、...、n,要求从1号从1开始报数,报到k的人出圈,接着下一个人又从1开始报数,如此循环,直到只剩最后一个人时,该人即为胜利者。例如当n=10,k=4时,依次出列的人分别为4、8、2、7、3、10,9、1、6、5,则5号位置的人为胜利者。给定n个人,请你编程计算出最后胜利者标号数。
- 时间复杂度: 第1种列表法的时间复杂度是O(n2).第2种链表法的时间复杂度是O(nm)
- 如果n大于m时,链表法优于列表法,n小于m时,列表法优于链表法
#n表示总人数,m表示报到的数 def yuesefu_1(n,m): #1.将所有元素放进列表中,并定义初始的下标为0 people = [i for i in range(1,n+1)] x = 0 #2.在列表不空的时候循环 while len(people) > 0: #3.计算报数的人的下标, # 1,2,3,4,5,6,7,8 报数 # 0,1,2,3,4,5,6,7 下标,每次取出对总人数的余数就是要找的人 dead_location = (x+(m-1))%len(people) yield people.pop(dead_location) #将找到的人移除出列表 x = dead_location #从移除出去的人的位置上,继续执行 print(list(yuesefu_1(9,4)))
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 def __iter__(self): return self def __init__(self,iteratbe=None): self.head = LinkList.Node(0) self.tail = self.head self.extend(iteratbe) #链表添加 def append(self,obj): s = LinkList.Node(obj) self.tail.next = s self.tail = s #链表扩展 def extend(self,iterable): for obj in iterable: self.append(obj) self.head.item += len(iterable) def remove_nth_node(self,node,m): #删除链表第n个元素 for i in range(m-2): node = node.next p = node.next node.next = p.next self.head.item -= 1 return p def __iter__(self): return self.LinkListIterator(self.head.next) def __len__(self): return self.head.item def __str__(self): return '<<'+", ".join(map(str,self)) +">>" def yuesefu_link(n,m): people = LinkList([i for i in range(1,n+1)]) people.tail.next = people.head.next x = people.head.next while len(people)>0: p = people.remove_nth_node(x,m) x = p.next yield p.item print(list(yuesefu_link(9,4)))