AcWing学习——链表

1. 单链表

单链表常用于写邻接表,而邻接表的主要应用是用于存储图和树。

算法题的c/c++中,new一块地址空间的操作非常慢,当我们需要对非常大的数据进行链表操作时,可以使用数组模拟链表,即静态操作。

邻接表就是把每个点的所有临边存下来。邻接表相当于多个单链表,每个单链表都用于存储当前元素所有的邻边元素。

1.1. 数组模拟邻接表

使用两个数组e[]ne[]分别存储链表元素和next下标,并且利用一个指针idx指向当前可用下标,初始为0。

当在第k+1个位置处插入一个节点时,都相当于在e[idx]处存储当前数值,而ne[idx]处存储ne[k]的值,而ne[k]处存储idx值。

当要将第k个位置的值删除时,仅需令ne[k]处修改为ne[ne[k]]即可,无需考虑内存泄露问题,因为算法题主要是为了快。

1.2. 例题

1.2.3.826. 单链表 -AcWing题库

c++ 复制代码
#include <iostream>

using namespace std;

const int N = 100010;

// head 表示头节点下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;

//初始化
void init()
{
    head = -1;
    idx = 0;
}

// 头插法:将x插入头节点
void add_to_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++; 
}

// 将x插入下标为k的节点后面
void add(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}

// 将下标为k的节点后面的节点删掉
void remove(int k)
{
    ne[k] = ne[ne[k]]; 
}

int main()
{
    int m;
    cin >> m;
    
    init();
    
    while(m --)
    {
        int k, x;
        char op;
        
        cin >> op;
        
        if(op == 'H')
        {
            cin >> x;
            add_to_head(x);
        }
        else if(op == 'D')
        {
            cin >> k;
            if(!k) head = ne[head];
            remove(k - 1);
        }
        else if(op == 'I')
        {
            cin >> k;
            cin >> x;
            add(k-1, x);
        }
    }
    
    for(int i = head; i != -1; i = ne[i]) cout << e[i] << " ";
    cout << endl;
    
    return 0;
}

2. 双链表

2.1. 数组模拟邻接表

使用三个数组e[]l[]r[]分别存储链表元素和当前元素的前一个元素下标和当前元素的后一个元素下标,并且利用一个指针idx指向当前可用下标,初始为0。

2.2. 例题

2.2.1.827.双链表 -AcWing题库

c++ 复制代码
#include <iostream>

using namespace std;

const int N = 100010;

int m;
int e[N], l[N], r[N], idx;

// 初始化
void init()
{
    // 使用0表示左端点,1表示右端点,
    // 因为0和1已经被使用了,idx从2开始
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}

// 在下标为k的节点后面插入x
void add(int k, int x)
{
    e[idx] = x;
    l[idx] = k;
    r[idx] = r[k];
    l[r[k]] = idx;
    r[k] = idx;
    idx++;
}

// 删除下标为k的节点
void remove(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}


int main()
{
    ios::sync_with_stdio(false);
    cin >> m;

    init();

    while(m--)
    {
        string op;
        cin >> op;
        int k, x;
        if(op=="R")
        {
            cin >> x;
            //!   0和 1 只是代表 头和尾  所以   最右边插入 只要在  指向 1的 那个点的右边插入就可以了
            add(l[1], x); 
        }
        //! 同理  最左边插入就是 在指向 0的数的左边插入就可以了   也就是可以直接在 0的 有右边插入
        else if(op=="L")
        {
            cin >> x;
            add(0, x);
        }
        else if(op=="D")
        {
            cin >> k;
            remove(k + 1);
        }
        else if(op=="IL")
        {
            cin >> k >> x;
            add(l[k + 1], x);
        }
        else
        {
            cin >> k >> x;
            add(k + 1, x);
        }    
    }
    for(int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
        
    return 0;
}
相关推荐
NAGNIP4 小时前
AI训练要的数据这么多,怎么存?
算法
CoovallyAIHub4 小时前
冻结比微调更好?YOLOv8/V10迁移学习最佳实践发布,GPU内存直降28%
深度学习·算法·计算机视觉
此间码农4 小时前
c-依赖库汇总与缺失检测
c++
Hankin_Liu的技术研究室4 小时前
可观测副作用:C++编译器优化的“红线”
c++·编译原理
爱编程的化学家4 小时前
代码随想录算法训练营第21天 -- 回溯4 || 491.非递减子序列 / 46.全排列 /47.全排列 II
数据结构·c++·算法·leetcode·回溯·全排列·代码随想录
console.log('npc')4 小时前
前端性能优化,给录音播放的列表加个播放按键,点击之后再播放录音。减少页面的渲染录音文件数量过多导致加载缓慢
前端·javascript·vue.js·算法
吾当每日三饮五升4 小时前
RapidJSON 自定义内存分配器详解与实战
c++·后端·性能优化·json
_不会dp不改名_5 小时前
leetcode_138 随机链表的复制
算法·leetcode·链表
小欣加油5 小时前
leetcode 129 求根节点到叶节点数字之和
数据结构·c++·算法·leetcode