数据结构 栈的应用 队列 链表 双向链表 哈希表

栈 stack

栈的出队序列

  • n个有序元素的出栈序列个数:

  • 卡特兰数

    # ABC
    
    # CBA
    # ABC
    # ACB
    # BAC
    # BCA
    # CAB
    
    # ABCDE
    
    # ACBDE  AEDCB  ADBCE  ABDCE
    

栈处理括号匹配 (brace match)

  • "{{[]}({}){[][]}{[]}([({})]([{[]}]))}"
def brace_match(s):
    stack = []
    brace_dict = {"(": ")", "[": "]", "{": "}"}
    for ch in s:
        # 添加左括号
        if ch in brace_dict.keys():
            stack.append(ch)
        # 匹配右括号 
        elif ch in brace_dict.values():
            # 判断栈的最后一个 是否跟 当前的反括号 匹配
            if brace_dict[stack[-1]] == ch:
                stack.pop()
            else:
                return False
    if stack:
        return False
    else:
        return True
    
print(brace_match(s))

栈的应用 迷宫问题

  • 给一个二维列表,表示迷宫(0表示通道,1表示围墙)。给出算法,求一条走出迷宫的路径。
  • 思路:
    • 在一个迷宫节点(x,y)上,可以进行四个方向的探查:maze[x-1][y], maze[x+1][y], maze[x][y-1], maze[x][y+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]
]

DES ( 使用栈解决 )

# 起点 (1, 1) 终点 (8, 8)
dirs = [
    # x 为纵向坐标  y 横向坐标
    lambda x, y: (x - 1, y),  # 上
    lambda x, y: (x, y + 1),  # 右
    lambda x, y: (x + 1, y),  # 下
    lambda x, y: (x, y - 1),  # 左
]

def solve_maze_DFS(maze, x, y, x1, y1):
    stack = []  # 定义栈
    stack.append((x, y))
    maze[x][y] = 2  # 标记走过的路
    while stack:
        cur_node = stack[-1]
        if cur_node == (x1, y1):
            print(maze)
            return True

        for dir in dirs:
            next_x, next_y = dir(*cur_node)
            if maze[next_x][next_y] == 0:
                stack.append((next_x, next_y))
                maze[next_x][next_y] = 2
                break
        else:
            err_x, err_y = stack.pop()
            maze[err_x][err_y] = 3
    return False


# solve_maze_DFS(maze, 1, 1, 8, 8)
# 找到的路径 2 走错的 路径 3
o = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
     [1, 2, 2, 1, 3, 3, 3, 1, 3, 1],
     [1, 3, 2, 1, 3, 3, 3, 1, 3, 1],
     [1, 2, 2, 3, 3, 1, 1, 2, 2, 1],
     [1, 2, 1, 1, 1, 2, 2, 2, 2, 1],
     [1, 2, 2, 2, 1, 2, 0, 0, 2, 1],
     [1, 0, 1, 2, 2, 2, 1, 0, 2, 1],
     [1, 0, 1, 1, 1, 0, 1, 1, 2, 1],
     [1, 1, 0, 0, 0, 0, 0, 0, 2, 1],
     [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

BFS ( 使用队列解决 )
def solve_maze_BfS(maze, x, y, x1, y1):
    d = deque()
    path = []
    # 将起点加入到队列中
    d.append((x, y, -1))
    maze[x][y] = 2
    while d:
        cur_code = d.popleft()
        path.append(cur_code)
        if cur_code[:2] == (x1, y1):
            p = len(path) - 1
            over_path = []
            # p = cur_code[-1]
            # over_path = [cur_code[:2]]
            while p >= 0:
                over_path.append(path[p][:2])
                p = path[p][-1]
            over_path.reverse()
            print(over_path)
            return True
        for dir in dirs:
            next_x, next_y = dir(cur_code[0], cur_code[1])
            if maze[next_x][next_y] == 0:
                d.append((next_x, next_y, len(path) - 1))
                maze[next_x][next_y] = 2
    return False


solve_maze_BfS(maze, 1, 1, 8, 8)

队列:

  • 先进先出

    from collections import deque  # 生产者消费者模型的 队列
    
    q = deque()
    q.append(1)
    q.append(2)
    
    print(q.popleft())  # 1
    print(q.popleft())  # 2
    print(q.popleft())  # 报错
    
  • 使用队列实现 Linux 的 headtail 命令

    from collections import deque
    print(list(deque(open('abc.txt', 'r', encoding='utf-8'), 5)))
    
    q = deque([1,2,3,4,5], 3)
    print(list(q))
    

如何用两个栈实现一个队列?

#1 2 3 4 5

# Stack 1:
# Stack 2: 4 3 2 1

# 进队: 进栈1; 出队:出栈2;如果栈2空,就将栈1一次出栈并进到栈2,在从栈2出栈一次
class Queue(object):

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def push(self, item):
        self.stack1.append(item)

    def pop(self):
        if self.stack2:
            return self.stack2.pop()
        elif self.stack1:
            while len(self.stack1):
                self.stack2.append(self.stack1.pop())
            return self.stack2.pop()
        else:
            raise ConnectionError("empty queue")

q = Queue()
q.push(1)
q.push(2)
print(q.pop())
q.push(13)
print(q.pop())
print(q.pop())
print(q.pop())

链表:

  • 创建链表的方式:
    1. 头插法
    2. 尾插法
class LinkList(object):
    def __init__(self, data=None):
        self.data = data
        # 元素的 指向
        self.next = None


# a = LinkList('11')
# b = LinkList('33')
# c = LinkList('33')
# b.next = a
# c.next = b

def create_link(li):
    """ 头插法 每一次都从头部开始往后插  """ 
    head = LinkList()  # 创建链表的头部
    for val in li:  # 
        v = LinkList(val)  # 将列表的值 创建为链表的值 
        v.next = head.next  # 创建的量表对象的 下一个节点指向  链表的头部的下一个指向内容 1 > 3 >  2
        head.next = v  # 重新定义头部的 下一个指向
    return head


def create_link_tail(li):
    """ 尾插法 """
    head = LinkList()  # 创建链表 头部
    tail = head  # 定义初始尾部
    for var in li:  
        v = LinkList(var)  
        tail.next = v  # 定义链表最后的 下一个节点的指向为新创建的 对象
        tail = v  # 重新定义 尾部
    return head

# 正向遍历
def look_link_head(head):
    h = head.next  # 头部的下一个内容 
    while h:  # 如果下一个内容有值 就继续
        yield h.data  # 返回 下一个内容的 值
        h = h.next  # 重新定义下一个内容


head = create_link_tail([1, 2, 3, 4, 5, 6, 7])
for i in look_link_head(head):
    print(i)

双向链表

  • 双链表中每个节点有两个指针:一个指向后面节点、一个指向前面节点
"""
双向链表 创建
"""


class Node(object):

    def __init__(self, data=None):
        self.data = data
        self.next = None
        self.prior = None


def create_link_list(li):
    head = Node()  # 链表的 头
    tail = head  # 下一个要插入的 内容

    for val in li:
        v = Node(val)
        tail.next = v
        v.prior = tail
        tail = v
    return head, tail


# 正向遍历
def look_link_head(head):
    h = head.next  # 头部的下一个内容 
    while h:  # 如果下一个内容有值 就继续
        yield h.data  # 返回 下一个内容的 值
        h = h.next  # 重新定义下一个内容


# 反向遍历
def look_link_tail(tail):
    t = tail
    while t.prior:  # 不打印 头部最后的 None
        yield t.data
        t = t.prior


head, tail = create_link_list([1, 2, 3, 4, 5, 6, 7])
for i in look_link_head(head):
    print(i)
for i in look_link_tail(tail):
    print(i)

列表与链表 复杂度

  • 时间复杂度

    • 按元素值查找 O(n) O(n)
    • 按下标查找 O(1) O(n)
    • 在某元素后插入 O(n) O(1)
    • 删除某元素 O(n) O(1)
  • 链表在插入和删除的操作上明显快于顺序表

  • 链表的内存可以更灵活的分配

    • 试利用链表重新实现栈和队列
  • 链表这种链式存储的数据结构对树和图的结构有很大的启发性

哈希表:

  • 哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
    • ninsert(key, value):插入键值对(key,value
    • nget(key):如果存在键为key的键值对则返回其value,否则返回空
    • ndelete(key):删除键为key的键值对

解决哈希冲突——开放寻址法

  • 开放寻址法:如果哈希函数返回的位置已经有值,则可以向后探查新的位置来存储这个值。
    • 线性探查:如果位置i被占用,则探查i+1, i+2,……
    • 二次探查:如果位置i被占用,则探查i+12,i-12,i+22,i-22,……
    • 二度哈希:有n个哈希函数,当使用第1个哈希函数h1发生冲突时,则尝试使用h2,h3,……
posted @ 2019-04-22 12:57  拐弯  阅读(403)  评论(0编辑  收藏  举报