Python 触“类”旁通5|链表类才是单链表的主咖

本文详细介绍了Python中单链表的实现,包括初始化、遍历、操作(如插入、删除、查找、反转)、排序、计数、包含和比较等功能。通过自定义`classList`类和`Node`类,展示了如何创建、操作和操作单链表,并提供了丰富的示例。文章还涉及了链表的加法、复制、长度计算、头尾节点获取等操作,以及重载了一些内置方法,如`__add__`、`__radd__`、`__contains__`和`__eq__`等。

有上图可知,其实在《触“类”旁通》系列之前的四篇中,所操作的对象其实一直是节点和节点组成的链式结构,不能算是真正的链表。为此,我们有请单向链表的“主咖”出场:

链表类

class List():
	def __init__(self, node=None):
		self.head = node

为了展示和操作方便,需要节点类以及几个最基本的方法协助:

class Node():
    def __init__(self, value=None, Next=None):
        self.val = value
        self.next = Next
        if (type(self.next)==Node and self.next.val==None
            or type(self.next)!=Node and self.next!=None):
            self.next = None

    def __repr__(self):
        return f'{self.val}->{self.next}'

    @property
    def values(self):
        ret,ptr = [],self
        while ptr is not None:
            ret.append(ptr.val)
            ptr = ptr.next
        return ret

    def build(*data, split=True):
        '''把数据转换成节点链表'''
        lst,ret = [],Node()
        for val in data:
            if type(val) is str:
                if not split:
                    lst.append(val)
                    continue
                if str=='':
                    continue
                try:
                    num = int(val)
                    lst.extend([int(_) for _ in val])
                except:
                    lst.extend([_ for _ in val])
            elif hasattr(val,'__iter__'):
                lst.extend([_ for _ in val])
            elif type(val) is Node:
                if val is not None:
                    lst.extend(val.values)
            elif type(val) is List:
                if val.head is not None:
                    lst.extend(val.head.values)
            else:
                lst.append(val)
        ret = Node()
        for i in lst[::-1]:
            ret = Node(i, ret)
        return ret


class List():
    def __init__(self, *node):
        self.head = Node.build(*node)
		
    def __repr__(self):
        return f'[{self.head}]'

创建链表

操作链表总得新建一个链表吧,不管是怎样去实现的(方法很原始),总之这样新建链表就是非常方便,并且和内置的顺序列表作个对比,创建和返回值都有几分神似:

>>> List(1,2,3,4,5)
[1->2->3->4->5->None]
>>> List(range(1,11,2))
[1->3->5->7->9->None]

>>> list((1,2,3,4,5))
[1, 2, 3, 4, 5]
>>> list(range(1,11,2))
[1, 3, 5, 7, 9]
>>> 

以下所有代码都要放在class List()里,由此开始对单链表“主咖”的探索:

链表长度、判断是否为空

    def __len__(self):
        return self.size()

    @property
    def length(self):
        return self.size()

    def is_empty(self):
        if self.head.val==None:
            return True
        else:
            return False

    def size(self):
        ret,ptr = 0,self.head
        if ptr.val==None: return 0
        while ptr:
            ptr = ptr.next
            ret += 1
        return ret

>>> list1 = List()
>>> list1.is_empty()
True
>>> list1.size()
0
>>> len(list1)
0
>>> 
>>> list2 = List(1,2,3,4,5)
>>> list2.is_empty()
False
>>> list2.size()
5
>>> len(list2)
5
>>> 

单链表的尾结点

    @property
    def tail(self):
        ptr = self.head
        while ptr.next:
            ptr = ptr.next
        return ptr

# 方法.tail修饰成只读属性,返回尾节点
# 而.head头指针是可修改,因为指向头节点,一般就代表整个单链表

>>> list1 = List()
>>> list1.tail
None->None
>>> list1.head
None->None
>>> list1
[None->None]
>>> 
>>> list2 = List(1,2,3,4,5)
>>> list2.tail
5->None
>>> list2.head
1->2->3->4->5->None
>>> list2
[1->2->3->4->5->None]
>>>   

创建链表副本

    def copy(self):
        return List(self.head)

>>> list1 = List(1,2,3)
>>> list2 = list1.copy()
>>> list2.head.val = 0
>>> list2
[0->2->3->None]
>>> list1
[1->2->3->None]
>>> list3 = list1  # 直接赋值相当于只是设置一个指针
>>> list3.head.val = 0
>>> list3
[0->2->3->None]
>>> list1
[0->2->3->None]
>>> 

遍历和生成器

    @property
    def values(self):
        if self.is_empty(): return []
        ret,ptr = [],self.head
        while ptr:
            ret.append(ptr.val)
            ptr = ptr.next
        return ret

    def __iter__(self):
        ptr = self.head
        while ptr:
            yield ptr.val
            ptr = ptr.next

    @property
    def items(self):
        yield from self.values

yield 语法糖:

yield form iterable object  (python 3.3+)

等价于:
for item in iterable:
        yield item

>>> list1 = List(range(9))
>>> list1.items
<generator object List.items at 0x0000000002F95270>
>>> lstgen = list1.items
>>> next(lstgen)
0
>>> next(lstgen)
1
>>> next(lstgen)
2
>>> next(lstgen)
3
>>> [i for i in lstgen]
[4, 5, 6, 7, 8]
>>> list1.values
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> 

头部插入

    def push(self, data):
        if not isinstance(data, List):
            data = List(data)
        if self.is_empty():
            self.head = data.head
        else:
            ret = data.head
            ret.next,self.head = self.head,ret
        return self

>>> list1 = List()
>>> list1.push(3)
[3->None]
>>> list1
[3->None]
>>> list1.push(Node(2))
[2->3->None]
>>> list1
[2->3->None]
>>> list1.push(List(1,2,3))
[1->2->3->None]
>>> list1.push(range(5))
[0->1->2->3->None]
>>> list1
[0->1->2->3->None]
>>> 
>>>  # 只增加一个节点

前插合并

    def add(self, *data):
        data = List(*data)
        if self.is_empty():
            self.head = data.head
        else:
            ptr = data.head
            while ptr.next:
                ptr = ptr.next
            ptr.next,self.head = self.head,data.head
        return self

 >>> list1 = List()
>>> list1.add(3,4,5)
[3->4->5->None]
>>> list1.add(1,2)
[1->2->3->4->5->None]
>>> list1
[1->2->3->4->5->None]

>>> list1 = List(3,4)
>>> list2 = List(1,2)
>>> list1.add(list2)
[1->2->3->4->None]
>>> list1
[1->2->3->4->None]
>>> list2
[1->2->None]
>>> 

尾部追加

    def append(self, data):
        if not isinstance(data, List):
            data = List(data)
        if self.is_empty():
            self.head = data.head
        else:
            ptr = self.head
            while ptr.next:
                ptr = ptr.next
            ptr.next = data.head
        return self

>>> list1 = List(1,2,3)
>>> list1.append(4)
[1->2->3->4->None]
>>> list1.append(5)
[1->2->3->4->5->None]
>>> list1
[1->2->3->4->5->None]
>>> 
>>> list2 = List()
>>> list2.append(1)
[1->None]
>>> list2.append(2)
[1->2->None]
>>> list2.append(Node(3,Node(4)))
[1->2->3->4->None]
>>> list2
[1->2->3->4->None]
>>> list1.append(list2)
[1->2->3->4->5->1->2->3->4->None]
>>> list1
[1->2->3->4->5->1->2->3->4->None]
>>> 

追加合并

    def cat(self, *data):
        for val in data:
            self.append(val)
        return self

>>> list1 = List(1,2,3)
>>> list1.cat(4,5,6,7)
[1->2->3->4->5->6->7->None]
>>> list1
[1->2->3->4->5->6->7->None]
>>> list2 = List()
>>> list2.cat(1,2)
[1->2->None]
>>> list2
[1->2->None]
>>> list2.cat(3,[4,5],range(6,9),List(9,10))
[1->2->3->4->5->6->7->8->9->10->None]
>>> list2
[1->2->3->4->5->6->7->8->9->10->None]
>>> 

链表加法

    def __add__(self, *data):
        self.cat(*data)
        return self

    def __radd__(self, *data):
        self.add(*data)
        return self

>>> List()+1+2+3
[1->2->3->None]
>>> (1,2,3)+List()
[1->2->3->None]
>>> list0 = List()
>>> list0 = 0 + list1 + 1 + 2
>>> list0
[0->1->2->None]
>>> list1 = List(1,2,3)
>>> list1 = 0 + list1 + 4 + 5
>>> list1
[0->1->2->3->4->5->None]
>>> list2 = List()
>>> for i in range(6):
    list2 += i

>>> list2
[0->1->2->3->4->5->None]
>>> 

反转链表

    def __reversed__(self):
        ptr,ret = self.head,List()
        while ptr:
            ret = ptr.val + ret
            ptr = ptr.next
        return ret

    def reverse(self):
        ptr,ret = self.head,List()
        while ptr:
            ret.push(ptr.val)
            ptr = ptr.next
        self.head = ret.head
        return self

>>> list1 = List(1,2,3)
>>> reversed(list1)
[3->2->1->None]
>>> list1
[1->2->3->None]
>>> list1.reverse()
[3->2->1->None]
>>> list1
[3->2->1->None]

>>> list2 = List()
>>> reversed(list2)
[None->None]
>>> list2.reverse()
[None->None]
>>> list2
[None->None]
>>> 

数据域的最大(小)值

    def nlargest(self):
        if self.is_empty():
            return None
        ptr,ret = self.head,self.head.val
        while ptr:
            ret = max(ret, ptr.val)
            ptr = ptr.next
        return ret
 
    def nsmallest(self):
        if self.is_empty():
            return None
        ptr,ret = self.head,self.head.val
        while ptr:
            ret = min(ret, ptr.val)
            ptr = ptr.next
        return ret

>>> list1 = List(1,2,3)
>>> list1.nlargest()
3
>>> list1.nsmallest()
1
>>> list2 = List(1,0,2,5,3)
>>> list2.nlargest()
5
>>> list2.nsmallest()
0
>>> 

指定数值的节点索引

返回数据域等于指定目标值num的第一个节点的索引号,头节点为0;找到返回索引号,若找不到 find() 返回 -1, index() 报错。

    def find(self, num):
        if self.is_empty():
            return -1
        ptr,ret = self.head,-1
        while ptr:
            ret += 1
            if ptr.val == num: break
            ptr = ptr.next
        else:
            ret = -1
        return ret

    def index(self, num):
        error = lambda i:ValueError(f'{i} is not in List')
        if self.is_empty():
            raise error(num)
        ptr,ret = self.head,-1
        while ptr:
            ret += 1
            if ptr.val == num: break
            ptr = ptr.next
        else:
            raise error(num)
        return ret

>>> list1 = List(range(1,5))
>>> list1.find(1)
0
>>> list1.find(2)
1
>>> list1.find(5)
-1
>>> list1.find(0)
-1

>>> list2 = List(range(6))
>>> list2.index(0)
0
>>> list2.index(5)
5
>>> list2.index(6)
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    list2.index(9)
  File "D:\List0821.py", line 202, in index
    raise error(num)
ValueError: 9 is not in List

>>> 

指定数值的节点个数

    def count(self, value):
        if self.is_empty():
            return 0
        ptr,ret = self.head,0
        while ptr:
            if ptr.val==value: ret += 1
            ptr = ptr.next
        return ret

 >>> list1 = List(0,1,2,1,0,2,0,3,0,2)
>>> list1.count(0)
4
>>> list1.count(1)
2
>>> list1.count(2)
3
>>> list1.count(3)
1
>>> list1.count(4)
0
>>> 

包含和等于

    def __contains__(self, data):
        if self.is_empty():
            return False
        if isinstance(data,List):
            data = data.head
        if isinstance(data,Node):
            count = self.find(data.val)+1
            if count==0: return False
            ptr1,ptr2 = self.head,data
            for _ in range(1,count):
                ptr1 = ptr1.next
                if ptr1 is None: return False
            while ptr2:
                if ptr1 is None or ptr1.val!=ptr2.val:
                    return False
                ptr1,ptr2 = ptr1.next,ptr2.next
            return True
        else:
            return bool(1 + self.find(data))
            
    def __eq__(self, data):
        if not isinstance(data,List):
            return False
        if data.is_empty():
            if self.is_empty(): return True
            else: return False
        return data in self and self in data

>>> list1 = List(range(1,4));
>>> list2 = List(range(6))
>>> list1 in list2
True
>>> list2 in list1
False
>>> 2 in list1
True
>>> 2 in list2
True
>>> None in list1
False
>>> Node() in list1
False
>>> List() in list1
False
>>> Node(1) in list1
True
>>> List(2) in list1
True
>>> Node(2,Node(3)) in list1
True
>>> List(2,3,4) in list2
True

>>> list3 = List(1, 2, 3)
>>> list1 == list2
False
>>> list1 == list3
True
>>> list1 != list2
True
>>> list1 != list3
False
>>> List() == List(1)
False
>>> List() == List()
True
>>> List() == None
False
>>> List() != 1
True
>>> 

链表排序

python没提供重载__sorted__()方法,所有暂不支持内建函数sorted(),可能需要重载切片方法后变相来实现它。以下方法.sort()直接交换节点数据域实现,.sorted()排序但不改变自身:

    def sort(self):
        length = self.size()
        for i in range(1, length):
            ptr = self.head
            for j in range(length - i):
                if ptr.val>ptr.next.val:
                    ptr.val,ptr.next.val = ptr.next.val,ptr.val
                ptr = ptr.next
        return self

    def sorted(self):
        if self.size()<2: return self
        return List(self.head).sort()

>>> list1 = List(2,1,0,3,4,5,3)
>>> list1.sorted()
[0->1->2->3->3->4->5->None]
>>> list1
[2->1->0->3->4->5->3->None]
>>> list1.sort()
[0->1->2->3->3->4->5->None]
>>> list1
[0->1->2->3->3->4->5->None]

重载链表索引

    def __getitem__(self, item):
        return self.values[item]

    def __setitem__(self, idx, value):
        length = self.size()
        if idx<0: idx += length
        if self.is_empty() or idx+1>length or idx<0:
            raise IndexError('List() index out of range.')
        if isinstance(value,Node) or isinstance(value,List):
            raise TypeError('value type Error')
        ptr = self.head
        for i in range(idx):
            ptr = ptr.next
        ptr.val = value

>>> list1 = List(range(1,6))
>>> list1
[1->2->3->4->5->None]
>>> list1[0],list1[2],list1[4]
(1, 3, 5)
>>> list1[-1],list1[-3],list1[-5]
(5, 3, 1)
>>> list1[1] *= 5
>>> list1[3] += 6
>>> list1
[1->10->3->10->5->None]
>>> list1[-1] = 0
>>> list1[-5] = -1
>>> list1
[-1->10->3->10->0->None]
>>> list1[5], list1[-6]
Traceback (most recent call last):
  File "<pyshell#62>", line 1, in <module>
    list1[5],list1[-6]
  File "D:/Python/List0823.py", line 103, in __getitem__
    return self.values[item]
IndexError: list index out of range

>>> # 上节排序中预计的:有了切片功能就支持sorted()函数,但返回的是普通列表
>>> list2 = List(2,1,0,3,4,5,3)
>>> sorted(list2)
[0, 1, 2, 3, 3, 4, 5]

>>> List(sorted(list2))
[0->1->2->3->3->4->5->None]
>>> list2
[2->1->0->3->4->5->3->None]
>>> 

删除头节点或尾节点

    def pop(self):
        if self.is_empty():
            raise IndexError('pop from empty List.')
        if self.head.next is None:
            ret,self.head = self.head.val,Node()
            return ret
        ptr = self.head
        while ptr.next.next:
            ptr = ptr.next
        ret,ptr.next = ptr.next.val,None
        self = List(self.head)
        return ret

    def popfront(self):
        if self.is_empty():
            raise IndexError('pop from empty List.')
        ret,ptr = self.head.val,self.head.next
        if ptr is None: self.head = Node()
        else: self.head = self.head.next
        return ret

>>> list1 = List(1,2,3)
>>> list1.pop()
3
>>> list1.pop()
2
>>> list1.pop()
1
>>> list1.pop()
Traceback (most recent call last):
  File "<pyshell#84>", line 1, in <module>
    list1.pop()
  File "D:\List0821.py", line 249, in pop
    raise IndexError('pop from empty List.')
IndexError: pop from empty List.

>>> list1 = List(1,2,3)
>>> list1.popfront()
1
>>> list1.popfront()
2
>>> list1.popfront()
3
>>> list1.popfront()
Traceback (most recent call last):
  File "<pyshell#89>", line 1, in <module>
    list1.popfront()
  File "D:\List0821.py", line 262, in popfront
    raise IndexError('pop from empty List.')
IndexError: pop from empty List.

>>>   

重载索引删除的功能

删除指定索引的节点

    def __delitem__(self, index):
        length = self.size()
        if index<0: index += length
        if self.is_empty() or index+1>length or index<0:
            raise IndexError('List() index out of range.')
        ptr = self.head
        if index>0:
            for i in range(index-1):
                ptr = ptr.next
            ptr.next = ptr.next.next
        else:
            if ptr.next is None: self.head = Node()
            else: self.head = self.head.next
        return self
    
    delete = __delitem__

>>> list1 = List(1,2,3,4)
>>> list1.delete(-5)
Traceback (most recent call last):
  File "<pyshell#115>", line 1, in <module>
    list1.delete(4)
  File "D:\List0821.py", line 272, in delete
    raise IndexError('List() index out of range.')
IndexError: List() index out of range.

>>> list1.delete(0)
[2->3->4->None]
>>> list1.delete(1)
[2->4->None]
>>> list1.delete(-2)
[4->None]
>>> list1.delete(-1)
[None->None]

>>> list2 = List(1,2,3,4)
>>> del list2[0]
>>> list2
[2->3->4->None]
>>> del list2[1]
>>> list2
[2->4->None]
>>> del list2
>>> list2
Traceback (most recent call last):
  File "<pyshell#113>", line 1, in <module>
    list2
NameError: name 'list2' is not defined

>>> 

删除指定数值的节点

    def remove(self, value):
        index = self.find(value)
        if index==-1:
            raise ValueError('List.remove(x): x not in List')
        self.delete(index)
        return self

>>> list1 = List(0,1,2,3)
>>> list1.remove(1)
[0->2->3->None]
>>> list1.remove(0)
[2->3->None]
>>> list1.remove(3)
[2->None]
>>> list1.remove(3)
Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    list1.remove(3)
  File "D:\Python\List0823.py", line 169, in remove
    raise ValueError('List.remove(x): x not in List')
ValueError: List.remove(x): x not in List

>>> list1.remove(2)
[None->None]

点个赞再走!谢啦!! 


相关文章链接:

触“类”旁通1|以单链表为例,一步步深入了解类

触“类”旁通2|数据结构入门之单链表的创建和遍历

触“类”旁通3|单链表基本操作之找查、删除和反转

触“类”旁通4|重载运算符之单链表的“加减乘除”

评论 35
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hann Yang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值