python--数据结构--链表

最近会更新很多内容,感兴趣的友友支持一下吧!!

一、链表介绍

概述:

属于线性结构, 即: 每个节点都有1个父节点(前驱节点) 和 1个子节点(后继节点)

链表可以看做是 用链条(一根绳) 把节点连接起来的 一种结构.

节点介绍(此处以 单链表举例):

由 元素域(数值域) 和 地址域(链接域)组成, 其中 数值域存储的是 数值, 地址域存储的是 下个节点的地址.

链表根据节点不同, 分类如下:

单向链表:

每个节点由 1个数值域和1个地址域组成, 且每个节点的地址域指向下个节点的地址. 最后1个节点的地址域为: None

单向循环链表:

每个节点由 1个数值域和1个地址域组成, 且每个节点的地址域指向下个节点的地址. 最后1个节点的地址域为: 第1个节点的地址

双向链表:

每个节点由 1个数值域和2个地址域组成, 且每个节点的地址域分别指向前1个节点的地址 和 后1个节点的地址.

第1个节点的(前)地址域为: None, 最后1个节点的(后)地址域为: None

双向循环链表:

每个节点由 1个数值域和2个地址域组成, 且每个节点的地址域分别指向前1个节点的地址 和 后1个节点的地址.

第1个节点的(前)地址域为: 最后1个节点的地址, 最后1个节点的(后)地址域为: 第1个节点的地址.

二、案例:

自定义代码, 模拟(单向)链表.

分析:

节点类: SingleNode

属性:

item 表示: 数值域

next 表示: 地址域, 即: 指向下个节点的地址的.

链表类: SingleLinkedList

属性:

head 表示: 头结点, 即: 默认指向链表第1个节点的 地址.

函数:

is_empty(self) 链表是否为空

length(self) 链表长度

travel(self. ) 遍历整个链表

add(self, item) 链表头部添加元素

append(self, item) 链表尾部添加元素

insert(self, pos, item) 指定位置添加元素

remove(self, item) 删除节点

search(self, item) 查找节点是否存在

示例代码:

复制代码
# 1. 定义SingleNode, 表示: 节点类.
class SingleNode(object):
    # 1.1 初始化节点的属性.
    def __init__(self, item):
        # 1.2 接收由用户传入的: 数值, 放到数值域中.
        self.item = item
        # 1.3 地址域默认为: None
        self.next = None


# 2. 定义SingleLinkedList, 表示: 链表类.
class SingleLinkedList(object):
    # 2.1 初始化链表的属性.
    def __init__(self, head=None):
        # 2.2 默认: 头结点为: None
        self.head = head

    # 2.2 is_empty(self) 链表是否为空
    def is_empty(self):
        # 判断依据: 头结点head是否为None
        # 写法1: if方式
        # if self.head is None:
        #     return True     # 链表为空
        # else:
        #     return False    # 链表不为空

        # 写法2: 三元表达式
        # return True if self.head is None else False

        # 写法3: 直接返回即可.
        # return self.head == None
        return self.head is None

    # 2.3 length(self) 链表长度
    def length(self):
        # 1. 定义变量count, 记录: 链表长度.
        count = 0
        # 2. 定义变量cur, 表示: 当前的节点, 初值为: 头结点.
        cur = self.head
        # 3. 判断当前节点是否为空, 不为空就一直遍历.
        while cur is not None:
            # 4. 走到这里, 说明当前节点不为空, 就: 计数器 + 1, 然后设置当前节点为它的下个节点.
            count += 1
            cur = cur.next
        # 5. 返回结果(链表的长度).
        return count


    # 2.4 travel(self ) 遍历整个链表
    def travel(self):
        # 1.  定义变量cur, 表示: 当前的节点, 初值为: 头结点.
        cur = self.head
        # 2. 判断当前节点是否为空, 不为空就一直遍历.
        while cur is not None:
            # 3. 走到这里, 说明当前节点不为空.
            print(cur.item)     # 打印当前节点的 数值域
            cur = cur.next      # 设置当前节点为它的下个节点.

    # 2.5 add(self, item) 链表头部添加元素
    def add(self, item):
        # 1. 把传入的item(元素值)封装成: 节点
        new_node = SingleNode(item)
        # 2. 设置新节点的 地址域为: 头结点.
        new_node.next = self.head
        # 3. 设置新节点为: 新的头结点.
        self.head = new_node

    # 2.6 append(self, item) 链表尾部添加元素
    def append(self, item):
        # 1.把要添加的元素值封装成: 节点.
        new_node = SingleNode(item)
        # 2. 判断当前链表是否为空.
        if self.is_empty():
            # 3. 走到这里, 说明链表为空, 直接设置新节点为: 头结点即可.
            self.head = new_node
        else:
            # 4. 走到这里, 说明链表不为空, 需找到最后1个节点.
            cur = self.head     # 设置当前节点为: 头结点.
            # 5. 只要当前节点的地址域不为空, 就说明还有下个节点, 就一直遍历即可.
            while cur.next is not None:
                # 当前节点不为空, 继续往下找.
                cur = cur.next
            # 6. 走到这里, 说明cur的下个节点为空, 即: cur已经为最后1个节点了.
            # 设置最后1个节点(cur)的地址域为: 新的节点.
            cur.next = new_node

    # 2.7 insert(self, pos, item) 指定位置添加元素
    def insert(self, pos, item):
        # 1. 判断pos(要插入的位置)是否合法.
        if pos <= 0:
            # 往头部添加.
            self.add(item)
        elif pos >= self.length():
            # 往尾部添加
            self.append(item)
        else:
            # 2. 走到这里, 说明pos是合法位置, 即: 链表的中间位置.
            # 解题核心: 找到要插入的位置的前边那个节点, 即: pos - 1位置的那个节点.
            # 3. 定义变量cur, 表示: 要插入的位置前 那个节点.
            cur = self.head
            # 4. 定义变量count, 表示: 要插入的位置前 那个节点的 索引.
            count = 0
            # 5. 只要cur不是要插入节点 前一个节点, 就一直遍历.
            while count < pos - 1:  # 如果不减1, 拿的是插入后. pos位置后的哪个元素.
                # 走到这里, 说明cur不是要找的元素, 就继续往后找.
                cur = cur.next
                # 计数器 要 + 1
                count += 1
            # 6. 走到这里, 说明cur就是 要插入位置前的那个节点.
            # 先设置 新节点的地址域为: cur节点的地址域
            new_node = SingleNode(item)
            new_node.next = cur.next
            # 再设置 cur节点的地址域为: 新节点
            cur.next = new_node

    # 2.8 remove(self, item) 删除节点
    def remove(self, item):
        # 1. 定义变量, 表示: 要被删除的元素.
        cur = self.head     # 从头结点往后找.
        # 2. 定义变量, 表示: 要被删除的节点的 前1个节点.
        pre = None          # 初值为None
        # 3. 具体的判断动作, 只要当前节点不为空, 就一直遍历.
        while cur is not None:
            # 4. 判断当前节点是否是要被删除的节点.
            if cur.item == item:
                # 走这里, 说明cur就是要被删除的节点.
                # 5. 判断cur是否是头结点, 如果是头结点, 直接设置它的地址域 为 新的头结点即可.
                if cur == self.head:
                    self.head = cur.next
                else:
                    # 6. 走这里, 说明cur不是头结点, 设置它的前1个节点的地址域 = cur的地址域即可.
                    pre.next = cur.next
                # 7. 删完以后, 记得: break
                break
            else:
                # 8. 走这里, 说明cur不是要被删除的节点, 我们继续往下找.
                pre = cur       # 当前节点已经是: 要被删除的节点的 前1个节点了
                cur = cur.next  # 当前节点变更为: 它的下个节点.

    # 2.9 search(self, item) 查找节点是否存在
    def search(self, item):
        # 1. 定义变量cur, 表示: 当前节点, 默认从头结点开始.
        cur = self.head
        # 2. 判断当前节点是否为空, 不为空就一直遍历.
        while cur is not None:
            # 3. 判断当前节点的 元素域 是否和 要查找的元素值相同.
            if cur.item == item:
                # 4. 走这里, 找到了, return True即可.
                return True
            # 5. 走到这里, 说明当前节点不是我们要的, 继续往下找.
            cur = cur.next
        # 6. 走到这里, 说明 没找到, return False即可.
        return False


# 3. 在main方法中测试
if __name__ == '__main__':
    # 3.1 测试 节点类.
    node1 = SingleNode('乔峰')
    print(node1)    # 地址: <__main__.SingleNode object at 0x0000015D0D48C5C0>
    # 3.2 打印该节点的属性
    print(node1.item)  # 数值域, 乔峰
    print(node1.next)  # 地址域, None
    print('-' * 21)

    # 3.2 测试 链表类
    linkedlist1 = SingleLinkedList()    # 空链表.
    print(linkedlist1.head)             # 打印链表的: 头结点
    print('-' * 21)

    linkedlist2 = SingleLinkedList(node1)
    # 打印链表的: 头结点, 这里是: node1节点, <__main__.SingleNode object at 0x0000015D0D48C5C0>
    print(linkedlist2.head)
    print(linkedlist2.head.item) # 打印 头结点的 数值域, 乔峰
    print(linkedlist2.head.next) # 打印 头结点的 地址域, None
    print('-' * 21)

    # 3.3 测试链表的方法: add()
    linkedlist2.add('虚竹')
    linkedlist2.add('段誉')

    # 3.4 测试链表的方法: append()
    linkedlist2.append('王语嫣')
    linkedlist2.append('李清露')

    # 3.5 测试链表的方法: insert()
    linkedlist2.insert(-2, '鸠摩智')
    linkedlist2.insert(20, '慕容复')
    linkedlist2.insert(3, '丁春秋')

    # 3.6 测试链表的方法: remove()
    linkedlist2.remove('乔峰')
    linkedlist2.remove('鸠摩智')

    # 3.7 测试链表的方法: search()
    print(linkedlist2.search('慕容复'))
    print(linkedlist2.search('鸠摩智'))
    print('#' * 21)

    # 3.8 测试链表的方法: is_empty()
    print(linkedlist1.is_empty())
    print(linkedlist2.is_empty())

    # 3.9 测试链表的方法: length()
    len1 = linkedlist1.length()
    len2 = linkedlist2.length()
    print(f'len1: {len1}')  # 0
    print(f'len2: {len2}')  # 1
    print('-' * 21)

    # 3.10 测试链表的方法: travel()
    linkedlist2.travel()      # 鸠摩智, 段誉, 虚竹, 丁春秋, 乔峰, 王语嫣, 李清露, 慕容复

运行结果:

<main.SingleNode object at 0x00000125ED554FE0>

乔峰

None


None


<main.SingleNode object at 0x00000125ED554FE0>

乔峰

None


True

False

#####################

True

False

len1: 0

len2: 6


段誉

虚竹

丁春秋

王语嫣

李清露

慕容复

相关推荐
im_AMBER14 分钟前
Leetcode 97 移除链表元素
c++·笔记·学习·算法·leetcode·链表
海奥华217 分钟前
Golang Channel 原理深度解析
服务器·开发语言·网络·数据结构·算法·golang
星火开发设计28 分钟前
链表详解及C++实现
数据结构·c++·学习·链表·指针·知识
CCPC不拿奖不改名34 分钟前
python基础:python语言的数据结构+面试习题
开发语言·数据结构·python·面试
CCPC不拿奖不改名1 小时前
Python基础:python语言中的文件操作+面试题目
开发语言·数据结构·人工智能·python·学习·面试·职场和发展
橘颂TA1 小时前
【剑斩OFFER】算法的暴力美学——力扣 43 题:字符串相乘
数据结构·算法·leetcode·职场和发展·哈希算法·结构与算法
lalala_lulu1 小时前
MySQL数据库存储引擎的数据结构(超详细版)
数据结构·数据库·mysql
漫随流水1 小时前
leetcode算法(199.二叉树的右视图)
数据结构·算法·leetcode·二叉树
黎雁·泠崖1 小时前
二叉树入门全攻略:从树的概念到遍历实现
c语言·数据结构
海奥华21 小时前
Golang Slice深度解析
开发语言·数据结构·后端·链表·golang