【python】Single / Single Cycle / Double Link List

https://www.bilibili.com/video/av53583801/?p=20
学习笔记


1 Single Link List

在这里插入图片描述
图片来源:https://www.bilibili.com/video/av53583801/?p=19

class Node(object):
    def __init__(self,value,next=None):
        self.value = value
        self.next = next

class SingleLinkList(object):
    def __init__(self,node=None):
        self.__head = node # 初始化头指针指向 None

    def is_enmpty(self):
        """链表是否为空"""
        return self.__head==None

    def length(self):
        """链表长度"""
        cur = self.__head # 头节点
        count = 0
        while(cur): # 当前指针不指向 None 时候
            count+=1
            cur = cur.next
        return count

    def travel(self):
        """遍历链表"""
        cur = self.__head # 头节点,私有变量
        while (cur):  # 当前指针不指向 None 时候
            print(cur.value,end=" ")
            cur = cur.next
        print("")

    def append(self,item):
        """链表尾部添加元素"""
        node = Node(item,None) # 创建一个新节点

        if self.is_enmpty(): # 如果是空链表,头指针直接指向新的节点
            self.__head = node
        else:
            cur = self.__head
            while(cur.next):
                cur = cur.next
            cur.next = node

    def add(self,item):
        """链表头部添加元素"""
        node = Node(item) # 新建节点
        node.next = self.__head # 新建结点指向原来的第一个节点
        self.__head = node # 头部节点指向新建的节点(新的第一个节点)

    def insert(self,pos,item):
        """链表任意位置添加元素,位置从0开始"""
        if pos <= 0: # 插入的位置小于等于0,则等价于在链表头部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大于链表长度,等价于在链表尾部添加元素
            self.append(item)
        else:
            cur = self.__head
            for i in range(pos-1): # 遍历到插入到位置的前一个位置
                cur = cur.next
            node = Node(item) # 新建一个节点
            node.next = cur.next
            cur.next = node

    def search(self,item):
        """查找元素是否在链表中,返回布尔值"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                return True
            else:
                return False

    def remove(self,item):
        """移除第一个匹配的元素"""
        """单指针,cur.next = cur.next.next"""
        """双指针,pre.next = cur.next"""
        pre = None
        cur = self.__head
        while(cur):
            if cur.value == item:
                if cur == self.__head: # 匹配上了第一个节点,此时 pre 为空,没有next,所以单独讨论
                    self.__head = cur.next
                else:
                    pre.next = cur.next # 删除节点
                break # 删完以后就应该退出
            else: # 向后走一步
                pre = cur
                cur = cur.next

if __name__ == "__main__":
    ll = SingleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

output

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

2 Double Link List

在这里插入图片描述
图片来源:https://www.bilibili.com/video/av53583801/?p=23

is_enmptylengthtravelsearch 同 Single Link List,完全可以继承 Single Link List 类!remove 改动较大,注意要 remove 的元素是最后一个节点的时候的情况

class Node(object):
    def __init__(self,value,pre=None,next=None):
        self.value = value
        self.pre = pre
        self.next = next

class DoubleLinkList(object):
    def __init__(self,node=None):
        self.__head = node # 初始化头指针指向 None

    def is_enmpty(self): # 同 SingleLinkList
        """链表是否为空"""
        return self.__head==None

    def length(self): # 同 SingleLinkList
        """链表长度"""
        cur = self.__head # 头节点
        count = 0
        while(cur): # 当前指针不指向 None 时候
            count+=1
            cur = cur.next
        return count

    def travel(self): # 同 SingleLinkList
        """遍历链表"""
        cur = self.__head # 头节点,私有变量
        while (cur):  # 当前指针不指向 None 时候
            print(cur.value,end=" ")
            cur = cur.next
        print("")

    def append(self,item):
        """链表尾部添加元素"""
        node = Node(item,None) # 创建一个新节点

        if self.is_enmpty(): # 如果是空链表,头指针直接指向新的节点
            self.__head = node # 注意只有一个node的话,pre 和 next 都是空,不要以为 pre 是 head
        else:
            cur = self.__head
            while(cur.next):
                cur = cur.next
            cur.next = node
            node.pre = cur # 相比于 SingleLinkList 新增

    def add(self,item):
        """链表头部添加元素"""
        node = Node(item) # 新建节点
        node.next = self.__head # 新建结点指向原来的第一个节点
        self.__head.pre = node # 相比于 SingleLinkList 新增
        self.__head = node # 头部节点指向新建的节点(新的第一个节点)

    def insert(self,pos,item):
        """链表任意位置添加元素,位置从0开始"""
        if pos <= 0: # 插入的位置小于等于0,则等价于在链表头部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大于链表长度,等价于在链表尾部添加元素
            self.append(item)
        else: # 相比与 SingleLinkList
            cur = self.__head
            for i in range(pos): # 遍历到插入的位置
                cur = cur.next
            node = Node(item) # 新建一个节点
            node.next = cur # 先让新建的节点搭在原来的列表上
            node.pre = cur.pre
            cur.pre = node # 再断开原有链表的链接,搭在新建列表上
            node.pre.next = node

    def search(self,item): # 同 SingleLinkList
        """查找元素是否在链表中"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                return True
            else:
                return False

    def remove(self,item):
        """移除第一个匹配的元素"""
        """不同于 singleLinkList,这里不需要定义两个指针了"""
        cur = self.__head
        while(cur):
            if cur.value == item:
                if cur == self.__head: # 匹配上了第一个节点,此时 pre 为空,没有next,所以单独讨论
                    self.__head = cur.next
                else:
                    cur.pre.next = cur.next # 删除节点
                    if cur.next: # 这里判断是否是最后一个节点(最后一个节点的next为none,none没有pre)
                        cur.next.pre = cur.pre
                break # 删完以后就应该退出
            else: # 向后走一步
                if cur.next:
                    cur = cur.next

if __name__ == "__main__":
    ll = DoubleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

结果

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

3 Single Cycle Link List

在这里插入图片描述
图片来源:https://www.bilibili.com/video/av53583801/?p=25

Single Cycle Link List 在 Single Link List 的基础上改动还是比较大的,特别是 remove 的时候。search 同 Single Link List

class Node(object):
    def __init__(self,value,next=None):
        self.value = value
        self.next = next

class SingleCycleLinkList(object):
    def __init__(self,node=None):
        self.__head = node  # 初始化头指针指向 None
        if node: # 新建一个不为空的循环链表
            node.next = self.__head

    def is_enmpty(self): # 同 SingleLinkList
        """链表是否为空"""
        return self.__head==None

    def length(self):
        """链表长度"""
        if self.is_enmpty():
            return 0
        else:
            cur = self.__head # 头节点
            count = 1
            while(cur.next != self.__head):
                count+=1
                cur = cur.next
            return count

    def travel(self):
        """遍历链表"""
        if self.is_enmpty():
            return 0
        else:
            cur = self.__head # 头节点,私有变量
            while (cur.next != self.__head):  # 当前指针不指向 None 时候
                print(cur.value,end=" ")
                cur = cur.next
            print(cur.value,end=" ") # 退出循环的时候,cur 指向尾节点,但尾节点的元素并没有打印
            print("")

    def append(self,item):
        """链表尾部添加元素"""
        node = Node(item,None) # 创建一个新节点
        if self.is_enmpty(): # 如果是空链表,头指针直接指向新的节点
            self.__head = node
            node.next = node  # 新增节点自己指向自己形成cycle
        else:
            cur = self.__head
            while(cur.next != self.__head): # 遍历让cur指向尾节点
                cur = cur.next
            cur.next = node
            node.next = self.__head # 形成 cycle

    def add(self,item):
        """链表头部添加元素"""
        node = Node(item)  # 新建节点
        if self.is_enmpty():
            self.__head = node # 头指向新增的节点
            node.next = node # 新增节点自己指向自己形成cycle
        else:
            cur = self.__head
            while(cur.next!=self.__head): # 遍历让cur指向尾节点(因为是循环链表,所以尾部要指向新增的头)
                cur = cur.next
            node.next = self.__head # 新建结点指向原来的第一个节点
            self.__head = node # 头部节点指向新建的节点(新的第一个节点)
            cur.next = node #相比于 SingleLinkList 新增,尾部指向头部


    def insert(self,pos,item): # 同 SingleLinkList
        """链表任意位置添加元素,位置从0开始"""
        if pos <= 0: # 插入的位置小于等于0,则等价于在链表头部添加元素
            self.add(item)
        elif pos > self.length()-1: # 大于链表长度,等价于在链表尾部添加元素
            self.append(item)
        else:
            cur = self.__head
            for i in range(pos-1): # 遍历到插入到位置的前一个位置
                cur = cur.next
            node = Node(item) # 新建一个节点
            node.next = cur.next
            cur.next = node

    def search(self,item):
        """查找元素是否在链表中,返回布尔值"""
        if self.is_enmpty():
            return False
        else:
            cur = self.__head
            while(cur.next!=self.__head): # 遍历 1-n-1
                if cur.value == item:
                    return True
                else:
                    return False
            if cur.value == item: # 同travel,退出循环的时候,cur 指向尾节点,但尾节点的元素并没有遍历
                return True
            else:
                return False

    def remove(self,item):
        """移除第一个匹配的元素"""
        if self.is_enmpty():
            return
        else:
            pre = None
            cur = self.__head
            while(cur.next!=self.__head):
                if cur.value == item: ### 匹配到了第一个
                    if cur == self.__head: # 匹配上了第一个节点,此时 pre 为空,没有next,所以单独讨论
                        end = self.__head
                        while(end.next!=self.__head): # 遍历定位到尾部指针
                            end = end.next
                        self.__head = self.__head.next
                        end.next = self.__head
                    else: ### 匹配到了 2-n-1,删除操作同单链表
                        pre.next = cur.next # 删除节点
                    return # 删完以后就应该退出
                else: # 向后走一步
                    pre = cur
                    cur = cur.next
            if cur.value == item: ### while 循环外表示遍历到了最后一个节点(只有一个节点/不止一个节点),匹配到了第n个
                if cur.next == self.__head: # 这表示匹配的是链表中最后一个节点
                    pre.next = self.__head
                else: #cur == self.__head: # 链表只有一个节点,此时 pre 为 none,不能用上面的一句话
                    self.__head = None



if __name__ == "__main__":
    ll = SingleCycleLinkList()
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(1)
    print("is_empty:",ll.is_enmpty())
    print("length",ll.length())

    ll.append(2)
    ll.add(8)
    ll.append(3)
    ll.append(4)
    ll.append(5)
    ll.append(6)
    ll.insert(-1,9)
    ll.insert(3,100)
    ll.insert(10,200)
    ll.travel()

    result = ll.search(9)
    print(result)
    result = ll.search(300)
    print(result)

    ll.remove(9)
    ll.travel()
    ll.remove(200)
    ll.travel()
    ll.remove(100)
    ll.travel()

output

is_empty: True
length 0
is_empty: False
length 1
9 8 1 100 2 3 4 5 6 200 
True
False
8 1 100 2 3 4 5 6 200 
8 1 100 2 3 4 5 6 
8 1 2 3 4 5 6 

4 链表的应用

(1)内存管理

LRU cache 的典型实现是用双向链表加上 hash table 做的

Least Recently Used,即最近最少使用,是一种常用的页面置换算法(内存管理方法)

(2)文件系统

你格式化硬盘时会让你选择 fat32、ntfs格式(B+tree),其实就是让你选择存储链表空间规模及格式。为提高系统效率,你有时需要做文件碎片整理,这说明一个文件的数据不一定是连续存放的,那么操作系统是如何知道把不连续的数据合成一个文件提供给你的呢?其实就是通过访问一个指向文件数据区的链表得到的。操作系统通常会把一个硬盘的文件区域划分为3个部分:簇链表空间(FAT)/根目录区(Root)、数据区,而数据区是按指定空间大小分为一簇簇,并编号,假入一个文件数据分布在1/3/5簇,那么目录区该文件目录后面会跟随一个指针指向1,接着在FAT编号为1的指针指向3,3指向5,5没有指向,通常链表是以NULL结束,但文件系统是以-1结束。所以文件系统通过访问目录(头指针head)、FAT区(链表区相当去申请到的堆空间)得到一个完整的链表1-3-5,再通过计算获取文件数据所在的簇,最后得到数据。由于链表属于环环相扣的串行数据,任何一环断开,这个链条就坏了,所以文件系统通常会有一个备份FAT,确保一个损坏可以恢复。

作者:lowxiong
链接:https://www.zhihu.com/question/60724366/answer/179567609
来源:知乎

(3)git

git checkout、delete branch、merge、rebase这些基本上都是以链表操作为主

5 小结

Single Link List 是情况最简单的,在这个的基础上,我们改进实现了 Double Link List 和 Single Cycle Link List,三种链表的测试样例是一样的,所以如果 coding 没有问题的话,结果是一样的!
主要实现了如下功能:

  • is_empty():是否为空
  • length():链表的长度
  • traval():遍历链表,打印出来
  • search(item):查找元素是否在链表中,返回 boolean 值
  • add(item):在链表的第一个位置添加元素
  • append(item):在链表的最后一个位置添加元素
  • insert(pos,item):在链表的指定位置插入元素
  • remove(item):删除掉匹配到的第一个元素

在 coding 的时候一定要注意以下的边界情况是否要单独讨论:

  • 链表为空
  • 链表只有一个元素
  • 要对链表的第一个元素进行操作
  • 要对链表的最后一个元素进行操作

然后插入的时候,最好不要先动原来的链表,先让新节点搭上去,然后再改原来的链表。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值