python算法笔记——链表与队列

链表

链表是一种在物理上非连续,非顺序的数据结构,由若干个节点(node)组成。单向链表的每一个节点包含两部分,一部分存放数据data,另一部分是指向下一个节点的指针next。链表的第一个节点成为头节点,最后一个节点称为尾节点。尾节点的指针指向为空。而双向列表,就是比单项列表稍微复杂一点,它的每个节点除了有data和next指针外,还有指向前置节点的prev指针。上次我们说到的数组是在内存中顺序存储的,而链表在内存中是随机存储的。

增删改查

查找元素时,链表不能像数组那样通过下标快速定位,只能从头节点逐一查找。这样链表的时间复杂度就是O(n)。
不考虑查找节点的过程,链表的更新也非常容易,像数组一样使用新数据替换旧数据即可。
插入节点,链表的插入操作分为3种情况,尾部插入,头部插入,中间插入。每次只是改动指针的位置便可。具体操作后面代码中会比较清晰。
删除操作,与插入类似,改变指针的指向即可。

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None
class LinkedList:
    def __init__(self):
        self.size = 0
        self.head = None
        self.last = None

    def get(self, index):
        if index < 0 or index >= self.size:
            raise Exception("超出链表节点范围!")
        p = self.head
        for i in range(index):
            p = p.next
        return p

    def insert(self, data, index):
        if index < 0 or index > self.size:
            raise Exception("超出链表节点范围!")
        node = Node(data)
        if self.size == 0:
            # 空链表
            self.head = node
            self.last = node
        elif index == 0:
            # 插入头部
            node.next = self.head
            self.head = node
        elif self.size == index:
            # 插入尾部
            self.last.next = node
            self.last = node
        else:
            # 插入中间
            prev_node = self.get(index-1)
            node.next = prev_node.next
            prev_node.next = node
        self.size += 1

    def remove(self, index):
        if index < 0 or index >= self.size:
            raise Exception("超出链表节点范围!")
        # 暂存被删除的节点,用于返回
        if index == 0:
            # 删除头节点
            removed_node = self.head
            self.head = self.head.next
        elif index == self.size - 1:
            # 删除尾节点
            prev_node = self.get(index-1)
            removed_node = prev_node.next
            prev_node.next = None
            self.last = prev_node
        else:
            # 删除中间节点
            prev_node = self.get(index-1)
            next_node = prev_node.next.next
            removed_node = prev_node.next
            prev_node.next = next_node
        self.size -= 1
        return removed_node
    def output(self):
        p = self.head
        while p is not None:
            print(p.data)
            p = p.next


队列

栈和队列也是常见的线性数据结构,其实都比较简单。栈是先进先出,队列是后进后出。举个例子,我们在网上冲浪时,不断地打开网页,然后不断得返回上一个网页,最近打开的也是最先被返回的网页,这就是典型的栈的结构。最早进入的元素存放的位置叫栈底,最后放入的元素存放的位置叫栈顶。与栈正好相反,队列是前进先出,就像马路上的车辆,先到达的也先离开,没有超车的情况。队列的出口短叫队头,队列的入口端叫队尾。
所以队列的增加删除都只是对队头或者队尾进行操作。入队就是在队尾加入新元素,新元素的下一个位置就会称为新的队尾。出队就是将队头元素移出,原先队头的下一个位置就会称为新的队头。

循环队列

为了使内存能够得到充分利用。在物理存储上,队尾的位置可以在队头之前。当再有元素入队,队尾指针继续后移即可。
当(队尾下标+1)%数组长度 = 队头下标时,代表这个队列已经满了。这里需要注意一下,队尾指针指向的位置要空出一位,所以队列最大容量比数组长度小1。

python队列实现

class MyQueue:
    def __init__(self, capacity):
        self.list = [None] * capacity
        self.front = 0
        self.rear = 0

    def enqueue(self, element):
        if (self.rear+1) % len(self.list) == self.front:
            raise Exception("队列已满 !")
        self.list[self.rear] = element
        self.rear = (self.rear+1) % len(self.list)

    def dequeue(self):
        if self.rear == self.front:
            raise Exception("队列为空 !")
        dequeue_element = self.list[self.front]
        self.front = (self.front+1) % len(self.list)
        return dequeue_element

    def output(self):
        i = self.front
        while i != self.rear:
            print(self.list[i])
            i = (i+1) % len(self.list)

双端队列与优先队列

双端队列其实是将栈和队列的特点结合起来,既可以先入先出,也可以先入后出。也就是说队头可以入队出队,队尾也可以入队出队。细节这里不再赘述。优先队列其实是基于二叉堆来实现的,遵循的不是先入先出,而是谁的优先级最高,谁先出队。

发布了30 篇原创文章 · 获赞 16 · 访问量 1145

猜你喜欢

转载自blog.csdn.net/qq_34523665/article/details/105609708