CHAPTER_7 提高篇(1)——数据结构(1)
7.3.4静态链表
在前面几小节,我们介绍了动态列表。这节我们来介绍静态链表。静态链表的实现原理是hash,即通过建立一个结构体数组,并且令数组的下标直接表示结点的地址。结构体定义如下:
struct Node {
typename data;
int next;
}node[size];
其中typename是数据类型,如int、char等。而整数next则标志着下一个结点的数组下标。通过这种方式,我们可以快速的访问到下一个结点。并且静态链表是不需要头结点的。
下面我们来看两道例题练习链表的使用:
题目1:
有两条链表,给出链表种每个结点的地址、下一个结点的地址和结点存储的数据。求两条链表首个公共结点的地址。若两链表无公共结点,则输出-1。
输入格式:
每个输入包含一个用例。对于每一个用例,在第一行分别给出两个链表第一个结点的地址以及总共的结点的个数。地址是一个int型整数,并且-1表示NULL。之后的每一行给出各结点的内部信息:结点地址、存储数据、下一个结点的地址,中间用空格分隔。
输出格式:
输出第一个公共结点的地址,如果没有公共结点,则输出-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
输出样例:
67890
思路:
由于地址的范围很小,我们使用静态链表更为方便。根据题目,我们在结点中定义一个flag变量,true表示在第一条链表出现,false表示不在第一条链表出现。
接下来我们遍历第一条链表,将所有结点flag置为1。然后哦遍历第二条链表,当出现第一个flag为1的结点,则是第一个共用结点。如果遍历结束没有找到共用结点,则输出-1。
参考代码:
#include<iostream>
using namespace std;
const int maxn=100010;
typedef struct Node {
char data;
int next;
bool flag;
}node;
int main() {
node n[maxn];
int lst1,lst2,num; //lst1和lst2分别代表两个表的首地址,num为结点个数
int adr,data,next; //三个变量分别临时存储地址、数据、下一个结点地址
cin>>lst1>>lst2>>num;
for(int i=0;i<num;i++) { //循环将n各结点存入数组
cin>>adr>>data>>next;
n[adr].flag=false; //将flag初始化为0
n[adr].data=data;
n[adr].next=next;
}
int p;
for(p=lst1;p!=-1;p=n[p].next) {
n[p].flag=true; //将链表1中的结点flag置1
}
for(p=lst2;p!=-1;p=n[p].next) {
if(n[p].flag==1)
break;
}
if(p==-1) { //找到公共结点,地址为p
printf("%05d\n",p);
}
else { //没有公共结点
cout<<-1<<endl;
}
return 0;
}
题目2:
给出一条链表,给出链表中每个结点的address、数据data和指针域next,并给出链表的首地址,要求把链表上的结点按data值递增排序。
输入格式:
每个输入包含一个测试用例。对于每一个用例,第一行给出结点总数N(N<10^5)以及链表的首地址,链表的地址是一个小于10^5的整形数据,-1代表NULL。下面N行,每行给出每个结点的内部信息:地址、数据、下一个结点地址。
输出格式:
对于每一个用例,输出数据格式与输入格式相同。即第一行给出结点总数和首地址,下面N行给出排序后各结点信息,每行中的数据用空格分隔。
输入样例:
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
思路:
首先我们要考虑一个问题:用例所给的结点中是否都在这条链表上?如果存在干扰的结点,我们要如何处理?
处理的方法和题目1类似,我们在结点中定义一个flag变量,标志这个结点是否存在于链表上,如果存在令值为true,反之为false。接下来我们按照如下步骤完成算法:
(1)初始化所有结点,将flag置为false。
(2)从首地址开始遍历链表,当遍历到的结点flag置为true,同时设置一个count计数。
(3)对结点排序,排序函数cmp的规则是:如果cmp函数的两个参数结点中有flag=0的结点(无效结点),则按照flag从大到小排序,如果flag都等于1,则按照data值由小到大排序。这样一来,我们就可以把所有不在链表的干扰结点放到最右边,同时让最左边的有效结点按data值递增排序。
(4)遍历结点数组,控制格式输出。
参考代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100010;
typedef struct Node {
int adr,data,next;
bool flag;
}node;
bool cmp(node a,node b) {
if(a.flag==false||b.flag==false) {
return a.flag>b.flag;
}
else {
return a.data<b.data;
}
}
int main() {
node n[maxn];
int num,p; //num为输入结点总数,p存放首地址
int adr,data,next; //三个临时变量
cin>>num>>p;
for(int i=0;i<num;i++) {
cin>>adr>>data>>next;
n[adr].flag=false;
n[adr].adr=adr;
n[adr].data=data;
n[adr].next=next;
}
int count=0; //计数器
while(p!=-1) {
n[p].flag=true;
count++;
p=n[p].next;
}
if(count==0) {
cout<<"0 -1"<<endl; //特殊情况,链表中没有结点
}
else {
sort(n,n+maxn,cmp);
cout<<count<<' '<<n[0].adr<<endl;
for(int i=0;i<count;i++) {
if(i!=count-1)
printf("%05d %d %05d\n",n[i].adr,n[i].data,n[i+1].adr);
else
printf("%05d %d -1\n",n[i].adr);
}
}
return 0;
}