Python全栈(二)数据结构和算法之5.双向链表的实现和栈的实现

一、双向链表的定义、判断是否为空、长度&遍历

1.定义

单向链表只有一个方向,每个节点只有一个后继节点next指向下一个节点;
双向链表支持两个方向,每个节点不止有一个后继指针next指向后面的节点,还有一个前驱指针prev指向前面的节点。
双向链表占内存更大,用空间换时间
当前节点的前一个节点称为前驱结点,后一个节点称为后继节点。
双向链表的示意图
双向链表的示意图

2.is_empty():

def is_empty(self):
    '''判断是否为空'''
    return self.__head == None

3.length():

count = 0开始计时,循环条件和单链表一样,即cur != None

def length(self):
    '''链表长度'''
    #判断是否为空链表
    if self.is_empty():
        return 0
    # cur即游标,指向首节点,用来遍历
    cur = self.__head
    count = 0
    # 当未到达尾部时
    while cur != None:
        count += 1
        # 将游标向后移动一位
        cur = cur.next
    return count

4.travel():

循环条件是cur != None

def travel(self):
    '''遍历链表'''
    #链表为空时
    if self.is_empty():
        return
    cur = self.__head
    while cur != None:
        print(cur.item, end=' ')
        cur = cur.next

二、双向链表的插入

1.add():

新节点的next指向首节点node.next = self.__head
原来头节点指向新节点(实现双向)self.__head.prev = node
__head指向新节点self.__head = node

def add(self, item):
    '''链表头部添加元素'''
    # 创建一个保存数据的节点
    node = Node(item)
    # 判断链表是否为空,若为空,则将__head指向新节点,新节点指向头部
    if self.is_empty():
        self.__head = node
    #若不为空
    else:
        #将node的next指向__head的头节点
        node.next = self.__head
        #将__head的prev指向node
        self.__head.prev = node
        #将__haed指向node
        self.__head = node

2.append()

遍历找到尾节点
循环条件:while cur.next != None
尾节点指向新节点:cur.next = node
新节点的prev指向cur:node.prev = cur

def append(self, item):
    '''链表尾部添加元素'''
    # 创建一个保存数据的节点
    node = Node(item)
    #判断是否为空链表
    if self.is_empty():
        self.__head = node
        node.next = self.__head  # 等价于node.next = node
    else:
        cur = self.__head
        # 找到尾节点
        while cur.next != None:
            cur = cur.next
        # 尾节点指向新节点
        cur.next = node
        #新节点的prev指向cur
        node.prev = cur

3.insert()

双向链表不需要pre指针。
将node的prev指向cur:node.prev = cur
将node的nextcur的下一个节点:node.next = cur.next
将cur的下一个节点的prev指向node:cur.next.prev = node
将cur的下一个节点指向node:cur.next = node

def insert(self, pos, item):
    '''指定位置添加元素'''
    # 若指定位置为第一个位置之前,则指向头部插入
    if pos <= 0:
        self.add(item)
    # 若指定位置超过链表尾部,则执行尾部插入
    elif pos > self.length() - 1:
        self.append(item)
    # 找到指定位置
    else:
        node = Node(item)
        cur = self.__head
        count = 0
        while count < pos - 1:
            count += 1
            cur = cur.next
        #循环结束,找到插入位置的前一个节点。
        #将node的prev指向cur
        node.prev = cur
        #将node的nextcur的下一个节点(与前一步顺序可换)
        node.next = cur.next
        #将cur的下一个节点的prev指向node
        cur.next.prev = node
        #将cur的下一个节点指向node
        cur.next = node

插入操作
插入操作

三、双向链表的查找、删除

1.查找

def search(self, item):
    '''查找节点是否存在'''
    if self.is_empty():
        return False
    cur = self.__head
    while cur != None:
        # 判断第一个节点
        if cur.item == item:
            return True
        # 未找到,继续向后移动
        else:
            cur = cur.next
    return False

2.删除

如果首结点的元素就是要删除的元素:
链表中只有一个节点:self.__head = None
链表中有多个节点:cur.next.prev = None,self.__head = cur.next
如果首结点的元素不是要删除的元素:cur.next.prev = cur.prevcur.prev.next = cur.next

def remove(self, item):
    '''删除节点'''
    #如果链表为空,直接返回
    if self.is_empty():
        return
    else:
        cur = self.__head
        pre = None
        #头节点的元素就是要删除的元素
        if cur.item == item:
            #链表中只有一个节点
            if cur == None:
                self.__head = None
            #有多个节点
            else:
                cur.next.prev = None
                self.__head = cur.next
            return
        #头节点不是要删除的元素
        while cur != None:
            if cur.item == item:
                #删除
                if cur.next:
                    cur.next.prev = cur.prev
                cur.prev.next = cur.next
                break
            cur = cur.next

删除操作
删除操作
整个双向链表的实现如下,

class Node(object):
    '''节点'''
    def __init__(self,item):
        '''初始化'''
        self.item = item
        #next是下一个节点的标识
        self.next = None
        #prev是上一个节点的标识
        self.prev = None

class DoubleLinkList(object):
    '''双向链表'''
    def __init__(self,node = None):
        self.__head = node

    def is_empty(self):
        '''判断是否为空'''
        return self.__head == None

    def length(self):
        '''链表长度'''
        #判断是否为空链表
        if self.is_empty():
            return 0
        # cur即游标,指向首节点,用来遍历
        cur = self.__head
        count = 0
        # 当未到达尾部时
        while cur != None:
            count += 1
            # 将游标向后移动一位
            cur = cur.next
        return count

    def travel(self):
        '''遍历链表'''
        #链表为空时
        if self.is_empty():
            return
        cur = self.__head
        while cur != None:
            print(cur.item, end=' ')
            cur = cur.next

    def add(self, item):
        '''链表头部添加元素'''
        # 创建一个保存数据的节点
        node = Node(item)
        # 判断链表是否为空,若为空,则将__head指向新节点,新节点指向头部
        if self.is_empty():
            self.__head = node
        #若不为空
        else:
            #将node的next指向__head的头节点
            node.next = self.__head
            #将__head的prev指向node
            self.__head.prev = node
            #将__haed指向node
            self.__head = node


    def append(self, item):
        '''链表尾部添加元素'''
        # 创建一个保存数据的节点
        node = Node(item)
        #判断是否为空链表
        if self.is_empty():
            self.__head = node
            node.next = self.__head  # 等价于node.next = node
        else:
            cur = self.__head
            # 找到尾节点
            while cur.next != None:
                cur = cur.next
            # 尾节点指向新节点
            cur.next = node
            #新节点的prev指向cur
            node.prev = cur

    def insert(self, pos, item):
        '''指定位置添加元素'''
        # 若指定位置为第一个位置之前,则指向头部插入
        if pos <= 0:
            self.add(item)
        # 若指定位置超过链表尾部,则执行尾部插入
        elif pos > self.length() - 1:
            self.append(item)
        # 找到指定位置
        else:
            node = Node(item)
            cur = self.__head
            count = 0
            while count < pos - 1:
                count += 1
                cur = cur.next
            #循环结束,找到插入位置的前一个节点。
            #将node的prev指向cur
            node.prev = cur
            #将node的nextcur的下一个节点(与前一步顺序可换)
            node.next = cur.next
            #将cur的下一个节点的prev指向node
            cur.next.prev = node
            #将cur的下一个节点指向node
            cur.next = node

    def remove(self, item):
        '''删除节点'''
        #如果链表为空,直接返回
        if self.is_empty():
            return
        else:
            cur = self.__head
            pre = None
            #头节点的元素就是要删除的元素
            if cur.item == item:
                #链表中只有一个节点
                if cur == None:
                    self.__head = None
                #有多个节点
                else:
                    cur.next.prev = None
                    self.__head = cur.next
                return
            #头节点不是要删除的元素
            while cur != None:
                if cur.item == item:
                    #删除
                    if cur.next:
                        cur.next.prev = cur.prev
                    cur.prev.next = cur.next
                    break
                cur = cur.next

    def search(self, item):
        '''查找节点是否存在'''
        if self.is_empty():
            return False
        cur = self.__head
        while cur != None:
            # 判断第一个节点
            if cur.item == item:
                return True
            # 未找到,继续向后移动
            else:
                cur = cur.next
        return False

if __name__ == '__main__':
    d = DoubleLinkList()
    print(d.is_empty())
    print(d.length())
    d.travel()
    d.add(1)
    d.add(2)
    d.add(3)
    d.append(4)
    d.append(5)
    d.append(6)
    d.travel()
    d.insert(-1,7)
    d.insert(3,8)
    d.insert(10,9)
    d.travel()
    print(d.search(3))
    print(d.search(10))
    d.remove(2)
    d.remove(100)
    d.travel()

进行测试,结果如下,

True
0
3 2 1 3 2 1 4 5 6 7 3 2 8 1 4 5 6 9 True
False
7 3 8 1 4 5 6 9 

四、栈的实现

1.定义

栈:后进先出,先进后出,只能在栈顶入或者出。
栈是一种“操作受限”的线性表,只允许在一端插入和删除数据,在满足先进后出、后进先出的特性时,应该使用栈。
如图,
栈的示意

2.实现

用顺序表实现的栈成为顺序栈,用链表实现的栈叫做链式栈。
顺序栈的实现相对简单,因为对于栈的操作可以通过对顺序表的操作方法实现,不用自己定义,直接调用即可,相对简单。
整个栈的实现如下,

class Stack(object):
    '''栈,用列表的方法实现'''
    def __init__(self):
        self.__items = []

    def is_empty(self):
        '''判断栈是否为空'''
        return self.__items == []

    def push(self,item):
        '''添加一个新的元素item到栈顶'''
        self.__items.append(item)

    def pop(self):
        '''弹出栈顶元素'''
        return self.__items.pop()

    def peek(self):
        '''返回栈顶元素'''
        #判断是否为空
        if self.is_empty():
            return None
        else:
            return self.__items[-1]

    def size(self):
        '''返回栈的元素个数'''
        return len(self.__items)

    def travel(self):
        '''遍历'''
        for item in self.__items:
            print(item)

if __name__ == '__main__':
    s = Stack()
    s.push(1)
    s.push(2)
    s.push(3)
    s.pop()
    print(s.size())
    print(s.is_empty())
    print(s.peek())
    s.travel()

并进行测试,结果如下,

2
False
2
1
2

3.简单应用:

浏览器的前进后退:
当你依次访问完一串页面a-b-c之后,点击浏览器的后退按钮,就可以查看之前浏览过的页面b和a。当你后退到页面a,点击前进按钮,就可以重新查看页面b和c。但是,如果你后退到页面b后,点击了新的页面d,那就无法再通过前进、后退功能查看页面c了。
如图,
浏览器的前进后退
公众号二维码
大家也可以关注我的公众号:Python极客社区,在我的公众号里,经常会分享很多Python的文章,而且也分享了很多工具、学习资源等。另外回复“电子书”还可以获取十本我精心收集的Python电子书。

发布了51 篇原创文章 · 获赞 184 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/103057843