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

目录
  • 前言
  • 1.单链表
  • 2.双链表
  • 总结

前言

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

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

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++数组模拟链表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 老生常谈C语言链表小结

    目录 链表的概念及结构 概念 结构 链表的分类 单链表的实现(无头) 双向链表的实现 总结:链表和顺序表的区别 链表的概念及结构 概念 链表是一种物理存储结构上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 . 结构 代码 struct Slist { int* a; struct Slist* next; }; 逻辑结构: 物理结构: 注意: 从上图可以看出,链式结构在逻辑上是连续的,但是在物理上是不一定是连续的. 这些结点一般是从堆上申请出来的. 从堆上申请的空

  • 数据结构C语言链表的实现介绍

    目录 前言 函数 1. 链表初始化 2. 计算链表长度 3. 打印链表 4.计算链表长度 5. 删除链表中指定位置节点 6. 向链表中指定位置插入节点 7. 全代码+运行效果 前言 需要用到的函数库 #include<stdio.h> #include<malloc.h> malloc函数用来动态分配空间,相当于Java中new的作用 先是需要创建一个节点的结构体 typedef struct{ int data; struct linkNode* next; }linkNode;

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

    目录 前言 1.单链表 2.双链表 总结 前言 链表是指由一系列储存在非连续储存空间 结点组成的储存结构.每个结点由两部分组成:一是储存元素的数据域,一是储存下一个节点地址的指针域.用数组模拟链表可以十分清晰明了地理解这一定义. 在这里,我们简单地介绍一下单链表和双链表两种链表以及用数组模拟实现它们的方式. 1.单链表 单链表是指针方向单向的链表,即a结点的指针域储存着b结点的地址,而b结点的指针域内没有储存a结点的地址.在访问时,可以由a到b访问,而不能由b到a访问. 如图可以清晰地看到,各个

  • 如何用JS模拟实现数组的map方法

    昨天使用map方法的时候,突然感觉一直在直接用,也没有试试是怎么实现的,本来想直接搜一篇文章盘一下子,结果没搜到合适的,好吧,那就自己来写一下子吧 今天就来实现一个简单的map方法 首先我们来看一下map方法的使用以及具体的参数 var arr = ["a","b","c","d","e"]; arr.map(function(currentValue,index,arr){ console.log(&qu

  • 教你怎么用Java数组和链表实现栈

    一.何为栈? 栈(stack)又名堆栈,它是一种运算受限的线性表.限定仅在表尾进行插入和删除操作的线性表.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素. 栈可以类比成现实生活中的弹夹或者羽毛球桶 二.用数组实现栈 用数组模拟栈的思路分析如图: 1.定义一个top变量(指针)表示栈顶初始化为-1. 2.定义一个变量来记

  • Java队列篇之实现数组模拟队列及可复用环形队列详解

    队列简介 队列是一个有序列表,可以用数组或是链表来实现. 遵循先入先出的原则.即先存入队列的数据,先取出,后存入的后取出. 示意图:(使用数组模拟队列示意图) 有两个分别指向头部和尾部的"指针". 数组模拟队列(无法复用) 1.实现思路 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量. 因为队列的输出.输入是分别从前后端来处理,因此需要两个变量front及rear分别记录队列前后端的下标,front会随着数据输出而改变

  • C语言 数据结构之数组模拟实现顺序表流程详解

    目录 线性表和顺序表 线性表 顺序表 静态顺序表 动态顺序表 代码已经放在Gitee上,需要可以小伙伴可以去看看 用C语言数组模拟实现顺序表 Gitee 线性表和顺序表 线性表 线性表(linear list)是n个具有相同特性的数据元素的有限序列,这是我们广泛使用的数据结构. 线性表在逻辑上是线性结构,也就说是连续的一条直线.但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储. 常见的线性表:顺序表.链表.栈.队列.字符串- 顺序表 顺序表是用一段物理地址连

  • java数据结构与算法数组模拟队列示例详解

    目录 一.什么是队列 二.用数组来模拟队列 一.什么是队列 队列是一个有序列表,可以用数组或者链表来实现. 遵循先入先出的原则,即:先存入队列的数据,要先取出.后存入的的数据,后取出. 看一张队列的模拟图,1,2,3表示同一个队列Queue.在队列中有2个指针,front表示队首,rear表示队尾. 图1中表示队列里还没有数据,所以front跟rear初始化都是-1. 当图2中有数据进行存入的时候,front没变,而rear则随着数据的增多而改变.存入了4个数据,于是rear=3. 再看图3,f

  • Java数组模拟优先级队列数据结构的实例

    优先级队列 如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了.这样,我们就引入了优先级队列 这种数据结构. 优先级队列(priority queue) 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素 (3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 .对于优先权相同的元素,可按先进先出次序处理或按任意优先

  • PHP中模拟链表和链表的基本操作示例

    模拟链表: <?php /** * PHP实现链表的基本操作 */ class linkList { /** * 姓名 * @var string */ public $name = ''; /** * 编号 * @var int */ public $id = 0; /* * 引用下一个对象 */ public $next = null; /** * 构造函数初始化数据 * @param int $id * @param string $name */ public function __co

  • C#多线程数组模拟socket

    本文实例为大家分享了C#多线程数组模拟socket的具体代码,供大家参考,具体内容如下 代码如下 //实例化线程组 Thread[] clientThreads = new Thread[numThread]; for (int i = 0; i < numThread; i++) { clientThreads[i] = new Thread(new ParameterizedThreadStart(SocketClient)); clientThreads[i].Start(i); } 多线

  • C#使用二维数组模拟斗地主

    本文实例讲述了C#使用二维数组模拟斗地主的方法.分享给大家供大家参考.具体如下: package com.pb.demo; import java.util.Arrays; import java.util.Random; /** * 扑克牌随机发牌♠♥♣♦ 二维数组实现 * */ public class Puker { public static void main(String[] args) { // 定义数组 String[][] puker = new String[5][]; pu

随机推荐