数组模拟实现链表

单链表

存储结构

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

其中:

  • head 表示头结点的下标 == first指针

  • e[i] 表示第 i 个节点的值

  • ne[i] 表示下一个节点所在的下标

  • idx 表示下一个新建节点的下标

初始化

头节点指向 -1

下一个节点所在下标设置为0

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

头插法添加节点

常规链表头插法添加节点:

void CreateHead(LinkNode *&head,ElemType a[],int n) {
    LinkNode *cur;
    head = new LinkNode;
    head->next = NULL;
    for(int i = 0 ; i < n ; i++) {
        cur = new LinkNode;
        cur->data = a[i];
        cur->next = head->next;
        head->next = cur;
    }
}

数组模拟实现:

// 头插法添加节点 
void add(int x) {
	e[idx] = x;		// 赋值 
	ne[idx] = head;	// 下一个节点指向头结点 
	head = idx;		// 头结点指向新节点 
	idx++;
}

初始:head = -1, idx = 0

存一个 1 :e[0] = 1, ne[0] = -1, head = 0, idx = 1

存一个 2 :e[1] = 2, ne[1] = 0, head = 1, idx = 2

存一个 3 :e[2] = 3, ne[2] = 1, head = 2, idx = 3

现在:head -> 3 -> 2 -> 1

i = head = 2; e[i] = e[2] = 3; i = ne[head] = ne[2] = 1;

所以可以看出,head一般是比较大的,i 是在一个减小的过程,最后一个 i = 0

插入节点

idxpos的元素后添加新节点:

// 插入节点 
void insert(int pos, int x) {	// 在插入到下标为pos的后一位 
	e[idx] = x;			// 赋值 
    ne[idx] = ne[pos];	// 下一个节点为原本pos位置节点的下一个 
    ne[pos] = idx;		// 原本位置的节点的下一个节点为新节点 
    idx++;
} 

在第 k 个元素后添加一个新节点(从0开始数):

void insert2(int k, int x) {
	int i = head;
	if (head < i) return;
	while (k > 0) {
		i = ne[i];
		k--;
	}
	e[idx] = x;
	ne[idx] = ne[i];
	ne[i] = idx;
	idx++;
}

删除节点

删除idxpos的节点:

// 删除节点 
void remove(int pos) { // 删除pos后面的一个节点 
	ne[pos] = ne[ne[pos]];	
} 

遍历链表

// 遍历链表
void disp() {
	int i = head;
	while (i != -1) {
		cout << e[i] << "[" << i << "]->" ;
		i = ne[i];
	}
	cout << endl;
} 

完整模拟代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

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

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

// 头插法添加节点 
void add(int x) {
	e[idx] = x;		// 赋值 
	ne[idx] = head;	// 下一个节点指向头结点 
	head = idx;		// 头结点指向 
	idx++;
}

// 插入节点 
void insert(int pos, int x) {	// 在插入到下标为pos的后一位 
	e[idx] = x;			// 赋值 
    ne[idx] = ne[pos];	// 下一个节点为原本pos位置节点的下一个 
    ne[pos] = idx;		// 原本位置的节点的下一个节点为新节点 
    idx++;
} 

void insert2(int k, int x) {
	int i = head;
	if (head < i) return;
	while (k > 0) {
		i = ne[i];
		k--;
	}
	e[idx] = x;
	ne[idx] = ne[i];
	ne[i] = idx;
	idx++;
}

// 删除节点 
void remove(int pos) { // 删除pos后面的一个节点 
	ne[pos] = ne[ne[pos]];	
} 

// 遍历链表
void disp() {
	int id = head;
	while (id != -1) {
		cout << e[id] << "[" << id << "]->" ;
		id = ne[id];
	}
	cout << endl;
} 

int main () {
	init();
	add(2);
	add(5);
	add(17);
	add(8);
	disp();
	insert(2,18);
	insert2(3,19);
	remove(1);
	disp();
}

循环双链表

存储结构

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

其中:

  • e[i] 表示第 i 个节点的值
  • l[i] 表示第 i 个节点的前一个节点所在的下标
  • r[i] 表示第 i 个节点的后一个节点所在的下标
  • idx 表示下一个新建节点的下标

初始化

设置节点0为起始节点,节点1为为尾节点,下标从2开始。不设head指针

注意:下面这样是不循环双向链表

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

这样是循环双向链表:

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

插入

插入到idxk的节点的右边:

void insert(int k, int x) {
	e[idx] = x;		// 赋值 
	l[idx] = k;		// 新节点的左节点 
	r[idx] = r[k];	// 新节点的右节点 
    // 下面两步要注意顺序,如果先改变了r[k]此时的l[r[k]]就会受到影响
    l[r[k]] = idx;	// 原节点的右节点的左节点 
	r[k] = idx;		// 原节点的右节点 
    idx++;
}

特殊位置插入:

  1. 插入到idxk的节点的左边:

    insert(l[k], x)

  2. 插入到第一个节点前

    insert(0, x)

    因为初始化时,第一个节点是 0

  3. 插入到最后一个节点后

    insert(l[1], x)

    因为初始化时,最后一个节点是 1

删除

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

遍历

可以分为向左遍历和向右遍历两种。两者的差异就是代码中的 rl

注意一般来说,i = 0i = 1 的节点是不用输出的。

向左遍历:

void dispL(int k) { // 从下标为k的位置遍历链表 ,向左遍历 
	int i = k;
	while (l[i] != k) {
		if (i != 0 && i != 1)
			cout << e[i] << "[" << i << "]->" ;
		i = l[i];
	} 
	if (i != 0 && i != 1)
		cout << e[i] << "[" << i << "]->" ;
	cout << endl;
}

向右遍历:

void dispR(int k) { // 从下标为k的位置遍历链表 ,向右遍历 
	int i = k;
	while (r[i] != k) {
		if (i != 0 && i != 1)
			cout << e[i] << "[" << i << "]->" ;
		i = r[i];
	} 
	if (i != 0 && i != 1)
		cout << e[i] << "[" << i << "]->" ;
	cout << endl;
}

完整模拟代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;

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

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

void insert(int k, int x) { // 在idx为k的节点后面插入一个新节点 
	e[idx] = x;		// 赋值 
	l[idx] = k;		// 新节点的左节点 
	r[idx] = r[k];	// 新节点的右节点 
	l[r[k]] = idx;	// 原节点的右节点的左节点 
	r[k] = idx;		// 原节点的右节点 
	idx++;
}

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

void dispR(int k) { // 从下标为k的位置遍历链表 ,向右遍历 
	int i = k;
	while (r[i] != k) {
		if (i != 0 && i != 1)
			cout << e[i] << "[" << i << "]->" ;
		i = r[i];
	} 
	if (i != 0 && i != 1)
		cout << e[i] << "[" << i << "]->" ;
	cout << endl;
}

void dispL(int k) { // 从下标为k的位置遍历链表 ,向左遍历 
	int i = k;
	while (l[i] != k) {
		if (i != 0 && i != 1)
			cout << e[i] << "[" << i << "]->" ;
		i = l[i];
	} 
	if (i != 0 && i != 1)
		cout << e[i] << "[" << i << "]->" ;
	cout << endl;
}

int main() {
	init();
	insert(0, 1);
	insert(0, 2);
	insert(0, 3);
	dispR(0);
	dispL(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您讲解一下利用数组模拟双向链表来解决约瑟夫环问题的方法。 首先,我们需要了解约瑟夫环问题的基本情况。这个问题的描述是:有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; } ``` 希望这个解法能够帮助到您解决约瑟夫环问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值