C++如何用数组模拟链表

 更新时间:2022年01月12日 14:27:19   作者:Kicamon  
大家好,本篇文章主要讲的是C++如何用数组模拟链表,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

前言

链表是指由一系列储存在非连续储存空间 结点组成的储存结构。每个结点由两部分组成:一是储存元素的数据域,一是储存下一个节点地址的指针域。用数组模拟链表可以十分清晰明了地理解这一定义。

在这里,我们简单地介绍一下单链表和双链表两种链表以及用数组模拟实现它们的方式。

1.单链表

单链表是指针方向单向的链表,即a结点的指针域储存着b结点的地址,而b结点的指针域内没有储存a结点的地址。在访问时,可以由a到b访问,而不能由b到a访问。

单链表

如图可以清晰地看到,各个结点的指向都是单向的。

Q: 那么,如何用数组来实现它呢?

A: 方法如下

在k结点右侧插入元素x。先将x赋值给该节点的数据域(e[idx]),然后将k结点的指针域赋值给该结点的指针域,最后将k结点的指针域储存的地址改为该节点的地址。

void add(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx++;
}
删除k结点指向的结点。这里所指的删除,是将k的指向改为该结点的指向。原本为a -> b -> c,改为a -> c,b结点依然存在,只是没有其他结点指向它,也就无法通过链表访问它,我们认为它就再链表上被删除了。
void remove(int k)
{
    ne[k] = ne[ne[k]];
}

读取链表。读取链表只用注意一点,在用单指针扫描时不是将指针位置右移,而是将指针移动到该结点指向的位置。

for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
cout << endl;

主要的操作就是如此,下边看看完整代码:

这是较为经典的写法,我个人认为有些麻烦,head不必单独拿出来写一个函数。但是有助于理解。

#include<iostream>
using namespace std;

const int M = 1e5 + 10;

int m, k, x, idx, head;
int e[M], ne[M];

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

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

void remove(int k)
{
    ne[k] = ne[ne[k]];
}

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

int main()
{
    init();

    cin >> m;
    while (m--)
    {
        char op;
        cin >> op;

        if (op == 'H')
        {
            cin >> x;
            add_head(x);
        }
        else if (op == 'D')
        {
            cin >> k;
            if (!k) head = ne[head];
            remove(k - 1);
        }
        else
        {
            cin >> k >> x;
            add(k - 1, x);
        }
    }

    for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

这种写法稍微简便一些,用a[0]替代head。

#include<iostream>
using namespace std;

const int M = 1e5 + 10;

int m, k, x, idx, head;
int e[M], ne[M];

void init()
{
    ne[0] = -1, idx = 1;
}

void remove(int k)
{
    ne[k] = ne[ne[k]];
}

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

int main()
{
    init();

    cin >> m;
    while (m--)
    {
        char op;
        cin >> op;

        if (op == 'H')
        {
            cin >> x;
            add(0, x);
        }
        else if (op == 'D')
        {
            cin >> k;
            if (!k) head = ne[head];
            remove(k);
        }
        else
        {
            cin >> k >> x;
            add(k, x);
        }
    }

    for (int i = ne[0]; i != -1; i = ne[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

2.双链表

双链表顾名思义就是指针方向双向的链表。

双链表

可以看到除了头尾他们的指针都是双向的。

它的实现方法如下:

创建开始和结束结点。0表示开始,1表示结束,互相指向,在插入时直接往中间插入即可。

void init()
{
	r[0] = 1, l[1] = 0;
	idx = 2;
}

插入结点。双链表插入结点的方法与单链表相同,但是操作要稍微复杂一些,这是在k结点右边插入一结点的代码。它要顾及结点左右的结点指向,对于两边都要操作。面临在k结点左边插入一结点时,不必单独在写一个函数,而改成在l[k]结点的右边插入一个结点。

void add(int k, int x)
{
	a[idx] = x;
	r[idx] = r[k], l[idx] = l[r[k]];
	l[r[k]] = idx, r[k] = idx;
	idx++;
}

删除节点。删除结点与插入结点同理,我就不多赘述了。

void remove(int k)
{
	r[l[k]] = r[k];
	l[r[k]] = l[k];
}

输出链表。可以选择输出方向,这里是从左往右输出。

for (int i = r[0]; i != 1; i = r[i])cout << a[i] << ' ';
	cout << endl;

以下是完整代码:

#include<iostream>
using namespace std;

const int N = 1e5 + 10;

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

void init()
{
	r[0] = 1, l[1] = 0;
	idx = 2;
}

void add(int k, int x)
{
	a[idx] = x;
	r[idx] = r[k], l[idx] = l[r[k]];
	l[r[k]] = idx, r[k] = idx;
	idx++;
}

void remove(int k)
{
	r[l[k]] = r[k];
	l[r[k]] = l[k];
}

int main()
{
	init();
	cin >> m;
	while (m--)
	{
		int k, x;
		string op;
		cin >> op;
		if (op == "L")
		{
			cin >> x;
			add(0, x);
		}
		else if (op == "R")
		{
			cin >> x;
			add(l[1], x);
		}
		else if (op == "D")
		{
			cin >> k;
			remove(k + 1);
		}
		else if (op == "IL")
		{
			cin >> k >> x;
			add(l[k + 1], x);
		}
		else if (op == "IR")
		{
			cin >> k >> x;
			add(k + 1, x);
		}
	}

	for (int i = r[0]; i != 1; i = r[i])cout << a[i] << ' ';
	cout << endl;
	return 0;
}

总结

到此这篇关于C++如何用数组模拟链表的文章就介绍到这了,更多相关C++数组模拟链表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程

    Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程

    这篇文章主要介绍了Mac OS X 10.8 中编译APUE(Unix环境高级编程)的源代码过程,对于用MAC学习Unix环境高级编程的同学会有些作用,需要的朋友可以参考下
    2014-09-09
  • C语言联合体类型的实现

    C语言联合体类型的实现

    联合体也是一种构造数据类型,和结构体类型一样,它也是由各种不同类型的数据组成,本文主要介绍了C语言联合体类型的实现,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • QT打包发布全流程(图文教程)

    QT打包发布全流程(图文教程)

    本文主要介绍了QT打包发布全流程,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • 基于C语言实现shell指令的详解

    基于C语言实现shell指令的详解

    本篇文章是对C语言实现shell指令的方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++深入讲解初始化列表的用法

    C++深入讲解初始化列表的用法

    这篇文章主要介绍了C++成员初始化列表,除了可以使用构造函数对类成员进行初始化之外,C++还提供了另外一种初始化的方法,叫做成员初始化列表。下面来看看文章的详细吧,需要的朋友可以参考一下
    2022-04-04
  • 基于C语言扫雷游戏的设计与实现

    基于C语言扫雷游戏的设计与实现

    大家好,本篇文章主要讲的是基于C语言扫雷游戏的设计与实现,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • C++实现LeetCode(39.组合之和)

    C++实现LeetCode(39.组合之和)

    这篇文章主要介绍了C++实现LeetCode(39.组合之和),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言判断一个数是否是2的幂次方或4的幂次方

    C语言判断一个数是否是2的幂次方或4的幂次方

    本文中我们来看一下如何用C语言判断一个数是否是2的幂次方或4的幂次方的方法,并且判断出来是多少次方,需要的朋友可以参考下
    2016-06-06
  • C语言线性表之双链表详解

    C语言线性表之双链表详解

    这篇文章主要为大家详细介绍了C语言线性表之双链表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • 数据结构与算法 排序(冒泡,选择,插入)

    数据结构与算法 排序(冒泡,选择,插入)

    这篇文章主要介绍了数据结构与算法 排序(冒泡,选择,插入)的相关资料,这里对冒泡,选择和插入都做有实例,需要的朋友可以参考下
    2017-07-07

最新评论