判断两个单链表是否相交,一般有下面几种方法:
1.遍历第一个链表,记录每次获得的节点地址,然后遍历第二个链表,看记录的节点地址是否存在第二个链表中,这种方法的时间复杂度为O(n^2)。
2.对第一个链表的每个节点地址构造哈希表,然后遍历第二个链表,查找当前节点是否存在哈希表中,此方式的时间复杂度为O(len1+len2)。
3.将其中一个链表首尾相接,遍历另一个链表,如果能达到首尾相接链表的头,说明两个链表存在公共部分,时间复杂度为O(len1+len2)。
下面给出第2种方法,用哈希表查找的实现代码。
首先定义链表数据结构:
typedef struct sigList{
int data;
struct sigList* nextNode;
}sigList_t;
接着构建单链表,这里我把事先定义好的组数存储的元素作为节点数据:
int data[] = {100,53,21,426,0,1235,19,83,26};
sigList_t* Head = NULL;
sigList_t* p;
sigList_t* q;
sigList_t* needToFind[9] = {NULL};
for(int idx = 0; idx < 9; idx++){
p = (sigList_t*)malloc(sizeof(sigList_t));
p->data = data[idx];
//cout << "main:data is " << p->data << "," << "address is " << p << endl;
p->nextNode = NULL;
needToFind[idx] = p;
if(Head == NULL){
Head = p;
}
else{
q->nextNode = p;
}
q = p;
}
链表构造好后,思考:我们要把什么存入哈希表才能达到查找的目的?
两个链表相交是指从某个节点开始,存储的下一个节点的地址相同,如下图所示:
因此我们需要对节点地址构造哈希表,用一个数组存储这些节点地址:
sigList_t* storeRcd[9] = {NULL};
采用除留取余法构造哈希函数,记录长度为9,选一个小于或等于9的最大质数作取余数用,这里用7:
int Hash(void* key){//key传入的节点地址
int Ckey = (int)key;
return Ckey % 7;//除留取余法
}
接着开始构造哈希表,这里采用链地址法解决冲突,哈希表冲突指对key1 != key2,存在f(key1)=f(key2),链地址法就是把key1和key2作为节点放在同一个单链表中,这种表称为同义词子表,在哈希表中只存储同义词子表的头指针,如下图:
void InsertHash(sigList_t* L){
sigList_t* tmp = L;
sigList_t* p;
sigList_t* tail[9] = {NULL};
while(tmp != NULL){
int addr = Hash(tmp);
p = (sigList_t*)malloc(sizeof(sigList_t));//创建节点addr
p->data = (int)tmp;//把地址转换成整型存储
p->nextNode = NULL;
//cout << "InsertHash data is " << tmp << endl;
if(storeRcd[addr] == NULL){//addr这个位置还没有key存储
storeRcd[addr] = p;
}
else{//addr位置已经存在key了,那就把当前要存储的key放在最后一个数据后面
tail[addr]->nextNode = p;
}
tail[addr] = p;
tmp = tmp->nextNode;
}
}
最后实现利用哈希表进行查找:
bool SearchHash(void* key){
int Ckey = (int)key;
int addr = Hash(key);
sigList_t* tmp = storeRcd[addr];
while(tmp != NULL){//遍历addr存储的单链表,直到查找到节点或者不存在
//cout << "SearchHash tmp is " << hex << tmp->data << endl;
if(tmp->data == Ckey)//匹配
return true;
tmp = tmp->nextNode;
}
return false;
}
完整代码:
#include <string.h>
#include <malloc.h>
#include <iostream>
using namespace std;
typedef struct sigList{
int data;
struct sigList* nextNode;
}sigList_t;
sigList_t* storeRcd[9] = {NULL};
void PrintData(sigList_t* L){
sigList_t* tmp = L;
while(tmp != NULL){
cout << hex << tmp->data << " ";
tmp = tmp->nextNode;
}
cout << endl;
}
int Hash(void* key){
int Ckey = (int)key;
//cout << "Hash:" << Ckey % 7 << endl;
return Ckey % 7;
}
void InsertHash(sigList_t* L){
sigList_t* tmp = L;
sigList_t* p;
sigList_t* tail[9] = {NULL};
while(tmp != NULL){
int addr = Hash(tmp);
p = (sigList_t*)malloc(sizeof(sigList_t));
p->data = (int)tmp;
p->nextNode = NULL;
//cout << "InsertHash data is " << tmp << endl;
if(storeRcd[addr] == NULL){
storeRcd[addr] = p;
}
else{
tail[addr]->nextNode = p;
}
tail[addr] = p;
tmp = tmp->nextNode;
}
}
bool SearchHash(void* key){
int Ckey = (int)key;
int addr = Hash(key);
sigList_t* tmp = storeRcd[addr];
while(tmp != NULL){
//cout << "SearchHash tmp is " << hex << tmp->data << endl;
if(tmp->data == Ckey)
return true;
tmp = tmp->nextNode;
}
return false;
}
int main(){
int data[] = {100,53,21,426,0,1235,19,83,26};
sigList_t* Head = NULL;
sigList_t* p;
sigList_t* q;
sigList_t* needToFind[9] = {NULL}; //把每个节点的记录存储起来,测试查找使用
for(int idx = 0; idx < 9; idx++){
p = (sigList_t*)malloc(sizeof(sigList_t));
p->data = data[idx];
//cout << "main:data is " << p->data << "," << "address is " << p << endl;
p->nextNode = NULL;
needToFind[idx] = p;
if(Head == NULL){
Head = p;
}
else{
q->nextNode = p;
}
q = p;
}
InsertHash(Head);
for(int idx = 0; idx < 9; idx++){
cout << idx << ":";
PrintData(storeRcd[idx]);
}
cout << "search record: " << needToFind[7] << ",";
if(SearchHash(needToFind[7])){
cout << "the record is exist in the List" << endl;
}
else{
cout << "the record is not exist in the List" << endl;
}
cout << "search record: " << needToFind[7]+1 << ",";
if(SearchHash(needToFind[7]+1)){
cout << "the record is exist in the List" << endl;
}
else{
cout << "the record is not exist in the List" << endl;
}
}
这里我没有构造一个新的链表来测试,只是把构造第一个链表时创建的节点地址都记录起来,用来验证查找是否有效。
运行结果: