数据结构学习——04—循环链表

在这里插入图片描述
循环链表与单向链表的区别就是尾结点的地址指向问题,尾结点需要指向首节点的数据,这里是实现的一个需要注意的地方,增删改查这些操作都要单独考虑尾结点的指向问题。

首先实现循环链表的单个节点

# 节点实现
class Node(object):
    # 单节点
    def __init__(self, item):
        # 存放数据元素
        self.item = item
        
        # next下一个节点的标识
        self.next = None  # 实现单个节点,暂定为None

这里需要注意的就是下一个节点的标识问题

实现单项循环链表:

# 单项循环链表
class SinCycLinkList(object):
    # 空的链表  self.__head = None
    # 非空链表   node.next = node
    def __init__(self, node=None):
        self.__head = None
        
        if node:
           node.next = node      # 尾节点指向node

这里的尾节点指向node是重要部分。只要不是空链表(if node;),尾节点就要指向数据(可能是本身)

实现循环链表的增删改查等函数:
1、判断链表是否为空

# 判断链表是否为空
    def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False

和单向链表实现相同。

2、计算链表长度函数:

def length(self):
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        
        count = 1   # 不同于单向链表   因为找到尾节点,此时的cur所处的节点不在计算数目范围内
        
        # 考虑空链表的情况
        if self.is_empty():
            return 0
        # 考虑非空情况
        while cur.next != self.__head:
            count += 1
            cur = cur.next                # 游标后移一位
        return count

这里需要注意的是count的初始值的设定问题,因为循环到尾节点的时候,此时cur所指的尾节点是不在循环中的,就不会计算在count中,所以初始值需要设定为1.

3、遍历链表并输出:

def travel(self):
        cur = self.__head
        
        # 考虑空链表情况
        if self.is_empty():
            return None
        
        # 非空链表情况
        while cur.next != self.__head:
            print(cur.item, end=',')     # 这两句的顺序不能换,否则会少打印节点值
            # cur.next = self.__head     # 这里出现错误 (遍历的时候不需要对地址指向做修改,链表内元素不改动)
            cur = cur.next               # 游标继续移动  继续循环
            
        print(cur.item)       # 打印当前游标所指尾节点的数据  退出循环需要打印最后一个节点
        print('')            # 输出换行

这里需要注意的是语句顺序和尾节点的打印问题:第一、print语句和游标的移动不能顺序错误,否则会有首节点不会打印出来,元素缺少。第二、循环到尾节点的时候,此时的尾节点是没进入到循环中并打印输出的,需要在循环外输出一次尾节点才行。

4、循环链表头部添加节点:

# 在头部添加一个新节点
    def add(self, item):
        # 实例化新的节点对象
        node = Node(item)
        
        # 考虑空链表情况
        if self.is_empty():
            self.__head = node            # 首地址指向新的节点
            node.next = self.__head       # 新节点的地址指向自己,也是首地址
            # 或者改为 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                # 第三步:尾节点指向新添加的节点
            # 或者可以写为  cur.next = self.__head

头部添加新节点需要注意的就是尾节点的指向改变了,需要指向新的节点。
另一个就是任何时候都要考虑特殊情况:空链表的情况
头部插入节点示意图如下:
头部插入节点示意图
5、在尾部添加一个新节点

# 在尾部添加一个新节点
    def append(self, item):
        node = Node(item)   # 实例化新节点对象
        
        # 考虑空链表情况,与add中情况相同
        if self.is_empty():
            self.__head = node  # 首地址指向新的节点
            node.next = self.__head  # 新节点的地址指向自己,也是首地址   # 或者改为 node.next = node
            
        # 考虑非空链表情况
        else:
            cur = self.__head
            while cur.next != self.__head:        # 查找尾节点临界条件
                cur = cur.next
                
            # 找到尾节点
            node.next = self.__head     # 新添加节点变为尾节点,指向首地址
            cur.next = node             # 之前尾节点地址指向变更为新节点的
            

需要注意的是:新添加尾结点的地址指向首节点,还有空链表的情况下添加新节点
链表尾部添加节点示意图
在这里插入图片描述

6、在任一部位插入新节点实现:

 # 在任一部位插入新节点      pos是插入的部位,item是插入的值
    def insert(self, pos, item):
        node = Node(item)
        
        # 插入位置为负数,即从最前插值,add函数实现
        if pos <= 0:
            self.add(item)
        # 插入位置超出链表长度,从最后插值,append函数实现
        elif pos > self.length() - 1:   # 这里需要注意
            self.append(item)
        # 中间位置插值
        else:
            count = 0
            cur = self.__head
            while count < (pos - 1):
                count += 1
                # 游标移动方向
                cur = cur.next
            # 第一步:新插入节点地址指向后一个节点
            node.next = cur.next
            # 第二步:前一个节点地址指向新的插入节点
            cur.next = node
     

这里需要注意:插入位置的值代表的不同意思:负值:从最前插值,add函数可以实现;超出链表长度值:从最后插值,append函数可以实现

任意部位插入新节点示意图:
在这里插入图片描述

7、查找节点是否存在实现:

# 查找一个节点是否存在
    def search(self, item):
        # 考虑是否为空链表
        if self.is_empty():
            return False
        
        cur = self.__head
        # 考虑非空链表
        while cur.next != self.__head:
            if cur.item == item:
                return True
            else:
                # 游标继续后移
                cur = cur.next
        # 查找到尾结点  检查尾结点是否
        if cur.item == item:
            return True
        # 所有都没有的情况
        return False

需要注意:在非空链表情况下,要检查尾部节点是否符合条件,当所有条件都不符合的时候,return False

8、删除一个节点实现:

# 删除一个节点
    def remove(self, item):
        pre = None
        cur = self.__head
        # 考虑空链表情况
        if self.is_empty():
            return None
        '''
        # 考虑非空情况
        while cur.next != self.__head:     # 删除元素不是尾结点
            if cur.item == item:           # 当前找到的是要删除的元素情况下
                # 删除第一个元素的情况  找到第一个地址
                if cur == self.__head:     # 条件错误?
                    self.__head = cur.next
                else:     # 删除的不是第一个元素
                    # 删除元素
                    # 将删除元素的前一项的地址指向当前要删除元素指向的地址
                    pre.next = cur.next             # 节点地址指向后一个节点
                    # pre.next = pre.next.next     与上式表达意思相同
                break
            else:
                pre = cur                  # 游标继续移动
                cur = cur.next
            
            # 删除节点是尾节点
            if cur.item == item:                 # 这里的缩进影响链表删除元素后面数据的打印
                pre.next = self.__head           # 尾结点指向首地址
        '''
            
        # 头节点的元素就是要删除的元素
        if cur.item == item:
            # 链表中的节点不止一个
            if cur.next != self.__head:
                while cur.next != self.__head:  # 循环查找不是尾节点
                    cur = cur.next
                    
                # 循环结束  cur指向尾结点
                cur.next = self.__head.next
                self.__head = cur.next
                
            else:
                self.__head = None
                
        # 第一个节点不是要删除的
        else:
            while cur.next != self.__head:
                if cur.item ==item:      # 除了首节点,之后的是要删除的节点
                    # 删除
                    pre.next = cur.next
                    return
                else:  # 不是这个节点要被删除
                    # 游标继续移动   继续查找删除元素
                    pre = cur
                    cur = cur.next
            
            if cur.item == item:           # 当尾节点就是要删除的元素时候
                pre.next = cur.next

上面注释掉的一部分代码是另一种思路,但是其中的条件好像写的有问题,还没有修改的思路,能实现一部分功能。
重点是非空链表情况下如何选择不同情况的条件,实现功能的代码是头节点的元素就是要删除的元素的前提下,判断链表中有一个元素还是多个元素,在头节点的元素不是要删除的元素的前提下,查找除了尾部节点的要删除的节点数据,还有就是在循环之外要判断尾节点是否是要删除的元素在这里插入图片描述
9、整体实现代码如下:

# -*- encoding: utf-8 -*-
"""
@File    : single_cyc_link_list.py
@Time    : 2019/11/11 20:08
@Author  : chen

"""
# 节点实现
class Node(object):
    # 单节点
    def __init__(self, item):
        # 存放数据元素
        self.item = item
        
        # next下一个节点的标识
        self.next = None  # 实现单个节点,暂定为None

# 单项循环链表
class SinCycLinkList(object):
    # 空的链表  self.__head = None
    # 非空链表   node.next = node
    def __init__(self, node=None):
        self.__head = None
        
        if node:
           node.next = node      # 尾节点指向node

    # 判断链表是否为空
    def is_empty(self):
        if self.__head == None:
            return True
        else:
            return False
        
    def length(self):
        # cur游标  指向首节点,用来遍历  相当于指针
        cur = self.__head  # 游标指向首地址
        
        count = 1   # 不同于单向链表   因为找到尾节点,此时的cur所处的节点不在计算数目范围内
        
        # 考虑空链表的情况
        if self.is_empty():
            return 0
        # 考虑非空情况
        while cur.next != self.__head:
            count += 1
            cur = cur.next                # 游标后移一位
        return count
    
    def travel(self):
        cur = self.__head
        
        # 考虑空链表情况
        if self.is_empty():
            return None
        
        # 非空链表情况
        while cur.next != self.__head:
            print(cur.item, end=',')     # 这两句的顺序不能换,否则会少打印节点值
            # cur.next = self.__head     # 这里出现错误 (遍历的时候不需要对地址指向做修改,链表内元素不改动)
            cur = cur.next               # 游标继续移动  继续循环
            
        print(cur.item)       # 打印当前游标所指尾节点的数据  退出循环需要打印最后一个节点
        print('')            # 输出换行
        
    # 在头部添加一个新节点
    def add(self, item):
        # 实例化新的节点对象
        node = Node(item)
        
        # 考虑空链表情况
        if self.is_empty():
            self.__head = node            # 首地址指向新的节点
            node.next = self.__head       # 新节点的地址指向自己,也是首地址
            # 或者改为 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                # 第三步:尾节点指向新添加的节点
            # 或者可以写为  cur.next = self.__head

    # 在尾部添加一个新节点
    def append(self, item):
        node = Node(item)   # 实例化新节点对象
        
        # 考虑空链表情况,与add中情况相同
        if self.is_empty():
            self.__head = node  # 首地址指向新的节点
            node.next = self.__head  # 新节点的地址指向自己,也是首地址   # 或者改为 node.next = node
            
        # 考虑非空链表情况
        else:
            cur = self.__head
            while cur.next != self.__head:        # 查找尾节点临界条件
                cur = cur.next
                
            # 找到尾节点
            node.next = self.__head     # 新添加节点变为尾节点,指向首地址
            cur.next = node             # 之前尾节点地址指向变更为新节点的
            
    # 在任一部位插入新节点      pos是插入的部位,item是插入的值
    def insert(self, pos, item):
        node = Node(item)
        
        # 插入位置为负数,即从最前插值,add函数实现
        if pos <= 0:
            self.add(item)
        # 插入位置超出链表长度,从最后插值,append函数实现
        elif pos > self.length() - 1:   # 这里需要注意
            self.append(item)
        # 中间位置插值
        else:
            count = 0
            cur = self.__head
            while count < (pos - 1):
                count += 1
                # 游标移动方向
                cur = cur.next
            # 第一步:新插入节点地址指向后一个节点
            node.next = cur.next
            # 第二步:前一个节点地址指向新的插入节点
            cur.next = node
            
    # 查找一个节点是否存在
    def search(self, item):
        # 考虑是否为空链表
        if self.is_empty():
            return False
        
        cur = self.__head
        # 考虑非空链表
        while cur.next != self.__head:
            if cur.item == item:
                return True
            else:
                # 游标继续后移
                cur = cur.next
        # 查找到尾结点  检查尾结点是否
        if cur.item == item:
            return True
        # 所有都没有的情况
        return False
    
    # 删除一个节点
    def remove(self, item):
        pre = None
        cur = self.__head
        # 考虑空链表情况
        if self.is_empty():
            return None
        '''
        # 考虑非空情况
        while cur.next != self.__head:     # 删除元素不是尾结点
            if cur.item == item:           # 当前找到的是要删除的元素情况下
                # 删除第一个元素的情况  找到第一个地址
                if cur == self.__head:
                    self.__head = cur.next
                else:     # 删除的不是第一个元素
                    # 删除元素
                    # 将删除元素的前一项的地址指向当前要删除元素指向的地址
                    pre.next = cur.next             # 节点地址指向后一个节点
                    # pre.next = pre.next.next     与上式表达意思相同
                break
            else:
                pre = cur                  # 游标继续移动
                cur = cur.next
            
            # 删除节点是尾节点
            if cur.item == item:                 # 这里的缩进影响链表删除元素后面数据的打印
                pre.next = self.__head           # 尾结点指向首地址
        '''
            
        # 头节点的元素就是要删除的元素
        if cur.item == item:
            # 链表中的节点不止一个
            if cur.next != self.__head:
                while cur.next != self.__head:  # 循环查找不是尾节点
                    cur = cur.next
                    
                # 循环结束  cur指向尾结点
                cur.next = self.__head.next
                self.__head = cur.next
                
            else:
                self.__head = None
                
        # 第一个节点不是要删除的
        else:
            while cur.next != self.__head:
                if cur.item ==item:
                    # 删除
                    pre.next = cur.next
                    return
                else:
                    # 游标继续移动
                    pre = cur
                    cur = cur.next
            
            if cur.item == item:
                pre.next = cur.next
        
s = SinCycLinkList()
print(s.is_empty())
print(s.length())


s.add(1)
s.add(2)
s.add(33)
s.add(44)
s.add(666)

s.append(999)
s.append(888)


s.travel()
print(s.is_empty())
print(s.length())


print(s.search(120))

s.remove(1)
s.remove(666)
s.travel()
发布了50 篇原创文章 · 获赞 9 · 访问量 2103

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103043722