Pyhton数据结构之单向链表实现

首先看一个python的小例子。在python中,如果我们想交换两个数的值的时候我们可以这样操作:

如:

a = 10
b = 20

如果此时我们想交换a,b的值可以直接执行 :

a, b = b, a

这样就可以实现a, b值得交换,说明在python中 “=” 表示的是指向的功能,a = 10 表示 a 指向的是10这个数的地址,b = 20指的是20这个数的地址,这样我们在执行a, b = b, a时相当于交换了a和b所指向的地址,所以python中的 “=” 我们可以理解成是C语言中的指针,下面我们简单说一下单链表的概念。

链表是由多个节点顺序串起来的一种数据结构,每个节点分为两个部分:数据区和链接区,数据区存放当前节点所存储的数据,链接区指向下一个节点,如下图所示:

节点(Node):,elem代表数据区,next代表链接区指向下一个节点,如果不存在下一个节点,则指向None。

单向链表:

head表示头节点,如果链表为空,则head指向的就是None;最后一个节点,也就是尾节点的链接区指向None。

上面我们已经分析过,python中的 “=” 相当于指向的意思,所以我们在链表中表示指向功能时可以用 “=” 表示。比如我们在创建节点时,因为是单个的节点,所以需要把节点的链接区next指向空,此时我们就可以用下面这段代码表示next指向的是None:

self.next = None

下面给个完整的代码实现,有我自己的注释方便大家阅读:

# -*- coding: utf-8 -*-

class Node(object):
    """单链表节点类"""

    def __init__(self, elem):
        self.elem = elem
        self.next = None


class SingleLinkList(object):
    """单链表类"""

    # 创建链表之后,为了使链表对象能够与节点对象产生联系,需要给链表定义头节点
    # 如果没有头节点,则默认为空,所以node的默认值应为None
    def __init__(self, node=None):
        self.__head = node

    def is_empty(self):
        """判断链表是否为空"""
        # 判断链表是否为空只需要看头节点是否为空
        # 或者用(self._head == None)
        return self.__head is None

    def length(self):
        """链表长度"""
        # 判断链表的长度,其实就是计算链表中的节点数,因为链表中的元素都是以节点的形式存储
        # 首先通过head节点找到第一个节点的位置,然后依次往后数就行了
        # 需要一个中间变量cur作为游标,cur本身也是个节点对象,记录当前跑到哪个节点了,
        # cur一开始的位置就是头节点head的位置
        # count记录节点数量
        cur = self.__head
        count = 0
        while cur is not None:
            count += 1
            cur = cur.next
        return count

    def travel(self):
        """遍历链表"""
        # 遍历和计算长度类似
        # 为了让显示的更好看,我们打印的时候不进行换行处理,赋值参数end=' '即可
        cur = self.__head
        while cur is not None:
            print(cur.elem, end=' ')
            cur = cur.next
        print('')  # 这句语句的作用是为了停止换行

    def add(self, item):
        """在头部添加元素"""
        # 想在头部添加元素N,那么就要使原始链表的头节点指向N,而N指向原始链表的第一个节点
        # 为了不使原始链断掉,所以先是把N节点的链接区指向原始头节点,再把N节点的地址复制给头节点
        node = Node(item)
        node.next = self.__head
        self.__head = node

    def append(self, item):
        """在尾部添加"""
        # 因为传入进来的是个真真切切的数据,而不是节点,所以需要把数据定义成节点
        # 首先要判断链表是否为空,如果是空,就可直接把头节点赋值为需要添加的节点node
        # 否则我们需要先遍历到链表的尾部,让链表的最后一个节点指向我们要添加的节点
        node = Node(item)
        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):
        """指定位置添加元素"""
        # pre也是一个游标,只不过代表的是我们需要添加的位置的前一个节点
        # 比如我们执行insert(2, 11),表示在索引为2的位置插入11,即我们需要在原始链表索引为2的位置前面插入11
        # 游标需要移动的次数就是pos-1  当pos小于0时默认是头部插入,大于链表长度默认尾部插入
        # 因为pos表示的也是索引位置,链表的最后一个节点的索引是链表长度减1,所以大于的应该是length()-1
        if pos <= 0:
            self.add(item)
        elif pos > (self.length()-1):
            self.append(item)
        else:
            node = Node(item)
            pre = self.__head
            count = 0
            while count < (pos-1):
                count += 1
                pre = pre.next
            node.next = pre.next
            pre.next = node

    def remove(self, item):
        """删除元素"""
        # 这里需要用到两个游标,删除操作只需要将前一个节点的next指向当前节点的next就行了
        # 首先让pre是个空节点,cur指向头节点,然后cur每往后移一位,pre跟着移动一位
        # 考虑特殊情况,如果cur当前指向的是头节点,说明需要删除的就是头节点,所以直接将头节点指向下一个节点
        # 一旦执行完删除就要退出循环
        pre = None
        cur = self.__head
        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当前所在位置对应的元素就是想查找的元素,则返回True
        # 否则就不断移动游标 知道所有链表元素都遍历完还没有找到,则返回False
        # 程序只要遇到return就会停止,所以当前面找到元素时就会执行return True,程序就会直接停止
        # 就不会再执行下面的return False了
        cur = self.__head
        while cur is not None:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        return False


if __name__ == '__main__':
    sll = SingleLinkList()
    print(sll.is_empty())  # 首先看看是不是为空
    print(sll.length())
    sll.append(1)          # 添加一个元素之后看看是不是为空
    print(sll.is_empty())
    print(sll.length())
    
    # 下面执行多段代码之后来验证功能是否可以用
    sll.append(2)
    sll.add(10)
    sll.insert(-1, 200)
    sll.append(3)
    sll.append(4)
    sll.insert(20, 100)  # 这里我们插入的位置是20,大于了链表的长度,默认是尾部插入
    sll.append(5)
    sll.insert(2, 11)
    sll.remove(100)
    sll.travel()
    

运行之后结果如下:

True
0
False
1
200 10 11 1 2 3 4 5 

共同学习,理解不到之处,欢迎指正。

猜你喜欢

转载自blog.csdn.net/weixin_38775269/article/details/81285651