4. 单链表

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4. 单链表相关的知识,希望对你有一定的参考价值。

链式结构--单链表

1.线性结构和链式结构的区别:

线性结构内存是连续的,可以通过下标访问的

链式结构内存是不连续的,无法通过下标访问

(链式结构append时会很方便,find的时候会很麻烦,因为要从头到尾的遍历、寻找)

技术分享图片

通过链接指针的方式,指向下一个。


2.单链表(LinkedList)

技术分享图片

由一个一个节点通过指针的方式,把节点给串起来,root节点是一个入口,可以去遍历。

每一块区域都会代表一个值,节点称之为node。

每个节点除了value外,还要一个指针指向下一个元素,如图:节点

技术分享图片

这里通过class来定义一个node属性,它包含值,还包含指向下一个元素的指针,

单链表的结构如下:

                    data:root(入口)、length(长度)

linkedlist

                    method:init(初始化的方法)、append(追加元素)、appendleft(左侧追加元素)、iter_node(遍历节点)、remove(删除及诶单)、find(查找节点)、popleft(删掉头节点)




代码实现:

class Node(object):                                 #Node节点
    def __init__(self, value=None, next=None):
        self.value, self.next = value, next         #多重赋值


class LinkedList(object):
    def __init__(self, maxsize=None):               #maxsize表示最大容量,None表示无限扩容
        self.maxsize = maxsize                      #赋值
        self.root = Node()                          #需要一个根节点,是一个空节点
        self.length = 0                             #需要一个长度,默认是0
        self.tailnode = None

    def __len__(self):                              #魔术方法,定义len方法
        return self.length                          #这里返回记录的长度值

    def append(self, value):
        if self.maxsize is not None and             len(self) > self.maxsize:               #条件
            raise Exception('LinkedList Is Full')   #抛出异常
        node = Node(value)                          #构造新加入的节点,并赋值为value
        tailnode = self.tailnode
        if tailnode is None:                        #这个表明尾节点是None,则说明链表只有root节点这一个节点
            self.root.next = node                   #把root的next指针指向新加入的节点
        else:
            tailnode.next = node                    #当尾节点不是None的时候,将尾节点的next指针指向新加入节点
        self.tailnode = node                        #更新链表,现在新加入的节点是链表的尾节点
        self.length += 1                            #将链表长度值加1

    def appendleft(self, vlaue):                    #定义往左边插入节点,位置为root节点的后面,实现往左边插入的操作
        headnone = self.root.next                   #定义头节点为root节点的下一个节点
        node = Node()                               #构造新加入的节点
        self.root.next = node                       #把root的next节点指向新加入的节点
        node.next = headnone                        #把新加入的节点的next节点指向原来的头节点
                                                    #这两步就把链表串起来了,实现在root节点后面和原头节点前面添加节点

    def iter_node(self):                            #遍历,从头节点,遍历到尾节点
        curnode = self.root.next                    #从第一个节点遍历,curnode为root节点的下一个节点
        while curnode is not self.tailnode:         #只要curnode不是尾节点
            yield curnode                           #就一直yield当前节点
            curnode = curnode.next                  #更新curnode为下一节点
        yield curnode                               #这个while循环只移动到了最后一个节点,然后还要把当前节点更新到最后一个节点,
                                                    #也要把他yield出来,这样就实现了完整的遍历操作。

    def __iter__(self):
        for node in self.iter_node():
            yield node.value


    def remove(self, value):                        #查找并删除这个节点,这里是O(n),必须从头查到尾,没法通过下标查找到
        prevnode = self.root                        #定义prevnode为根节点
        #curnode = self.root.next
        for curnode in self.iter_node():            #遍历链表,当前节点为curnode
            if curnode.value == value:              #如果当前遍历到的curnode的值和要删除的值一致
                prevnode.next = curnode.next        #将prevnode的next指针指向到当前要删除的curnode节点的下一个节点
                if curnode is self.tailnode:        #如果这个curnode刚好是尾节点
                    self.tailnode = prevnode        #则将prevnode就是尾节点
                del curnode                         #删除节点
                self.length -= 1                    #长度减1
                return 1                            #表明删除成功
            else:                                   #否则如果当前遍历到的curnode的值不是要删除的值
                prevnode = curnode                  #则更新prevnode的位置到遍历的这个非删除的节点,并继续遍历下一个curnode节点
        return 1                                    #删除完成返回1

    def find(self, value):                          #查找操作,也是要从头遍历,这也是单链表查找比较慢的原因,时间复杂度O(n)
        index = 0                                   #索引从0开始
        for node in self.iter_node():               #遍历链表
            if node.value == value:                 #如果要查找的值等于这个node的值
                return index                        #返回这个node的索引
            index += 1                              #索引+1
        return -1                                   #没有找到指定的值则返回-1

    def popleft(self):                              #相当于把第一个节点删掉
        if self.root.next is None:                  #如果root节点的下一个节点是None
            raise Exception('Pop From Empty')       #抛出异常
        headnode = self.root.next                   #将root的下一个节点定义为头节点
        self.root.next = headnode.next              #再将root的next指针指向上面头节点的下一个节点
        self.length -= 1                            #将长度减1
        value = headnode.value                      #同pop操作最后会返回一个值,并打印出来
        del headnode                                #删除原来的头节点
        return value                                #返回被popleft掉的值

    def clean(self):                                #清空操作,清空链表
        for node in self.iter_node():               #遍历节点,并删除遍历到的节点
            del node
        self.root.next = None                       #将root节点的下一个节点指向空
        self.length = 0                             #将长度值置0


#单元测试
def test_linked_list():
    lst = LinkedList()
    lst.append(0)
    lst.append(1)
    lst.append(2)
    assert len(lst) == 3
    assert lst.find(2) == 2
    assert lst.find(3) == -1
    lst.remove(0)
    assert len(lst) == 2
    assert lst.find(0) == -1
    assert list(lst) == [1, 2]   #断言list只有[1, 2]
    lst.appendleft(0)
    assert list(lst) == [0 ,1 ,2]
    assert len(lst) == 3
    headvalue = lst.popleft()   #验证popleft方法
    assert headvalue == 0
    assert len(lst) == 2
    assert list(lst) == [1, 2]
    lst.clean()                 #清空链表
    assert len(lst) == 0


# pytest linked_list.py

执行测试


平均时间复杂度:

链表操作平均时间复杂度
linked_list.append(value)O(1)
linked_list.appendleft(value)O(1)
linked_list.find(value)O(n)
linked_list.remove(value)O(n)


以上是关于4. 单链表的主要内容,如果未能解决你的问题,请参考以下文章

[程序员代码面试指南]链表问题-按照左右半区的方式重新组合单链表

基本数据结构实现--单链表含测试代码

合并两个有序单链表

递归-反转单链表 -图解

递归-反转单链表 -图解

数据结构 链表