静态链表(c++)


静态链表

与动态链表不同的是,静态链表不需要指针来建立结点之间的连接关系。对于有些问题,结点的地址是比较小的整数(例如五位数的地址),就没有必要建立动态链表。
静态链表的实现原理是hash,即通过建立一个结构体数组,并令数组的下标直接表示结点的地址,直接访问结点中的元素就可以访问结点。而且,静态链表是不需要头结点的。静态链表结点定义如下:

struct static_list {
  typename data; //数据域
  int next;  //指针域
} node[size];

next为一个int型整数,用于存放下一个结点的地址,实际上就是数组下标。例如包含三个结点的链表,初始结点的地址为111,第二个结点的地址为222,第三个结点的地址为333,则静态链表可如下建立:

node[111]=222;
node[222]=333;
node[333]=-1   //-1表示NULL,表明没有后继结点

通用解题步骤

  1. 定义静态链表;
struct static_list {
  int address; //地址
  typename data; //数据域
  int next;  //指针域
  xxxxx; //结点的某个性质
} node[size];

以上设置了结点的地址、指针域、数据域,以及一个适应性性质等等。

  1. 程序开始时对链表初始化,如xxx里的性质,是否访问过;
for(int i=0;i<MAXN;i++){
	node[i].xxx=false;
}
  1. 题目一般回给出一条链表首结点的地址,那么我们就可以根据这个地址来遍历得到整条链表。这一阶段我们需要对结点的性质进行标记,并且对有效结点进行计数等等操作。
int p = begin, count = 0;
while(p!=-1){
	xxx = 1;
	count++;
	p=node[p]->next;
}
  1. 由于使用静态链表,是直接采用地址映射的方式,这就会使数组下标不连续,而很多时候题目给出的结点并不都是有效结点。为了能够访问有效结点,一般需要对数组进行排序,并把有效结点移动到数组左端。具体需求视题目而定。

我们看如下两个例子理解静态列表的用法:

一、链表的共享空间

1、题目描述

给出两个链表的首地址以及若干结点的地址、数据、下一个结点的地址,求两条链表首个共用结点的地址。如果两个链表没有共用结点,输出-1。

输入样例1

第一行输入两个链表的首地址,以及正整数N,表示结点个数。

00001 00002 4
00001 a 10001
10001 s -1
00002 a 10002
10002 t -1
输出样例1
-1
输入样例2
11111 22222 9
67890 i 00002
00010 a 12345 
00003 g -1
12345 D 67890
00002 n 00003
22222 B 23456
11111 L 00001
23456 e 67890
00001 o 00010
输出样例2
67890

2、思路

  1. 由于地址的范围很小,因此直接使用静态链表,根据题意需要在结点的结构体中再定义一个int变量flag,表示结点是否在第一条链表出现,是返回1,否则返回-1;
  2. 从第一个链表首地址出发遍历第一个链表,将所有经过的结点flag值赋为1;
  3. 接下来枚举第二条链表,当出现第一个flag值为1的结点,说明是第一条链表中出现的结点,即两个链表的第一个共用结点;
  4. 如果第二条链表枚举完毕没有发现共用结点则返回-1。

3、注意

  1. 使用%05d格式输出地址,不足五位高位补0;
  2. 使用map可能会超时;
  3. scanf读入%c格式时,%d之间加空格。

4、代码实现

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 10000 + 10;
struct static_list {
  char data;
  int next;
  bool flag;
} node[MAXN];

int main() {
  for (int i = 0; i < MAXN; i++) {
    node[i].flag = false;
  }

  int s1, s2, n;  //第一行的三个元素,分别为两个链表首地址和结点个数
  scanf("%d%d%d", s1, s2, n);
  int address, next;
  char data;
  for (int i = 0; i < n; i++) {
    scanf("%d %c %d", &address, &data, &next);
    node[address].data = data;
    node[address].next = next;
  }

  int p;
  for (p = s1; p != -1; p = node[p].next) {
    node[p].flag = true;
  }
  for (p = s2; p != -1; p = node[p].next) {
    if (node[p].flag == true) break;
  }
  if (p != -1) {
    printf("%05d\n", p);
  } else {
    printf("-1\n");
  }
  return 0;
}

二、Linked List sorting

1、题目描述

给出N个结点的地址address、数据域data、以及指针域next,然后给出链表的首地址,要求把这个链表上的结点按data值从小到大输出。要求输入输出的格式相同。

输入样例
5 00001
11111 100 -1
00001 0 22222
33333 100000 11111 
12345 -1 33333
22222 1000 12345
输出样例
5 12345
12345 -1 00001
00001 0 11111
11111 100 22222
22222 1000 33333
33333 100000 -1

2、思路

按照输入,该链表如下:(格式[address,data,next])
[00001,0,22222] -> [22222,1000,12345] -> [12345,-1,33333]->[33333,100000,11111]->[11111,100,-1]

按照key值排序之后得到:
[12345,-1,00001] -> [00001,0,11111] -> [11111,100,22222]->[22222,1000,33333]->[33333,100000,-1]

按照通用解题步骤:

  1. 定义静态链表,其中结点性质由bool型变量flag定义,表示为结点在链表中是否出现;
  2. 初始化,令flag全为false(即0),表示初始状态下所有结点都是无效结点;
  3. 由题目给出的链表首地址begin遍历整条链表,并标记有效结点的flag为true(即1),同时计数有效结点的个数count;
  4. 对结点进行排序,排序函数cmp的原则是:如果cmp两个参数结点中有无效结点的话,按flag从大到小排序,以把有效结点拍到数组左端(有效结点的flag为1),否则按照数据域从小到大排序;
  5. 由于有效结点已经按照数据域从小到大排序,因此按照要求输出有效结点即可。

3、注意

  1. 可以直接使用%05d的输出格式,以在不足五位时高位补0;但要注意**-1**不能使用%05d输出,否则会输出-0001;
  2. 题目可能存在无效结点。

4、代码实现

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100000 + 10;

struct static_list {
  int address, data, next;
  bool flag;  //判断结点是否在链表上
} node[MAXN];

bool cmp(static_list a, static_list b) {
  if (a.flag == false || b.flag == false) {
    return a.flag > b.flag;  //把无效结点放后面
  } else {
    return a.data < b.data;
  }
}
int main() {
  for (int i = 0; i < MAXN; i++) {
    node[i].flag = false;
  }
  int n, begin, address;  //链表首地址begin
  scanf("%d%d", &n, &begin);
  for (int i = 0; i < n; i++) {
    scanf("%d", &address);
    scanf("%d%d", &node[address].data, &node[address].next);
    node[address].address = address;
  }

  int count = 0, p = begin;
  while (p != -1) {
    node[p].flag = true;
    count++;
    p = node[p].next;
  }
  if (count == 0) {
    printf("0 -1");
  } else {
    sort(node, node + MAXN, cmp);
    printf("%d %05d\n", count, node[0].address);
    for (int i = 0; i < count; i++) {
      if (i != count - 1) {
        printf("%05d %d %05d", node[i].address, node[i].data, node[i].next);
      } else {
        printf("%05d %d -1\n", node[i].address, node[i].data);
      }
    }
  }

  return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

eynoZzzzc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值