数组模拟实现链表(C++)

目录

前言

一、数组模拟单链表

二、数组模拟双链表

三、对比

总结


前言

 本章介绍怎么使用数组模拟链表,首先得理解其定义

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

为什么要用数组模拟实现呢?数组实现的双链表本质上是静态链表,但是具有实现简单,速度极快等特点,比数据结构书上声明新的数据类型的方法代码简洁,好记忆。

数组是一种线性结构,存储空间是内存连续的,每当创建一个数组时,就必须先申请好一段指定大小的空间。由于内存连续这一特性,数组的访问特别快,知道下标索引后,就可以通过地址偏移计算得到指定位置的地址,进而能够直接访问到那个地址空间的数据。数组要保证内存连续这一特性,数组在进行任意位置的插入和删除元素时,就会涉及到部分元素的移动,即插入第i个位置时,需要把i+1位置及其后面的所有元素都往后移动一位,然后执行插入;或者删除第i个位置时,在删除后,需要把第i+1位置及其后面的所有元素都往前移动一位,这样才能保证数据的内存连续。

一、数组模拟单链表

数组可以模拟实现单链表和双链表,我们先来介绍单链表。

代码如下

int head, e[N], ne[N], idx;
// head存储链表头,e[]存储节点的值,ne[]存储节点的next指针,idx表示当前用到了哪个节点

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

void insert(int a)// 在链表头插入一个数a
{
    e[idx] = a,ne[idx] = head, head = idx ++ ;
}

void remove(int k)// 将k位置删掉
{
   ne[k]=ne[ne[k]];
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';//输出链表元素

图解代码:

 

二、数组模拟双链表

代码如下:

// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;
void init()//初始化0是左端点,1是右端点
{
    r[0] = 1, l[1] = 0;
    idx = 2;
}
void insert(int a, int x)// 在节点a的右边插入一个数x
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

void remove(int a)// 删除节点a
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}
insert(0,x);//插到头节点后;头节点下标是0
insert(l[1],x);//插入到队尾,尾节点下标为1,所以尾节点的左节点下标为l[1]
insert(l[k],x);//插入到k节点左边,k节点的左下标为了l[k]
for (int i = r[0]; i != 1; i = r[i])cout << e[i] << ' ';//输出链表元素

图解代码

 

 三、对比

如果不用数组实现链表,代码的长度会很长,但是也有它的优点,好理解。

单链表代码如下


#define ERROR 0
#define OK 1

typedef int Status;
typedef int ElemType;

struct Node{
	ElemType data;
	struct Node * next;
};

typedef struct Node *LinkList;

void InitList(LinkList *L){
	*L = (LinkList)malloc(sizeof(Node));
	(*L)->next = NULL;
} 

bool ListEmpty(LinkList L){
	if(L->next)
		return false;
	else
		return true;
}

void ClearList(LinkList *L){
	LinkList p,q;
	p = (*L)->next;
	while(p){
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
}

int ListLength(LinkList L){
	int i = 0;
	LinkList p = L->next;
	while(p){
		i++;
		p = p->next;
	}
	return i;
}

Status GetElem(LinkList L,int i,ElemType *e){
	int cnt = 1;
	LinkList p = L->next;
	while(p && cnt < i){
		p = p->next;
		cnt++;
	}
	if(!p)
		return ERROR;
	*e = p->data;
	return OK;
}

int LocateElem(LinkList L,ElemType e){
	int cnt = 0;
	LinkList p = L->next;
	
	while(p){
		cnt++;
		if(p->data == e)
			return cnt;
		p = p->next;
	}  
	
	return 0;
}

Status ListInsert(LinkList *L,int i,ElemType e){
	LinkList p,s;
	p = (*L);
	int cnt = 1;
	while(p && cnt < i){
		cnt++;
		p = p->next;
	}
	if(!p)
		return ERROR;
	s = (LinkList)malloc(sizeof(Node));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return OK;
}

Status ListDelete(LinkList *L,int i,ElemType *e){
	int cnt = 1;
	LinkList q,p;
	p = (*L);
	//此时 p 为头节点,p->next为第一个节点
	while( p->next && cnt < i){
		p = p->next;
		cnt++;
	}
	//第一个节点不为空,并且 cnt 小于要删除的节点位置,开始循环 
	if(!(p->next))
		return ERROR;
	q = p->next;
	p->next = q->next;
	*e = q->data; 
	free(q);
	return OK;
}

Status ListTraverse(LinkList L){
	LinkList p = L->next;
	while(p){
	 	printf("%d ",p->data);
	 	p = p->next;
	} 
	printf("\n");
	return OK;
}

void CreateListHead(LinkList *L,int n){
	LinkList p;
	srand(time(0));
	*L = (LinkList)malloc(sizeof(Node));
	(*L)->next = NULL;
	
	for(int i = 0 ; i < n ; i++){
		p = (LinkList)malloc(sizeof(Node));
		p->data = rand()%100 + 1;
		p->next = (*L)->next;
		(*L)->next = p;
	}
}

void CreateListTail(LinkList *L,int n){
	LinkList p,r;
	srand(time(0));
	*L = (LinkList)malloc(sizeof(Node));
	r = *L;
	for(int i = 0 ; i < n ; i++){
		p = (LinkList)malloc(sizeof(Node));
		p->data = rand()%100 + 1;
		r->next = p;
		r = p;
	}
	r->next = NULL;
}


 对比发现代码长了非常多。这里只对比了单链表,如果有兴趣可以看看自行对比一下双链表

相较于数组模拟,数组更好写,更好记忆,如果参加一些比赛的话我更加推荐使用数组模拟的链表,另外C++ STL里的list,也是可以的。代码如下

#include <list>
list<int> a;

总结

数组模拟链表经常作为算法的模板使用,要学会这个得了解基础链表的基本概念和实现方式,并且多写多用。如果不是很好理解的话可以尝试跟着代码画一遍图,就可以很好的理解了。

  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
好的,我可以为您讲解一下利用数组模拟双向链表来解决约瑟夫环问题的方法。 首先,我们需要了解约瑟夫环问题的基本情况。这个问题的描述是:有n个人围成一圈,从第一个人开始报数,报到m的人出圈,然后从出圈的下一个人开始重新报数,直到所有人都出圈为止。我们需要编写程序来模拟这个过程,并输出出圈的顺序。 为了解决这个问题,我们可以使用一个数组模拟双向链表数组的每一个元素表示一个人,包含三个属性:前驱节点、后继节点和是否出圈。初始时,我们可以将数组中每个元素的前驱节点和后继节点设置成相邻的元素,形成一个环状结构。然后,我们可以依次遍历这个数组,找到下一个未出圈的人,并将当前人的后继节点指向下一个未出圈的人。当我们找到第m个未出圈的人时,我们可以将其标记为已出圈,并将其前驱节点的后继节点指向其后继节点,从而将其从链表中删除。最后,我们需要输出出圈的顺序,即每个人的编号。 下面是利用数组模拟双向链表解决约瑟夫环问题的示例代码: ```c++ #include <iostream> using namespace std; const int MAXN = 100; // 约瑟夫环的人数上限 int n, m, cnt; // n表示约瑟夫环的人数,m表示报数的次数,cnt表示当前未出圈的人数 int pre[MAXN], nxt[MAXN], out[MAXN]; // pre表示每个人的前驱节点,nxt表示每个人的后继节点,out表示每个人是否出圈 int main() { // 读入约瑟夫环的人数和报数的次数 cin >> n >> m; // 初始化数组,构建双向链表 for (int i = 1; i <= n; i++) { pre[i] = i - 1; nxt[i] = i + 1; out[i] = 0; } pre[1] = n; nxt[n] = 1; // 开始模拟约瑟夫环的过程 cnt = n; int cur = 1; // 从第一个人开始报数 while (cnt > 0) { for (int i = 1; i < m; i++) { cur = nxt[cur]; // 找到下一个未出圈的人 } out[cur] = 1; // 标记当前人已出圈 nxt[pre[cur]] = nxt[cur]; // 将当前人从链表中删除 pre[nxt[cur]] = pre[cur]; cnt--; // 未出圈的人数减1 cur = nxt[cur]; // 从下一个人开始重新报数 } // 输出出圈的顺序 for (int i = 1; i <= n; i++) { if (out[i]) { cout << i << " "; } } cout << endl; return 0; } ``` 希望这个解法能够帮助到您解决约瑟夫环问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super易

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值