用数组模拟链表 acwing 826.单链表

链表

使用数组模拟链表

为什么要这么做而不是使用结构体加指针或者直接用stl中的list

其一 new运算符很慢很慢 如果使用常规的链表 大概率是会超时的

其二 使用数组去模拟链表 更能深入理解两者的性质

总结一下就是提速和加深理解

要注意的是 这个知识点是非常重要的 不是说学着模拟一下就够了

这种静态链表要比动态链表更考验思路 并且在后面的图论中 基本是使用数组模拟的链表进行操作的 可以说 这是后面算法的一个基础

用数组模拟链表呢 只要就是要清楚 只是模拟逻辑 物理位置就是连续的前驱后继 把这里搞清楚了就没什么问题了

先分析一下链表 把它的核心拎出来才能谈模拟

链表核心不过就是一个值域和一个指针域 它们是一个整体 组成一个“结点”

那么 我完全就可以去使用两个数组 应该去记录值 一个去记录下一个位置的下标

重点在于 如何让这两个数组始终能够对应 对 用下标 相同下标始终表示一个结点

n[i],ne[i]就是i.data,i.next

这个地方我本来疑惑了蛮久 后面发现其实物理位置没变

变的只是记录的下标(即ne[]中的东西) 它是静态的而非动态

打个比方吧 一开始以为的是对于

e 66 23 11

下标 0 1 2

ne 1 2 -1

在0~1间插入33

要变成

e 66 33 23 11

下标 0 1 2 3

ne 1 2 3 -1

这样的话 e中和ne中的下标就变了 不再对应了 那么ne中记录的东西就没意义了

实际上呢 只是模拟链表 新插入一个数的物理位置不需要插在真正的0~1下标之间

它只需要插在下标3处就好了 要变的只有ne 它存储的是下一个位置的下标 改这个就好了

e 66 23 11 33

下标 0 1 2 3

ne 3 2 -1 1

可以尝试把它抽象成链表 就是66-33-23-11

关键就是: 所有操作都是在数组的可用下标处进行的 改变ne中的值也就是抽象的一种指针(这里用下标抽象指针) 改变数组中各元素的逻辑位置关系 从而实现数组模拟链表

物理or逻辑!!!

只要这个物理位置没变 e和ne就永远会一一以下标对应 而只要对应了 这那个东西就可以抽象成一个“结点”

具体操作分析

刚只是大概知道怎么回事 很多细节还没有扣出来

显而易见就存在一个问题

这里我们并不知道是从头哪个地方开始的 只知道33后面是11 因为ne中33对应的是1

就是说 缺少一个head 这个head不一定就是下标0位置 意思就是说 对于这样的一个模拟链表 第一个元素不一定就是66

要使用一个head去表示头结点的下标 它可以指向(为)任何一个下标 并且支持链表的头插操作

然后呢 可以看出我们需要

一个数组e 记录节点的值

一个数组ne 记录某节点的下一结点的下标

还有 每次操作都是在数组的可用位置进行 那就需要一个指针(下标)去记录当前可用位置

即 需要一个idx 表示数组中可用于操作的位置

准备工作完成了

首先就是考虑初始化问题

void init()

{

head=-1;

ind=0;
}

跟普通链表一样 head一开始指向空 表示没有元素 这里用的数组 用下标表示那就让head=-1 表示当前链表无元素

然后idx=0 就是说 数组现在是可用的位置为0号单元 这很显而易见的事 不用过多解释

考虑一下头插操作

对于一般链表来说 就是一个i.next=h.next   h=i的过程

其实在这里也一样

在ne的idx(可用位置)处存放h所在的下标

然后h移动到该位置即可

然后别忘了 数组中这个位置就存放东西了 可用位置要后移 idx++

void add_to_head(int x)

{

e[idx]=x;

ne[idx]=head;

head=idx;

idx++;
}

就是一样的 e数组为值域 ne数组为指针域 不用担心下标会变的问题了 刚已经证明过了

放心去用就行了 抛开数组 直接只看逻辑里的链表示意就行了

然后k位置插入x也是一样

找到k位置(注意这里是以下标0开始 调用时要传入k-1 函数实现里就别想这些了 k就k)

嗯 idx位置的ne要变成k的ne 然后k的ne要变成idx 记得++

void add(int k,int x){

e[idx]=x;

ne[idx]=ne[k];

ne[k]=idx;

idx++;
}

然后是删除操作

不过就是k的ne直接变成k的ne的ne

void remove(int k)

{

ne[k]=ne[ne[k]];
}

注意 这里其实也只是逻辑上的删除

这个数还存在于数组当中 只不过没有那个数的ne为该数的下标了

它无法被链接到 所以就认为它被删除了

另外 删除头结点应该是head=ne[haed]; 并没有放进去 所以另行考虑

也可以写成

void remove(int k)

{

    if(k==-1)//因为传入的是k-1

        head=ne[head];

    else

        ne[k]=ne[ne[k]];

}

最后就是怎么遍历得到“链表”结果

回忆一下链表怎么遍历 就是根据链顺下去呗

同理 这里就跟着ne[]顺下去

for(int i=head;i!=-1;i=ne[i])

cout<<e[i];

喏 先输出下标0的 66

然后下一个是下标3的 33

下标1的23

下标2的11

然后等于-1了说明到结尾了 结束

得到的就是66 33 23 11

so easy 搞清楚逻辑和物理位置的关系 就能很容易的模拟出链表

代码

#include<bits/stdc++.h>
using namespace std;

const int N=100010;

int head;
int e[N],ne[N];
int idx;

void init()
{
    head=-1;
    idx=0;
}

void add_to_head(int x)
{
    e[idx]=x;
    ne[idx]=head;
    head=idx;
    idx++;
}

void add(int k,int x)
{
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx;
    idx++;
}

void remove(int k)
{
    if(k==-1)//因为传入的是k-1
        head=ne[head]; 
    else
        ne[k]=ne[ne[k]];
}

void print()
{
    for(int i=head;i!=-1;i=ne[i])
        cout<<e[i]<<" ";
}

int main()
{
    int M;
    cin>>M;
    init();
    
    while(M--)
    {
        char op;
        cin>>op;
        switch(op)
        {
            case 'H':
            {
                int x;
                cin>>x;
                add_to_head(x);
            }break;
            case 'D':
            {
                int k;
                cin>>k;
                //if(k==0)
                //    head = ne[head];//删除头结点
                remove(k-1);
            }break;
            case 'I':
            {
                int k,x;
                cin>>k>>x;
                add(k-1,x);
            }break;
        }
    }
    print();
    return 0;
}

### 如何用 Python 实现单链表 #### 定义节点类 为了构建单链表,首先定义一个 `Node` 类来表示链表中的每个节点。每个节点包含两个属性:数据 (`data`) 和指向下一个节点的指针 (`next`)。 ```python class Node: def __init__(self, data=None): self.data = data # 节点存储的数据 self.next = None # 指向下一个节点的指针 ``` #### 定义链表类 接着定义一个 `LinkedList` 类用于管理这些节点,并提供一些基本操作方法: ```python class LinkedList: def __init__(self): self.head = None # 初始化头结点为空 def append(self, data): new_node = Node(data) if not self.head: # 如果当前列表为空,则设置新加入的元素为首元节点 self.head = new_node return last = self.head while last.next: # 找到链表最后一个位置 last = last.next last.next = new_node # 将新的节点连接到最后面 def display(self): elements = [] current_node = self.head while current_node: elements.append(current_node.data) current_node = current_node.next print(elements) def insert_at_beginning(self, data): new_node = Node(data) new_node.next = self.head self.head = new_node def delete_by_value(self, key): cur_node = self.head if cur_node and cur_node.data == key: self.head = cur_node.next cur_node = None return prev = None while cur_node and cur_node.data != key: prev = cur_node cur_node = cur_node.next if cur_node is None: return prev.next = cur_node.next cur_node = None ``` 此部分代码实现了创建一个新的节点并将其添加至链表末端的功能[^1]。此外还提供了打印整个链表以及删除指定值的方法。 对于AcWing 826题目而言,上述基础功能已经足够解决该问题的需求。如果需要更复杂的操作比如反转链表、查找倒数第K个节点等功能可以根据具体需求进一步扩展此类。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值