先给一个具体的例子:
问题:
给出两条链表的首地址以及若干节点的地址,数据,下一节点的地址,求两条链表的首个共用节点的地址。如果没有共用节点输出-1。
#include<cstdio>
#include<cstring>
const int maxn = 100010;
struct Node
{
int data;
int next;
bool flag;//节点是否在第一条链表中出现;
}node[maxn];
int main(void)
{
for(int i = 0; i < maxn; i++)
{
node[i].flag = false;
}
int s1, s2, n;//s1和s2分别代表两条链表的首地址
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);/*scanf使用%c格式时可以读入空格,
因此在输入地址数据和后继节点时格式不能写成%d%c%d 必须在中间加空格*/
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.定义静态链表:
const int maxn = 100010
struct Node
{
int address;//节点地址;
typename data;//数据域;
int next;//指针域;
XXX;//节点的某个性质,不同题目会有不同设置,如下例
}node[maxn];
定义了节点的地址、数据域、指针域、并且留下XXX来适应不同题目(例如可以设置成节点是否为链表上的一个节点)
2.初始化静态链表:
一般来说需要对定义中的XXX进行初始化,将其定义为正常情况下达不到的数字(一般来说需要小于所有能达到的数字,理由在第四步说明),例如针对节点是否在链表上这个性质来讲,可以初始化为0(即 false)表示节点不在链表上。
for(int i = 0; i < maxn; i++)
{
node[i].XXX = 0;
}
3.遍历标记
题目给出第一条链表的首地址,然后我们就依据首地址来遍历整条链表,这一步同时也是我们对节点性质XXX进行标记,并且对有效节点计数的时候,例如对节点是否在链表上在这个性质来讲就可以把XXX设置为1(即true)。
int p = begin, count = 0;
while(p != -1)
{
XXX = 1;
count ++;
p =node[p].next;
}
4.由于使用静态链表时是直接采用地址映射(hash)hash 的方式,就会使得数组下标的不连续,很多时候题目给出的节点并不都是有效节点,所以我们得对数组进行排序,将有效节点移到数组的左端,这样就可以用步骤3得到的count来访问他们,在写sort的排序函数cmp时候可以借助之前定义的XXX来帮忙,无效节点在步骤3不会被修改所以一定比有效节点的XXX小,就可以在cmp两个参数节点中有无效节点时按照XXX的大小排序,
bool cmp(Node a, Node b)
{
if(a.XXX == -1 || b.next == -1)
{
return a.XXX > b.XXX;
}
else
{
//二级排序;
}
}
5.经步骤4有效节点到了数组的左端,且按照节点的性质进行了排序,接下来接看排序后具体干什么了