数据结构与算法python(三)基本数据结构

3.0线性数据结构

3.0.1定义:对于一个数据集合,如果某个元素被添加进来之后,它与其他元素的相对位置没有发生改变,则称为线性数据结构;

3.0.2常见线性数据结构:栈,队列,双端队列,列表。

3.1栈的定义及其他

3.1.1栈是一种添加操作和移除操作都发生在同一端(顶端)有序集合;

3.1.2LIFO:栈满足的排序原则,也就是后进先出:最新添加的元素将被最先移除;

3.1.3栈的python实现

#栈的实现
class Stack:

    def __init__(self):
         self.items = []

    def isEmpty(self):#栈空否?
        return self.items == []

    def push(self, object):#栈添加
        self.items.append(object)

    def pop(self):#弹出
        return self.items.pop()

    def peek(self):#返回端口元素
        return self.items[-1]

    def size(self):#返回大小
        return len(self.items)
f1 = Stack()
print(f1.isEmpty())
f1.push(1)
f1.push(2)
f1.push(3)
f1.push(4)
print(f1.items)
print(f1.peek())
print(f1.pop())
print(f1.items)
print(f1.size())
print(f1.isEmpty())

结果:

True
[1, 2, 3, 4]
4
4
[1, 2, 3]
3
False

3.2与栈有关的问题举例

3.2.1括号匹配问题

括号匹配是指每一个左括号都有与之对应的一个右括号,并且括号对有正确的嵌套关系。

from pythonds.basic import Stack

def parChecker(symbolString):
    s1 = Stack()
    flag = True
    index = 0
    while index < len(symbolString) and flag:
        symbol = symbolString[index]
        if symbol == "(":
            s1.push(symbol)
        else:
            if s1.isEmpty():#(()))排除这种右括号多的情况
                flag = False
            else:
                s1.pop()

        index = index + 1

    if flag and s1.isEmpty():#左括号不存在则为匹配
        return True
    else:
        return False

print(parChecker("(()))))"))
print(parChecker("()()))(("))

结果:

False
False

3.2.2符号匹配问题

leetcode有效的括号icon-default.png?t=N7T8https://leetcode.cn/problems/valid-parentheses/description/以下代码并不适用于leetcode题目哦

from pythonds.basic import Stack

def parChecker(symbolString):
    s1 = Stack()
    flag = True
    index = 0
    while index < len(symbolString) and flag:
        symbol = symbolString[index]
        if symbol in "({[":
            s1.push(symbol)
        else:
            if s1.isEmpty():#(()))排除这种右括号多的情况
                flag = False
            else:
                top = s1.pop()
                if not matches(top,symbol):
                    flag = False

        index = index + 1

    if flag and s1.isEmpty():#左括号不存在则为匹配
        return True
    else:
        return False
def matches(open,close):
    opens = "{(["
    closes = "})]"
    return opens.index(open) == closes.index(close)

print(parChecker("{}[]()[{}]"))
print(parChecker("[[[]]{{{}}(((})))]"))

结果:

True
False

分析:

这段代码定义了两个函数:`parChecker` 和 `matches`。它们的作用是对一个包含括号、花括号和方括号的字符串 `symbolString` 检查其是否有效配对。主要思路是利用栈的数据结构来跟踪打开的括号。以下是详细解释:

1. `parChecker` 函数:
   - 初始化一个空栈 `s1` 和标志变量 `flag` 设定为 `True`,代表默认认为字符串有效。
   - 遍历输入字符串 `symbolString` 的每个字符,用索引 `index` 作为迭代器:
     - 如果遇到开括号(如 `{`, `[`, 或 `(`),则推入栈 `s1`。
     - 如果遇到闭合括号,检查栈是否为空:
       - 若为空,意味着缺少对应的开括号,`flag` 设置为 `False`。
       - 否则,弹出栈顶的开括号,通过 `matches` 函数比较是否匹配当前闭合括号。
         - 如果 `matches` 返回 `False`,则字符串无效,`flag` 设置为 `False`。
   
   - 遍历结束后,如果字符串有效(`flag` 仍为 `True`)并且栈也为空(说明所有的开括号都有相应的闭合括号),返回 `True`;否则返回 `False`。

2. `matches` 函数:
   - 判断给定的开括号 `open` 是否有与其相配对的闭合括号。它通过比较 `opens` 和 `closes` 中对应位置的括号是否相同来确定。

最后,示例调用了这两个函数并打印结果,分别验证了两段字符串的有效性。

3.2.3十进制转换为二进制问题

from pythonds.basic import Stack
def divideBy2(decNumber):
    remstack = Stack()

    while decNumber > 0:
        rem = decNumber % 2
        remstack.push(rem)
        decNumber = decNumber // 2#整除

    binString = ""
    while not remstack.isEmpty():
        binString = binString + str(remstack.pop())

    return binString

3.2.4十进制转换为任意进制问题

from pythonds.basic import Stack
def baseConverter(decNumber,base):
    digits = "0123456789ABCDEF"

    remstack = Stack()

    while decNumber > 0:
        rem = decNumber % base
        remstack.push(rem)
        decNumber = decNumber // base

    newString = ""
    while not remstack.isEmpty():
        newString = newString + digits[remstack.pop()]

    return newString

这段Python代码定义了一个名为 `baseConverter` 的函数,用于将十进制数(decNumber)转换成任意基数(base)的表示法。它使用了栈(Stack)数据结构来辅助计算。步骤如下:

1. 定义一个字符数组 `digits`,包含从0到15的十六进制数字,因为通常base <= 16。
2. 创建一个空的栈 `remstack`,用于存储余数(decimal number除以base后的余数)。
3. 当原始十进制数 `decNumber` 大于0时,继续进行以下操作:
   a. 计算 `decNumber` 除以 `base` 的余数,并将其压入栈中。
   b. 更新 `decNumber` 为商,即整除 `base` 后的结果。
4. 当余数栈不再为空时(说明已经处理完所有位),开始弹出栈顶元素,对应的是高位的数字,并拼接到新的字符串 `newString` 之前。
5. 最终返回转换后的字符串 `newString`,它是从高位到低位按顺序组成的。

举个例子,如果你传入 `decNumber = 13` 和 `base = 2`,这个函数将返回 "1101" (二进制表示),因为 13 转换成二进制就是 1101。

3.3队列的定义及其他

3.3.1队列是指添加发生在尾部,移除发生在头部的有序集合。即:新元素通过添加从尾部进入,再通过移除一步一步移动到头部,直到成为被移除的元素。

3.3.2FIFO:先进先出

3.3.3python实现队列

class Queue:
    def __init__(self):
        self.queue = []

    def enqueue(self,item): #发生在尾部的添加操作
        self.queue.insert(0,item)

    def dequeue(self):      #发生在头部的移除操作
        return self.queue.pop()

    def isEmpty(self):
        return self.queue == []

    def size(self):
        return len(self.queue)

3.4与队列有关的问题举例

3.4.1传土豆问题

假设有六个人围成一个圈,先从一号开始传递,1>2>3>4>5>6>1······

from pythonds.basic import Queue
def hotPotato(namelist,num):
    simqueue = Queue()
    for name in namelist:#初始化
        simqueue.enqueue(name)

    while simqueue.size() > 1:
        for i in range(num):#头部删除,尾部又添加,形成一个环
            simqueue.enqueue(simqueue.dequeue())

        simqueue.dequeue()

    return simqueue.dequeue()

这段Python代码实现了一个名为 "hotPotato" 的游戏模拟。这个游戏通常涉及一个土豆(在这里作为字符串名称)和一组玩家。游戏规则是这样的:玩家们围成一圈,然后逐次传递土豆,当音乐停止时,手里拿着土豆的人会被淘汰。这个过程会一直持续,直到只剩下一个人为止。

代码首先创建一个空的模拟队列 `simqueue`,并将所有玩家名字 `namelist` 入队。然后进入一个while循环,只要队列的大小大于1(意味着还有超过一个玩家),就按照以下步骤进行:

1. 遍历 `num`(表示传球次数)次,每次都从队头删除一个元素(模拟传球)并将其添加回队尾,形成一个闭环。这样做使得最后一个放入队列的人成为下一个被淘汰者。
2. 循环结束后,由于 `num` 次传递后只剩下了最后一个人,所以再次从队头删除(此时队列只有一个元素)并返回,这就是最终的赢家。

整个过程模拟了一轮又一轮的淘汰,直到只剩一人为止。

3.4.2打印机进程

import random
from pythonds.basic import Queue
class Printer:#模拟打印机
    def __init__(self,ppm):
        self.pagerate = ppm#定义打印机每秒可以打印的页数
        self.currentTask = None
        self.timeRemaining = 0

    def tick(self):
        if self.currentTask != None:
            self.timeRemaining = self.timeRemaining -1
            if self.timeRemaining <= 0:
                self.currentTask = None

    def busy(self):
        if self.currentTask != None:
            return True
        else:
            return False

    def startNext(self,newtask):
        self.currentTask = newtask
        self.timeRemaining = newtask.getPages() * 60 /self.pagerate

class Task:#模拟单个打印任务
    def __init__(self,time):
        self.timestamp = time#记录单个打印任务的时间戳
        self.pages = random.randrange(1,21)#对产生的任务初始化随机页数

    def getStamp(self):
        return self.timestamp

    def getPages(self):
        return self.timestamp

    def waitTime(self,currenttime):
        return currenttime - self.timestamp

#主程序函数
def simulation(numSeconds,pagesPerMinute):

    labprinter = Printer(pagesPerMinute)#初始化打印机,定义每秒打印速度
    printQueue = Queue()#初始化打印队列
    waitingtimes = []#初始化等待时间列表,方便后续使用

    for currentSecond in range(numSeconds):#模拟每秒钟的情况

        if newPrintTask():#若调用newPrintTask返回True,即该秒下有新任务产生,执行if条件语句
            task = Task(currentSecond)
            printQueue.enqueue(task)#将该秒产生的任务导入打印队列

        if(not labprinter.busy()) and (not printQueue.isEmpty()):#当打印机没有打印任务时 且 打印队列非空时执行if条件语句 也就是把打印队列里的打印任务压入打印机执行
            nexttask = printQueue.dequeue()#弹出打印队列头部任务
            waitingtimes.append(nexttask.waitTime(currentSecond))#
            labprinter.startNext(nexttask)

        labprinter.tick()

    averageWait = sum(waitingtimes) / len(waitingtimes)
    print("Average Wait {:.2f} seconds {} tasks remaining".format(averageWait,printQueue.size()))


def newPrintTask():#每秒钟是否产生新任务,如果产生,num == 180
    num = random.randrange(1,181)
    if num == 180:
        return True
    else:
        return False

for i in range(10):
    simulation(3600,10)

ai分析:

这个Python代码片段定义了一个简单的打印任务调度系统。它主要包括两个类:`Printer` 和 `Task`。`Printer` 类模拟了打印机的行为,包括打印速率、任务处理时间和状态。`Task` 类则代表打印任务,包含任务开始时间、页面数量以及计算等待时间的功能。

`simulation` 函数是主程序,接受两个参数:总运行秒数和每分钟的打印页数。函数通过循环模拟每秒的情况:

1. 检查是否有新的打印任务(由 `newPrintTask()` 决定),如果有,则创建一个新的 `Task` 并加入打印队列。
2. 如果打印机当前没有任务并且打印队列非空,从队列中取出第一个任务给打印机,并记录等待时间。
3. 每秒递减当前任务剩余时间,直到完成。
4. 统计所有任务的平均等待时间并报告剩余的任务数量。

`newPrintTask` 函数会随机生成1到180之间的整数,180表示这一秒会产生新的打印任务。

在这个场景中,循环运行 `simulation` 函数10次,每次运行3600秒,以此进行多次模拟测试。

3.5双端队列的定义及其他

3.5.1双端队列指:移除与添加操作发生在任何一端都可以的有序集合;

3.5.2双端队列的排序原则不同于栈的先进后出,队列的先进先出原则,具体排序原则根据使用者的需求规定;

3.5.3双端队列在前端发生的操作时间复杂度皆为常数阶,后端发生的操作皆为n阶。(注意:不论是栈、队列或者双端队列,前端都指列表的尾部,后端都指列表的前部

3.5.4双端队列的python实现

class Deque:
    
    def __init__(self):
        self.items = []
        
    def isEmpty(self):
        return self.items == []
    
    def size(self):
        return len(self.items)
    
    def addFront(self,item):
        self.items.append(item)
        
    def addRear(self,item):
        self.items.insert(0,item)
        
    def removeFront(self):
        return self.items.pop()
    
    def removeRear(self):
        return self.items.pop(0)

3.6与双端队列有关的问题举例

3.6.1回文检测器

from pythonds.basic import Deque
def palchecker(aString):
    chardeque = Deque()

    for ch in aString:
        chardeque.addRear(ch)

    stillEqual = True

    while chardeque.size() > 1 and stillEqual :
        first = chardeque.removeFront()
        last = chardeque.removeRear()
        if first != last:
            stillEqual = False

    return stillEqual
print(palchecker("1112x2111"))

3.7列表

3.7.1无序列表

指其中每个元素相对与其他元素都有自己的一个位置,所以并不是真的无序

python本身自带的列表使用顺序结构:即连续的内存地址储存数据。详细操作请参考:python列表操作icon-default.png?t=N7T8https://blog.csdn.net/m0_70885101/article/details/126022872?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172212970416800184196089%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=172212970416800184196089&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-126022872-null-null.142%5Ev100%5Epc_search_result_base3&utm_term=%E5%88%97%E8%A1%A8&spm=1018.2226.3001.4187

而以下两种都是基于链式结构实现

3.7.2利用链表实现无序列表(链式结构)

class Node:#节点类
    def __init__(self,initdata):
        self.data = initdata
        self.next = None#初始化下一节点不指向任何引用

    def getData(self):#访问该节点数据
        return self.data

    def getNext(self):#访问下一个节点数据
        return self.next

    def setData(self,newdata):#重新赋值本节点数据
        self.data = newdata

    def setNext(self,newnext):
        self.next = newnext

class UnorderedList:#链表类,不包含任何节点对象,只包含第一个节点的引用
    def __init__(self):
        self.head = None

    def isEmpty(self):#检查链表是否为空
        return self.head == None

    def add(self,item):#添加的这个元素位于这个链表的首端
        temp = Node(item)
        temp.setNext(self.head)
        self.head = temp

    def length(self):
        current = self.head
        count = 0
        while current != None:
            count += 1
            current = current.getNext()

        return count

    def search(self,item):
        current = self.head
        found = False
        while current != None and not found:
            if current.getData() == item:
                found = True
            else:
                current = current.getNext()
        return found

     def remove(self,item):
        current = self.head
        previous = None
        found = False
        while not found:
            if current.getData() == item:
                found = True
            else:
                previous = current
                current = current.getNext()

        if previous == None:
            self.head = current.getNext()
        else:
            previous.setNext(current.getNext())

注意:有关此部分的重点说明都在注释里。

3.7.3利用链表实现有序列表(链式结构)

class Node:#节点类
    def __init__(self,initdata):
        self.data = initdata
        self.next = None#初始化下一节点不指向任何引用

    def getData(self):#访问该节点数据
        return self.data

    def getNext(self):#访问下一个节点数据
        return self.next

    def setData(self,newdata):#重新赋值本节点数据
        self.data = newdata

    def setNext(self,newnext):
        self.next = newnext

class UnorderedList:#链表类,不包含任何节点对象,只包含第一个节点的引用
    def __init__(self):
        self.head = None

    def isEmpty(self):#检查链表是否为空
        return self.head == None

    def search(self,item):
        current = self.head
        found = False
        stop = False
        while current != None and not found and not stop:
            if current.getData() == item:
                found = True
            else:
                if current.getData() > item:
                    stop = True
                else:
                    current = current.getNext()

        return found

    def add(self,item):
        current = self.head
        previous = None
        stop = False
        while current != None and  not stop:
            if current.getData() > item:
                stop = True
            else:
                previous = current
                current = current.getNext()
                
        temp = Node(item)
        if previous == None:
            temp.setNext(self.head)#注意顺序不能颠倒,否则会失去head
            self.head = temp
        else:
            temp.setNext(current)
            previous.setNext(temp)

注意:以上方式皆为单链表哦

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值