数据结构:Python实现单向链表,双向链表,单项循环链表及其对应的方法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/rusi__/article/details/102099024

摘要

  • 所谓数据都是要存储在内存上的,而内存则是一个连续的存储空间。
  • 本文使用数据基本类型,实现链表的三种形式(链表不仅仅这三种形式)。
  • 需要定义两个类,一个是节点类,一个是实现类。
  • python中的列表使用的数据结构是:顺序表。而且具备以下几个特点:–分离式存储(数据区变化,表头信息是不变的),–元素外置(支持不同数据类型的存储和删改),–动态顺序表(扩充策略是:加倍)。
  • 双向链表还有继续拓展的空间:例如双向循环链表。
  • 理解代码需要参考以下几点:
    1、先算等号右边的
    2、等号只是引用
    3、一切皆对象
    4、一个节点的第二个属性,存储了下个节点的引用。
    5、核心思想是:改变节点中链接区的指向。
    6、若想动节点,先动自己的节点(要操作的数据),链表上的节点晚点动(原本的数据)。

代码片1:单向链表

# coding:utf-8
class Node(object):
    '''节点'''

    def __init__(self, elem):
        # 数据区
        self.elem = elem
        # 连接区
        self.next = None


class SingleLinkList(object):
    '''单链表'''

    def __init__(self, node=None):  # node默认为none
        self.__head = node  # 私有属性,对象无法访问,可以导入
        # self._head = 1  # 禁止导入,可以访问

    def is_empty(self):
        '''链表是否为空'''
        # print(node.elem)
        return self.__head is None  # 这里是一个判断

    def length(self):
        """链表长度"""
        # cur游标,用来移动遍历节点
        cur = self.__head
        count = 0
        # cur.next == None不可作为判断条件,因为链接区为None的时候,数据区实际上是有数据的,如果使用这个条件,那么这个数据将不被计入,游标也不会加1。
        # 但是当节点为None时,意味着数据区已经没有数据,而且之前的那个cur.next == None时的数据区的数据也已经被计入了。
        while cur is not None:
            count += 1
            cur = cur.next  # 游标运动的过程
        return count

    def travel(self):
        """遍历整个列表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next
        print('')

    def add(self, item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        node.next = self.__head
        self.__head = node

    def append(self, item):
        """尾部添加元素,尾插法"""
        node = Node(item)  # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next  # 链接区一直指向下一个节点
            cur.next = node  # 将本为空的这个链接区,指向下个节点,使之不为空。

    def insert(self, pos, item):
        """
        指定位置插入元素
        :param pos:从零开始,在哪添加元素
        :param item: 添加的元素
        """
        if pos <= 0:
            self.add(item)
        elif pos > self.length() - 1:  # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
            self.append(item)
        else:
            pre = self.__head
            count = 0
            while count < (pos - 1):  # 需要让添加的元素变为pos对应的索引,所以需要操作原本的该索引对应的前一个。
                count += 1
                pre = pre.next
            node = Node(item)
            # 当循环退出后 pre指向pos-1位置
            node.next = pre.next
            pre.next = node

    def remove(self, item):
        """删除节点"""
        pre = self.__head
        if pre.elem == item:
            self.__head = pre.next  # 头节点换为头节点链接的下一个节点。
        else:
            while pre.next is not None:
                if pre.next.elem == item:
                    pre.next = pre.next.next
                else:
                    pre = pre.next

    def remove_2(self, item):
        '''删除节点方法2'''
        cur = self.__head
        pre = None
        while cur is not None:
            if cur.elem == item:
                if cur == self.__head:
                    self.__head = cur.next
                else:
                    pre.next = cur.next
                break
            else:
                pre = cur
                cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        cur = self.__head
        while cur is not None:
            if item == cur.elem:
                return True
            else:
                cur = cur.next
        return False

    def __call__(self, *args, **kwargs):
        '''打印该列表'''
        cur = self.__head
        if cur is None:
            return None
        else:
            s_tr = ''
            while cur is not None:
                if cur.next is not None:
                    s_tr += str(cur.elem) + ','
                else:
                    s_tr += str(cur.elem)
                cur = cur.next
            s_tr = '<' + s_tr + '>'
            return s_tr


if __name__ == '__main__':
    ll = SingleLinkList()
    print(ll.is_empty())
    print(ll.length())

    print(ll())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.add(2)
    ll.insert(2, 7)
    print(ll.search(7))
    ll.remove(2)
    ll.remove_2(7)
    ll.travel()
    print(ll())

代码片2:双向链表

# coding:utf-8

class Node(object):
    """节点"""
    def __init__(self, item):
        self.elem = item
        self.next = None
        self.prev = None


class DoubleLinkList(object):
    "双链表"
    def __init__(self, node=None):  # node默认为none
        self.__head = node  # 私有属性,对象无法访问,可以导入
        # self._head = 1  # 禁止导入,可以访问

    def is_empty(self):
        '''链表是否为空'''
        return self.__head is None  # 这里是一个判断

    def length(self):
        """链表长度"""
        # cur游标,用来移动遍历节点
        cur = self.__head
        count = 0
        # cur.next == None不可作为判断条件,因为链接区为None的时候,数据区实际上是有数据的,如果使用这个条件,那么这个数据将不被计入,游标也不会加1。
        # 但是当节点为None时,意味着数据区已经没有数据,而且之前的那个cur.next == None时的数据区的数据也已经被计入了。
        while cur is not None:
            count += 1
            cur = cur.next  # 游标运动的过程
        return count

    def travel(self):
        """遍历整个列表"""
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next
        print('')

    def add(self, item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        node.next = self.__head
        self.__head = node
        node.next.prev = node

    def append(self, item):
        """尾部添加元素,尾插法"""
        node = Node(item)  # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
        if self.is_empty():
            self.__head = node
        else:
            cur = self.__head
            while cur.next is not None:
                cur = cur.next  # 链接区一直指向下一个节点
            cur.next = node  # 将本为空的这个链接区,指向下个节点,使之不为空。
            node.prev = cur

    def insert(self, pos, item):
        """
        指定位置插入元素
        :param pos:从零开始,在哪添加元素
        :param item: 添加的元素
        """
        if pos <= 0:
            self.add(item)
        elif pos > self.length() - 1:  # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
            self.append(item)
        else:
            cur = self.__head
            count = 0
            while count < pos:
                count += 1
                cur = cur.next
            # 当循环退出后 cur指向pos位置
            node = Node(item)
            node.next = cur
            node.prev = cur.prev
            cur.prev.next = node
            cur.prev = node

    def remove_2(self, item):
        '''删除节点方法2'''
        cur = self.__head
        while cur is not None:
            if cur.elem == item:
                if cur == self.__head:
                    self.__head = cur.next
                    if cur.next:  # 判断链表是否只有一个节点
                        cur.next.prev = None
                else:
                    cur.prev.next = cur.next
                    if cur.next:
                        cur.next.prev = cur.prev
                break
            else:
                cur = cur.next

    def search(self, item):
        """查找节点是否存在"""
        cur = self.__head
        while cur is not None:
            if item == cur.elem:
                return True
            else:
                cur = cur.next
        return False

if __name__ == '__main__':
    ll = DoubleLinkList()
    print(ll.is_empty())
    print(ll.length())


    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.add(2)
    ll.insert(2, 7)
    print(ll.search(7))
    ll.remove_2(2)
    ll.remove_2(7)
    ll.travel()


代码片3:单向循环链表

# coding:utf-8
class Node(object):
    '''节点'''

    def __init__(self, elem):
        # 数据区
        self.elem = elem
        # 连接区
        self.next = None


class SingleCycleLinkList(object):
    '''单向循环链表'''

    def __init__(self, node=None):
        self.__head = node
        if node:
            node.next = node

    def is_empty(self):
        '''链表是否为空'''
        return self.__head is None  # 这里是一个判断

    def length(self):
        """链表长度"""
        if self.is_empty():
            return 0
        else:
            # cur游标,用来移动遍历节点
            cur = self.__head
            count = 1  # cur=self.__head的那个节点(尾节点)也是需要被计数的。
            while cur.next != self.__head:
                count += 1
                cur = cur.next  # 游标运动的过程
            return count

    def travel(self):
        """遍历整个列表"""
        if self.is_empty():
            return
        else:
            cur = self.__head
            while cur.next != self.__head:
                print(cur.elem, end=' ')
                cur = cur.next
            # 退出循环的那个节点(尾节点)的那个值也得打印
            print(cur.elem)

    def add(self, item):
        """链表头部添加元素,头插法"""
        node = Node(item)
        if self.is_empty():
            self.__head = node
            node.next = node
            #  return
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            # 退出循环就代表游标已经处在尾节点了。
            node.next = self.__head
            self.__head = node
            # cur.next =
            cur.next = self.__head

    def append(self, item):
        """尾部添加元素,尾插法"""
        node = Node(item)  # 这个时候node已经实例化了,(也就具备节点所对应的两个属性了)
        if self.is_empty():
            self.__head = node
            node.next = node
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next  # 链接区一直指向下一个节点
            # cur.next=node
            # node.next=self.__head
            node.next = self.__head  # 等于 node.next = cur.next
            cur.next = node
        # node=Node(item)
        # if self.is_empty():
        #     self.__head=node
        #     node.next=self.__head
        # else:
        #     cur=self.__head
        #     while cur.next != self.__head:
        #         cur=cur.next
        #     node.next=self.__head
        #     cur.next=node

    def insert(self, pos, item):
        """
        指定位置插入元素
        :param pos:从零开始,在哪添加元素
        :param item: 添加的元素
        """
        if pos <= 0:
            self.add(item)
        elif pos > self.length() - 1:  # pos相当于索引,是要按照索引的级别进行比较的(而且不能相等--相等的时候意为在最后一个元素的前面添加一个元素,是插入,而不是追加)
            self.append(item)
        else:
            pre = self.__head
            count = 0
            while count < (pos - 1):  # 需要让添加的元素变为pos对应的索引,所以需要操作原本的该索引对应的前一个。
                count += 1
                pre = pre.next
            node = Node(item)
            # 当循环退出后 pre指向pos-1位置
            node.next = pre.next
            pre.next = node

    def remove_2(self, item):
        '''删除节点方法2'''
        if self.is_empty():
            return
        cur = self.__head
        pre = None
        while cur.next != self.__head:
            if cur.elem == item:
                if cur == self.__head:
                    # 头节点
                    # 找尾节点
                    rear = self.__head
                    while rear.next != self.__head:
                        rear = rear.next
                    self.__head = cur.next
                    rear.next = self.__head
                else:
                    # 中间节点
                    pre.next = cur.next
                # break
                return  # 为什么不用break?因为删完了就让该函数退出即可,break只能有终止此次循环的作用,有可能结束不了此程序
            else:
                pre = cur
                cur = cur.next
        # 退出循环代表尾节点
        if cur.elem == item:
            # 这个判断和上面的那个判断是否为头节点的语法不矛盾,因为只要退出循环了就说明cur.next==self.__head了,下面的这个判断,其实包含了两个判断语句。
            # 而上面的那个,也是在cur.next != self.__head的条件下,判断 cur == self.__head的。
            if cur == self.__head:  # 此时代表链表中只有一个节点(尾节点也是头节点) if self.length()==1
                self.__head = None
            else:
                pre.next = cur.next

    def search(self, item):
        """查找节点是否存在"""
        if self.is_empty():
            return False  # return 终止程序,所以不需要else
        cur = self.__head
        while cur.next != self.__head:
            if item == cur.elem:
                return True
            else:
                cur = cur.next
        if cur.elem == item:  # 判断尾节点
            return True
        else:
            return False



if __name__ == '__main__':
    ll = SingleCycleLinkList()
    print(ll.is_empty())
    print(ll.length())

    ll.append(1)
    print(ll.is_empty())
    print(ll.length())

    ll.append(2)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.add(2)
    ll.insert(2, 7)
    print(ll.search(7))
    ll.remove_2(2)
    ll.remove_2(7)
    ll.travel()
    print(ll.search(7))
    

补充

  • 顺序表和链表的核心区别:顺序表需要一段连续的存储空间(可以通过计算便能得出数据所在得位置),而链表则是利用了节点的指向,从而可以存储在零散的内存上(只能利用指向来获取对应数据得位置)。
  • 因为数据结构的不同,所以,显而易见的是:顺序表和链表修改数据时得时间复杂度是不同的。(具体参考百度一下)
  • 栈和队列需要依靠基本的“容器”来设计出来(可以是链表,也可以是顺序表)
  • 栈的特点:只有一端进出,先进后出(像杯子)
  • 队列的特点:一端进,一端出,先进先出(像管道)。
  • 本文仅服务作者本人

猜你喜欢

转载自blog.csdn.net/rusi__/article/details/102099024