数据结构与算法 | 链表-3:单向循环链表的实现

1 定义

在之前的博客:数据结构与算法 | 链表-2:单链表的实现中笔者提到了单向链表的相关实现,本期博客将注重单向循环链表的实现!

单向循环链表其实就是在单向链表的基础之上做了一点改进,就是尾节点的next区域并不是指向None了,而是指向头结点!
在这里插入图片描述

2 Python手写单向循环链表

2.1 先定义节点

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

2.2 增删改查

class SinCycLinkedlist(object):
    '''单向循环链表'''
    def __init__(self, node=None): # 设置一个默认参数
        self.__head = node # 双下划线表示私有属性 不让别人看到 仅自己用
        # node的next指向自身 如果传入了非空节点
        if node:
            node.next = node
    
    def is_empty(self):
        '''is_empty() 链表是否为空'''
        return self.__head == None
    
    def length(self):
        '''length() 链表长度'''
        # 另外下面对于空链表的特殊情况符合,返回0
        # 先定义辅助的cur cur游标,用来移动遍历节点
        if self.is_empty():
            # 针对空链表
            return 0
        else:
            cur = self.__head
            # count用来计数
            count = 1
    #         while cur != None: # 区别于cur.next
                # 但是尾部节点不是None了 而是指向头部节点 所以要变化
            while cur.next != self.__head:
                count += 1
                cur = cur.next
            return count
        # 1个节点的链表也可以cover
        
    
    def travel(self):
        '''travel() 遍历整个链表'''
        if self.is_empty():
            # 先看是否为空链表
            pass 
        else: 
            cur = self.__head # 进入了第一个结点
            while cur.next != self.__head:
                # 保证每一个结点的元素都打印出来即可
                # 但现在最后一个节点没打印出来!
                print(cur.elem, end = ' ') # 打印元素值
                cur = cur.next
            # 所以还要再加一个
            print(cur.elem, end = ' ')
            print(' ')
            # 一开始是空链表呢?所以没有next 故重新考虑加条件
            # 1个节点没问题
    
    def add(self, item):
        '''链表头部添加元素,头插法
        时间复杂度:O(1)
        '''
        # 先将加入的元素指向原有首节点,然后让头结点指向新插入的元素
        node = Node(item)
        # 这时候先改变谁的指向呢?最后一个还是首部的?
        # 先改最后一个
        if self.is_empty():
            # 如果为空链表的情况
            self.__head = node
            node.next = node # 指向自身
        else:
            cur = self.__head
            while cur.next != self.__head:
                cur = cur.next
            # 记录了游标位置
            # 再改头部
            node.next = self.__head
            self.__head = node
            # 修改尾节点指向
            cur.next = node
            # 能处理原有链表为空链表的情况
        
    def append(self, item):
        '''链表尾部添加元素,尾插法
        时间复杂度:O(n) 循环找到尾部
        '''
        # 先定义一个节点 然后循环到最后一个
        node = Node(item)
        if self.is_empty():
            # 如果为空 直接头结点指向该插入的元素
            self.__head = node
            node.next = node
        else:
            # 非空引入一个游标cur
            cur = self.__head
            while cur.next != self.__head: # 区别于上面,因为要在最后一个节点指向新的!
                # 这里无法直接兼容空链表 因为空链表没有.next
                cur = cur.next
            cur.next = node
            node.next = self.__head
            
    def insert(self, pos, item):
        '''指定位置添加元素
        参数pos:表示下标,从0开始索引
        区别于之前的cur 用pre 其实意思一致
        时间复杂度:O(n) 尾部插入 循环 也是n
        '''
        if pos <= 0:
            # 认为是头插法
            self.add(item)
        elif pos > (self.length()-1):
            # 认为是尾插法
            self.append(item)
        else: 
            # 中间插入和首尾的无关 所以中间不变!
            pre = self.__head 
            count = 0
            while count < (pos-1):
                count += 1
                pre = pre.next
            # 当循环退出后,pre指向pos-1位置
            node = Node(item)
            node.next = pre.next # 先改变新节点 不打乱原来的关系
            pre.next = node # 即pre.next指向node所在位置
    
    def remove(self, item):
        '''删除节点
        引入两个游标 比较好理解
        先cur指向首节点,pre指向None
        然后让pre指向cur cur再移动
        '''
        if self.is_empty():
            pass
        else:
            # 初始化
            cur = self.__head
            pre = None

            # 再引入一个指针 一开始先找到尾部!
            # 但是这样会比较浪费!是否有更优的方法?
            # 放到循环里面!
    #         last = self.__head
    #         while last.next != self.__head:
    #             last = last.next
            # 此时last就在尾节点
            # 终止条件判断
            while cur.next != self.__head:
                if cur.elem == item:
                    # 先判断此节点是否是头节点
                    if cur == self.__head:
                        # 头结点-需要先找到尾节点的游标last
                        last = self.__head
                        while last.next != self.__head:
                            last = last.next
                        # 此时last就在尾节点
                        self.__head = cur.next
                        # 让尾节点指向cur.next
                        last.next = self.__head
                    else:
                        # 中间节点-对应单链表的过程!
                        pre.next = cur.next
                        # 链表已经完成一条主线 不用管这个值对应下面的指向了
                    return 
                else:
                    # 开始移动 两个游标之间差1个位置
                    pre = cur
                    cur = cur.next
            # 尾节点:如果找到的元素是尾节点
            # 先判断
            if cur.elem == item:
                # 如果为单节点 pre为None 不移动 没有next
#                 if pre == None:
                if cur == self.__head:
                    # 单节点
                    self.__head = None
                else:
                    # 多节点
                    pre.next = self.__head
                    # 等价于 pre.next = cur.next
        
        
        # 空链表不执行任何操作!可以没问题
        # 如果删除首节点 需要变化的是头结点指向下一个!
        # 如果只有一个节点,咋办呢?没问题 指向None
        # 上面是头部情况,尾部情况捏?没有问题,直接指向None
    
    def search(self, item):
        '''查找节点是否存在
        时间复杂度:O(n) 循环!
        '''
        if self.is_empty():
            return False
        cur = self.__head # 用来遍历链表
        while cur.next != self.__head:
            if cur.elem == item:
                return True
            else:
                cur = cur.next
        # 退出循环的时候补充尾部节点
        if cur.elem == item:
            return True
        else:
            return False
        # 能解决空链表的问题 如果为空 直接return false

2.3 实例

if __name__ == "__main__":
    ll = SinCycLinkedlist()
    ll.add(1)
    ll.add(2)
    ll.append(3)
    ll.insert(2, 4)
    ll.insert(4, 5)
    ll.insert(0, 6)
    print ("length:",ll.length())
    ll.travel()
    print (ll.search(3))
    print (ll.search(7))
    ll.remove(1)
    print ("length:",ll.length())
    ll.travel()
    ll.remove(6)
    ll.travel()
    ll.remove(5)
    ll.travel()
length: 6
6 2 1 4 3 5  
True
False
length: 5
6 2 4 3 5  
2 4 3 5  
2 4 3  

参考

猜你喜欢

转载自blog.csdn.net/qq_27782503/article/details/94479938