我们知道,动态链表需要指针来建立结点之间的连接关系,但事实上,对有些问题来说,结点的地址是比较小的整数(比如5位整数),那么这时我们大可不必建立动态链表,建立静态链表就足够了,这样程序编写的时候也会减少许多问题。
静态链表的原理是hash,即通过建立一个结构体数组,并令数组的下标直接表示结点的地址,来达到直接访问数组中的元素就能访问结点的效果。另外,由于结点的访问非常方便,因此静态链表是不需要建立头结点的。定义方法如下:
struct Node{
typename data;
int next;
}node[size];
下面我们来看道例题:
【PAT1032 Sharing】 (25分)
To store English words, one method is to use linked lists and store a word letter by letter. To save some space, we may let the words share the same sublist if they share the same suffix. For example, loading and being are stored as showed in Figure 1.
fig.jpg
Figure 1
You are supposed to find the starting position of the common suffix (e.g. the position of i in Figure 1).
Input Specification:
Each input file contains one test case. For each case, the first line contains two addresses of nodes and a positive N (≤10^5), where the two addresses are the addresses of the first nodes of the two words, and N is the total number of nodes. The address of a node is a 5-digit positive integer, and NULL is represented by −1.
Then N lines follow, each describes a node in the format:
Address Data Next
whereAddress is the position of the node, Data is the letter contained by this node which is an English letter chosen from { a-z, A-Z }, and Next is the position of the next node.
Output Specification:
For each case, simply output the 5-digit starting position of the common suffix. If the two words have no common suffix, output -1 instead.
Sample Input 1:
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
Sample Output 1:
67890
Sample Input 2:
00001 00002 4
00001 a 10001
10001 s -1
00002 a 10002
10002 t -1
Sample Output 2:
-1
#include<cstdio>
#include<cstring>
const int maxn = 100010;
struct NODE{
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);//s1,s2分别代表两条链表的首地址
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;//枚举第一条链表出现的所有结点,令其出现次数为1
}
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;
}
以上是静态链表所能解决的比较简单的问题,而对一些稍微复杂的题,此处归纳了一个通用的解题步骤,读者可以结合下面面再给出的例题学习一下:
1.定义静态链表。代码如下:
struct Node{
int address;//结点地址
typename data;//数据域
int next;//指针域
XXX;//结点的某个性质,不同的题目会有不同的设置,详见例题
}node[100010];//例如可以设置成结点是否为链表上的一个结点
2.在程序的开始,需要对链表进行初始化。一般来说,需要对定义中的XXX进行初始化,将其定义为正常情况下达不到的数字(一般来说需要小于所有能达到的数字,理由在第4步给出说明),例如对结点是否在链表上这个性质来说,我们就可以把XXX设置为0,表示结点不在链表上
for(int i = 0;i<maxn;i++){
node[i].XXX = 0;
}
3.题目一般都会给出一条链表的首结点的地址,那么我们就可以依据这个地址来遍历得到整条链表。需要注意的是,这一步同时也是我们对结点的性质XXX进行标记、并且对有效结点的个数进行计数的时候,例如对结点是否在链表上这个性质来说,当我们遍历链表时,就可以把XXX设置为1
int p = begin , count = 0;
while(p!=-1){//-1表示链表结束
XXX = 1;
count++;
p = node[p] -> next;
}
4.由于使用静态链表时,是直接采用映射的方式,这就会使得数组的下标不连续,而很多时候题目给出的结点都不是有效结点(即可能存在不在链表上的结点)。为了能够可控地访问有效结点,一般需要对数组进行排序以把有效结点移到数组左端,这样就可以用步骤3得到的count来访问它们。 既然需要把有效结点都移到前面,那么就可以用之前定义的XXX来帮忙。在步骤2,XXX需要被初始化为比正常结点的XXX取值要小的数值,这个做法就可以在这一步起到作用。由于无效结点的XXX在步骤3不会被修改,因此一定比有效结点的XXX小。于是写sort的排序函数cmp时,就可以在cmp的两个参数结点中有无效结点时按XXX从大到小排序,这样就能把有效结点全部移到数组左端。
bool cmp(Node a,Node b){
if(a.XXX == -1 || b.XXX == -1){
//至少一个结点是无效结点,就把它放到数组后面
return a.XXX > b.XXX;
}else{
//第二级排序
}
}
5.在经历了步骤4之后,链表中的有效结点就在数组左端了,且已经按结点性质进行了排序,接下来就具体要看题目在排序之后具体要求做什么了(比较常见的是按各种不同的要求输出链表)