数据结构(列表 栈 队列 )

数据结构

 

数据结构是指相互之间存在着一种或多种关系的数据元素的集合和该集合中数据元素之间的关系组成。

简单来说,数据结构就是设计数据以何种方式组织并存储在计算机中。

比如:列表、集合与字典等都是一种数据结构。

N.Wirth: “程序=数据结构+算法”

 

数据结构按照其逻辑结构可分为线性结构、树结构、图结构

  线性结构:数据结构中的元素存在一对一的相互关系

  树结构:数据结构中的元素存在一对多的相互关系

  图结构:数据结构中的元素存在多对多的相互关系

 

列表

(其他语言称数组)是一种基本数据类型。

  关于列表的问题: 列表中的元素是如何存储的?(顺序存储 一块连续内存)

  列表的基本操作:按下标查找、插入元素、删除元素…… 这些操作的时间复杂度是多少?O(n)

  扩展:Python的列表是如何实现的?

    列表存的是数据内存的地址  

    数组和列表的两点不同:1、数组元素类型要相同 2、数组长度固定

 

栈(Stack)

是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表。

栈的特点:后进先出 LIFO(last-in, first-out)

栈的概念:栈顶、栈底

栈的基本操作: 进栈(压栈):push  出栈:pop  取栈顶:gettop

使用一般的 列表 结构即可实现栈:

  进栈:li.append

  出栈:li.pop

  取栈顶:li[-1]

 

栈的应用——括号匹配问题

  括号匹配问题:给一个字符串,其中包含小括号、中括号、大括号,求该字符串中的括号是否匹配。

  例如:

    ()()[]{}   匹配  

    ([{()}])   匹配 (左括号进栈 碰到右括号 如果是对应的就出栈 如果最终全部出栈 就匹配)

    [](      不匹配 

    [(])     不匹配

栈的应用——函数调用堆栈

  Python的函数调用堆栈 栈与递归

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 == Maxsize + 1时,再前进一个位置就自动到0.

  队首指针前进1:front = (front + 1) % MaxSize   取余

  队尾指针前进1:rear = (rear + 1) % MaxSize

  队空条件:rear == front

  队满条件:(rear + 1) % MaxSize == front

 

 

自己写的队列:

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) # 队列大小是5
for i in range(4):
    q.push(i)
print(q.pop())
q.push(4)

 

python队列内置模块

使用方法 from collections import deque

  创建队列:queue = deque()

  进队:append()

  出队:popleft()

  双向队列队首进队:appendleft()

  双向队列队尾出队:pop() 

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): # 把文件的最后n行数据读取
    with open('test.txt', 'r') as f:
        q = deque(f, n)
        return q # 返回列表

for line in tail(5):
    print(line, end='')

 

 

栈——深度优先搜索(一条道走到黑 不走回头路)

回溯法

思路:从一个节点开始,任意找下一个能走的点(上右下左的顺序 不走回头路),当找不到能走的点时,退回上一个点(出栈)寻找是否有其他方向的点。

使用栈存储当前路径(没有路出栈 直到有走的路径继续进栈)

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 24/12/2017
# 迷宫 1是墙0是路
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): # x1,y1 起点位置 x2,y2 终点位置
    stack = [] # 空栈
    stack.append((x1, y1))
    while(len(stack)>0):# 栈空了就没有路可走死胡同了
        curNode = stack[-1] # 当前的节点(栈顶) curNode长度是2的元组 0是x 1是y
        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)

 

队列——广度优先搜索

思路:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口。

使用队列存储当前正在考虑的节点

 

 

下一步能走的路径进队(一个点出队,这个点后面能走的点进队),走过了就出队

(开始1进队,2进队1出队,3进队2出队。)

(4、5进队3出队 ,5出队7进队6在队里,6出队8、9进队7在队)

 

如何输出路径(倒着找 从终点开始开他是由那个点出队进来的)所以需要一个额外的列表

 

 

 

 比如说7是终点找他是由谁带进队的,从1开始1是0号位前面没有值,2是由0号位的1带进队的,3是由1号位的2带进队的,类推7是由4号位的5带进队的 所以路径就是1 2 3 5 7

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# Date: 24/12/2017

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):

    realpath = []  # 存真实的路径
    i = len(path) - 1 # 当前节点位置(终点位置) realpath的最后一个位置下标
    while i >= 0: # i>0 表示i是真实路径上的节点
        # path[i] =(x,y,n) xy是坐标 n是此节点位置下表
        realpath.append(path[i][0:2]) # path[i][0:2] 当前节点坐标 也就是取的0到1(x,y)
        i = path[i][2] # path[i]是当前节点 [2]是带i进来的上一个节点的下标
    realpath.reverse()
    for node in realpath:
        print(node)


def maze_path_queue(x1, y1, x2, y2):  # 起点终点坐标
    queue = deque()  # 定义队列
    queue.append((x1, y1, -1))  # 初始先把起点坐标放进去 -1或None表示没人带它进来
    path = []  # 存储是谁带它进来的
    while len(queue) > 0:  # 如果队空 说明没有通路
        curNode = queue.popleft()  # 当前节点 appended是从右边加进去 popleft从左边出
        path.append(curNode)  # 出队的节点需要加到path里
        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))  # 后续节点进队,记录哪个节点带他来的, len(path) - 1表示上一个带此节点进来的节点位置的下标
                maze[nextNode[0]][nextNode[1]] = 2  # 标记为已经走过
    else:  # 队列空了 就没有路可以走了
        print("没有路")
        return False


maze_path_queue(1, 1, 8, 8)

  

 

posted @ 2022-05-25 09:40  贰号猿  阅读(94)  评论(0)    收藏  举报