判斷兩個單鏈表是否相交,一般有下面幾種方法:
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;
}
}
這裏我沒有構造一個新的鏈表來測試,只是把構造第一個鏈表時創建的節點地址都記錄起來,用來驗證查找是否有效。
運行結果: