数据结构学习笔记——链表

之前说的顺序表是按照数据结构的存储结构也就是物理结构,是按照顺序存储的list

1.单链表:存放数据也存放指向下一个节点的指针

 

  • 节点(Node):每个节点包含两部分信息,一部分是存储的数据,另一部分是指向下一个节点的指针。
  • 指针(Pointer):这是一个地址,指向内存中的下一个节点,从而构成链表的结构。

在单链表中,每个节点的指针指向下一个节点,这样整个链表可以通过不同节点之间的指针链接起来。这种结构使得在链表中插入和删除节点的操作更为灵活,因为不需要像数组那样进行元素的移动。

总结:链表中的指针指向下一个节点,而节点本身包含数据和指向下一个节点的指针。

要获取链表中特定节点的数据,通常需要遍历链表直到找到目标节点,并从该节点中获取存储的数据。下面是获取节点数据的一般步骤:

步骤:

  1. 从链表头开始遍历

    • 从链表的头节点(第一个节点)开始,依次沿着每个节点的指针移动,直到找到目标节点为止。
  2. 比较节点内容

    • 在遍历过程中,通常会比较当前节点的数据是否与目标数据匹配(如果是根据数据来获取节点),或者比较节点的索引是否符合(如果是根据位置来获取节点)。
  3. 获取数据

    • 一旦找到目标节点,就可以访问该节点中存储的数据。

示例代码:

假设有一个简单的单链表数据结构,每个节点包含一个整数值和指向下一个节点的指针。

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# 创建一个链表:1 -> 2 -> 3 -> 4 -> 5
node5 = ListNode(5)
node4 = ListNode(4, node5)
node3 = ListNode(3, node4)
node2 = ListNode(2, node3)
node1 = ListNode(1, node2)

# 定义一个函数来获取链表中第index个节点的数据
def get_node_data(head, index):
    current = head
    count = 0
    
    while current:
        if count == index:
            return current.val
        current = current.next
        count += 1
    
    # 如果超出链表长度,返回 None 或者抛出异常,视需求而定
    return None

# 获取第3个节点的数据(索引从0开始)
index = 2
data = get_node_data(node1, index)
print(f"The data at index {index} is: {data}")  # 输出 The data at index 2 is: 3

在上面的示例中,我们定义了一个简单的链表,然后编写了一个函数 get_node_data 来获取链表中指定索引位置节点的数据。在函数中,通过遍历链表并使用计数器来找到目标节点,一旦找到,就返回节点的值 val

这种方法是典型的链表访问方式,其中需要考虑链表可能为空或索引超出链表长度的情况。

单链表的插入删除

头结点(Head Node)在链表中并不存储实际的数据元素,它是用来指示链表的起始位置的特殊节点。在很多实现中,头结点通常是一个空节点(不包含有效数据),或者是一个包含初始值但不被认为是链表数据的节点。头结点的作用是简化链表操作的实现,特别是对于空链表或者需要插入操作时。

头结点的作用:

  1. 标识链表起点:头结点用来标志链表的开始位置,它的 next 指针指向链表中的第一个真实节点。

  2. 简化插入操作:在链表中插入节点时,如果头结点存在,可以统一处理插入位置为链表头部的情况,而不需要单独处理空链表的特殊情况。

  3. 辅助空链表处理:对于空链表,头结点可以简化对链表的操作,使得可以通过头结点的 next 指针直接操作链表的第一个节点。

头结点不存放数据:

通常情况下,头结点本身并不存放有效数据。它的主要作用是作为链表操作的辅助节点,用于管理链表结构而不是存储用户数据。因此,如果你要访问链表中的实际数据,需要从头结点的 next 指针开始遍历链表,直到找到实际存储数据的节点为止。

要分析清楚每一段代码都是干啥,宏观把握。

下面是不带头节点的链表按位序插入:

这也是为什么带头结点的代码为什么麻烦,因为要判断i=1,需要把L的指向更改到新的分配的地址。

前插操作,没有头指针很难实现,但是呢
 

把现在结点的值放到要插入的点,把之前的值改成要插入的值。

删除操作类似,

但是呢 如果要删除的结点是最后一个结点,指向的是null,会发生错误:

单链表的局限性:无法逆向操作。

单链表的查找:(基于带头结点)

 

只能依次扫描!!

双链表

多一个指针,prior指向之前的指针

循环单链表

静态链表

数组下表为0的结点是头结点,分配了一块连续的内存空间,各个结点集中安置。

用数组的方式实现的链表,容量不可变的

顺序表和链表的对比(总结)

逻辑结构都是线性表,一对一的关系。

创建:链表更方便

销毁:链表要依次free;对于静态分配的话,系统自动回收,对于malloc要配合free适用。

增删:顺序表把插入位置前后统一移动;链表只需要修改指针;时间复杂度都是O(n),但是考虑实际的化,链表效率会更高。

查找:顺序表按值查找O(log2(n)),对于链表都是O(n),链表更好

经常增删用链表。经常查询搜索用顺序表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值