第二章、删除双向循环列表的共同节点
1、面试题目
36、有双向循环链表结点定义为:
struct node
{
int data;
struct node *front,*next;
};
有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中
data 值相同的结点删除。
2、解题方法
2.1、暴力求解
2.1.1 思路
1、对于链表A中的每一个元素Ai, 查看Ai是否在链表B中,若存在则在A,B链表中删除Ai,否则继续查看下一个元素直到结束。
2、时间复杂度O(m*n),其中m为链表A的长度,n为链表B的长度;
2.1.2 测试代码
#include <iostream>
#include <algorithm>
#include <bitset>
using namespace std;
#define NSIZ 1000000
36、有双向循环链表结点定义为:
struct node
{
int data;
struct node *front,*next;
};
有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中
data 值相同的结点删除。
typedef struct Node_
{
intdata;
Node_* front, *next;
}Node;
void print(Node * head)
{
if(!head)
{
printf("链表为空!!!\n");
return;
}
Node* p = head;
do
{
printf("%d", p->data);
p= p->next;
}while(p!= head);
printf("\n");
}
//创建链表
Node * CreateList(int arr[], int n)
{
Node* head = 0, * p = 0;
inti;
if(head== 0)
{
head= new Node();
head->data= arr[0];
head->front= head;
head->next= head;
p= head;
}
for(i= 1;i < n; ++i)
{
Node* tmp = new Node();
tmp->data= arr[i];
tmp->next= p->next;
p->next= tmp;
tmp->front= p;
p= tmp;
}
head->front= p;
returnhead;
}
//若列表中剩下最后一个节点,删除后返回0
//若删除节点不是最后一个节点1)若非头结点,则直接返回该节点的下一个节点;2)若是头结点,调节头结点为下一个节点
Node* DeleteNode(Node * p, Node *&head)
{
if(p->next== p) //链表中只有一个节点
{
deletep;
p= 0;
head= p;
return0;
}
Node* p_next = p->next;
p->front->next= p->next;
p->next->front= p->front;
if(p== head)
{
head= p->next;
}
if(p)
{
deletep;
p= 0;
}
returnp_next;
}
//算法1,对A链中的每一个元素在B链中寻找,若存在则删除,时间复杂度O(M*N)
// M, N 分别为链表A,B的长度
void DeleteCommonNode(Node * &head1,Node * &head2)
{
if(!head1|| !head2 )
{
return;
}
printf("删除相同节点:");
Node* p1 = head1, *p2 = head2;
intfind = 0;
intend = 0;
do
{
find= 0;
p2= head2;
do
{
if(p1->data== p2->data)
{
find= 1;
break;
}
p2= p2->next;
}while(p2&& p2 != head2);
if(find== 1)//找到相同节点
{
printf("%d", p1->data);
p1= DeleteNode(p1, head1);
p2= DeleteNode(p2, head2);
if(head2== 0 || head1 == 0)
{
break;
}
}
else//未找到相同节点
{
p1= p1->next;
end++;
}
}while(p1&& (p1 != head1 || (end <= 1 && p1 == head1)));
printf("\n\n");
}
int main()
{
intarr[] = {1, 2, 3};
intan = sizeof(arr)/sizeof(arr[0]);
Node* head1 = CreateList(arr, an);
printf("链表A的元素: ");
print(head1);
intbrr[] = {1, -1, 2, 3};
intbn = sizeof(brr)/sizeof(brr[0]);
Node* head2 = CreateList(brr, bn);
printf("链表B的元素: ");
print(head2);
DeleteCommonNode(head1,head2);
printf("删除相同节点后再次遍历链表:\n");
printf("删除公共节点后链表A的剩下元素:");
print(head1);
printf("删除公共节点后链表B的剩下元素:");
print(head2);
return0;
}
2.2使用Bitmap
2.2.1 思路
思路:
使用辅助变量bitmap,主要针对A,B集合中的元素在某一个范围内比如1-10^9,使用bitmap,一个bit表示一个数。
1) 首先遍历链表A和链表B, 把最大值maxD =max(maxA, maxB)和最小值minD = min(mina, minB)找出来,maxA是A链表中的最大值,maxB是B链表的最大值,同理minA是A链表中的最小值,minB是B链表的最小值;申请bitset空间时,如果minD<0, 那么bitset大小为maxD+abs(mind) +1,若果minD>=0,那么bitset大小为maxD;
2) 再次遍历A链表,把相应的bitset[A[i]]为置为1;
3) 再次遍历B链表,把bitset[B[i]]为1的元素放到向量或者数组C中,数组C中元素就是链表A和B中公共元素;
4) Bitset全部重置,遍历数组C,把相应的bitset[C[i]]置为1;
5) 第三次遍历A链表,B链表,把bitset[A[i]]= 1,bitset[B[i]]=1的结点删掉;
6) 结束;
上述整个过程展现了一个在O(m+n)的时间复杂度内解决的法案,但是上述过程是可以继续优化的,比如
Ø 如果题目给定链表A和B的元素范围已经清晰知道,步骤1)中就不用遍历找最大值和最小值了;
Ø 步骤3)可以根据bitset[B[i]]=1把B链表中的公共结点删除,就不用在步骤5)再次链表B了;
你懂得,这里只是展示了一种O(m+n)思想,m为链表A的长度,B为链表B的长度;
2.2.2 测试代码
#include <iostream>
#include <algorithm>
#include <bitset>
using namespace std;
#define NSIZ 100000
typedef struct Node_
{
intdata;
Node_* front, *next;
}Node;
bitset<NSIZ> bs; //bitset,一个bit表示一个数
int arr[NSIZ];
int brr[NSIZ];
int crr[NSIZ];
int minD = 0, maxD = 0; //minD表示链表A和链表B的中最小值,maxD表示链表A和链表B的中最大值
//打印链表
void print(Node * head)
{
if(!head)
{
printf("链表为空!!!\n");
return;
}
Node* p = head;
do
{
printf("%d", p->data);
p= p->next;
}while(p!= head);
printf("\n");
}
//创建链表
Node * CreateList(int arr[], int n)
{
Node* head = 0, * p = 0;
inti;
if(head== 0)
{
head= new Node();
head->data= arr[0];
head->front= head;
head->next= head;
p= head;
}
for(i= 1;i < n; ++i)
{
Node* tmp = new Node();
tmp->data= arr[i];
tmp->next= p->next;
p->next= tmp;
tmp->front= p;
p= tmp;
}
head->front= p;
returnhead;
}
//若列表中剩下最后一个节点,删除后返回0
//若删除节点不是最后一个节点1)若非头结点,则直接返回该节点的下一个节点;2)若是头结点,调节头结点为下一个节点
Node* DeleteNode(Node * p, Node *&head)
{
if(p->next== p) //链表中只有一个节点
{
deletep;
p= 0;
head= p;
return0;
}
Node* p_next = p->next;
p->front->next= p->next;
p->next->front= p->front;
if(p== head)
{
head= p->next;
}
if(p)
{
deletep;
p= 0;
}
returnp_next;
}
//遍历整个链表
int GetNode(Node * head, int arr[], int&len, int &minD, int &maxD)
{
if(head== 0)
{
return0;
}
len= 0;
Node* p = head;
minD= p->data;
maxD= p->data;
arr[len++]= p->data;
p= p->next;
while(p!= head)
{
arr[len++]= p->data;
if(p->data> maxD)
{
maxD= p->data;
}
if(p->data< minD)
{
minD= p->data;
}
p= p->next;
}
returnlen;
}
首先遍历链表A和B获得数组A和数组B以及最大值maxD和最小值minD
//然后遍历数组A,对bitset[A[i]]进行置为1
//然后遍历数组B,把bitset[B[i]]为1的元素放到数组C中,C数组即为公共元素数组
//把bitset重置为0,再次遍历数组C, bitset[C[i]]置为1
void Common(Node * head1, Node * head2)
{
intan = 0, bn = 0;
inti = 0, cn = 0;
intminA = 0, maxA = 0;
intminB = 0, maxB = 0;
GetNode(head1,arr, an, minA, maxA);
GetNode(head2,brr, bn, minB, maxB);
minD= min(minA, minB);
maxD= max(maxA, maxB);
bs.reset();
//当最小值大于等于0时,统一为0
if(minD>= 0)
{
minD= 0;
}
for(i= 0;i < an; ++i)
{
bs[arr[i]-minD] = 1;
}
printf("删除相同节点:");
for(i= 0;i < bn; ++i)
{
if(bs[brr[i]- minD])
{
crr[cn++]= brr[i];
printf("%d", brr[i]);
}
}
bs.reset();
for( i = 0;i < cn;++i)
{
bs.set(crr[i]-minD);
}
}
//先求出两个链表公用节点,
//然后分别遍历两个链表A和B把bitset[A[i]]= 1和bitset[B[i]] = 1的节点分别删除之
void DeleteCommonNode(Node * &head1,Node * &head2)
{
if(!head1|| !head2 )
{
return;
}
//获得两个链表中的公共的节点
Common(head1,head2);
Node* p1 = head1, *p2 = head2;
intend = 0;
//删除链表A中公共节点
do
{
if(bs[p1->data- minD] == 1)
{
p1= DeleteNode(p1, head1);
}
else
{
p1= p1->next;
if(p1== head1)
{
end++;
}
}
}while(p1&&(p1 != head1 || (end <= 1 && p1 == head1)));
end= 0;
//删除链表B中公共节点
do
{
if(bs[p2->data- minD] == 1)
{
p2= DeleteNode(p2, head2);
}
else
{
p2= p2->next;
if(p2== p2->next)
{
end++;
}
}
}while(p2&&( p2 != head2 || (end <= 1 && p2 == head2)));
printf("\n\n");
}
int main()
{
intarr[] = {1, 2, 3};
intan = sizeof(arr)/sizeof(arr[0]);
Node* head1 = CreateList(arr, an);
printf("链表A的元素: ");
print(head1);
intbrr[] = {1, -1, 2, 3};
intbn = sizeof(brr)/sizeof(brr[0]);
Node* head2 = CreateList(brr, bn);
printf("链表B的元素: ");
print(head2);
DeleteCommonNode(head1,head2);
printf("删除相同节点后再次遍历链表:\n");
printf("删除公共节点后链表A的剩下元素:");
print(head1);
printf("删除公共节点后链表B的剩下元素:");
print(head2);
return0;
}
2.3 使用排序算法
2.3.1 思路
1)首先遍历链表A,把A中元素添加到数组A中,同理,把链表B的元素添加到数组B中。
2)对数组A,和数组B进行排序,再遍历一次已经有序的数组A和数组B,把相同元素填到数组C中,数组C中元素就是A和B中共同元素。
3)再次遍历链表A,遍历每一个元素Ai,使用二分查找其是否在数组C中,若在则删除之。
同理对链表B也是执行相同过程;
4)结束
时间复杂度为O(mlgm+nlgn);
2.3.2 测试代码
#include <iostream>
#include <algorithm>
#include <bitset>
using namespace std;
#define NSIZ 100000
typedef struct Node_
{
intdata;
Node_* front, *next;
}Node;
bitset<NSIZ> bs; //bitset,一个bit表示一个数
int arr[NSIZ]; //存放链表A中元素
int brr[NSIZ]; //存放链表B中元素
int crr[NSIZ]; //存放链表A和B中公共元素
int cn = 0; //数组crr[]的长度
//打印链表
void print(Node * head)
{
if(!head)
{
printf("链表为空!!!\n");
return;
}
Node* p = head;
do
{
printf("%d", p->data);
p= p->next;
}while(p!= head);
printf("\n");
}
//创建链表
Node * CreateList(int arr[], int n)
{
Node* head = 0, * p = 0;
inti;
if(head== 0)
{
head= new Node();
head->data= arr[0];
head->front= head;
head->next= head;
p= head;
}
for(i= 1;i < n; ++i)
{
Node* tmp = new Node();
tmp->data= arr[i];
tmp->next= p->next;
p->next= tmp;
tmp->front= p;
p= tmp;
}
head->front= p;
returnhead;
}
//若列表中剩下最后一个节点,删除后返回0
//若删除节点不是最后一个节点1)若非头结点,则直接返回该节点的下一个节点;2)若是头结点,调节头结点为下一个节点
Node* DeleteNode(Node * p, Node *&head)
{
if(p->next== p) //链表中只有一个节点
{
deletep;
p= 0;
head= p;
return0;
}
Node* p_next = p->next;
p->front->next= p->next;
p->next->front= p->front;
if(p== head)
{
head= p->next;
}
if(p)
{
deletep;
p= 0;
}
returnp_next;
}
//遍历链表
int GetNode(Node * head, int arr[], int&len)
{
if(head== 0)
{
return0;
}
len= 0;
Node* p = head;
do
{
arr[len++]= p->data;
p= p->next;
}while(p!= head);
returnlen;
}
//二分查找
int bin_search(int crr[], int cn, int key)
{
intleft = 0, right = cn - 1, mid = 0;
while(left<= right)
{
mid= left + (right - left) / 2;
if(crr[mid]< key )
{
left= mid + 1;
}
elseif(crr[mid] > key)
{
right= mid - 1;
}
else
{
return1;
}
}
return0;
}
//首先遍历链表A和B获得数组A和数组B
//然后分别对数组A和B进行排序
//再次遍历有序数组A,B获得公共元素数组C
void Common(Node * head1, Node * head2)
{
intan = 0, bn = 0;
inti = 0, j = 0;
GetNode(head1,arr, an);
GetNode(head2,brr, bn);
//使用库函数
sort(arr,arr+an);
sort(brr,brr+bn);
//查找公共元素
cn= 0;
while(i< an && j < bn)
{
if(arr[i]< brr[j])
{
++i;
}
elseif(arr[i] > brr[j])
{
++j;
}
else
{
crr[cn++]= arr[i++];
++j;
}
}
//输出公共元素
printf("输出公共元素:");
for(i= 0;i < cn; ++i)
{
printf("%d", crr[i]);
}
printf("\n");
}
先求出两个链表公用节点,然后利用二分查找在两个链表中分别删除之
void DeleteCommonNode(Node * &head1,Node * &head2)
{
if(!head1|| !head2 )
{
return;
}
//获得两个链表中的公共的节点
Common(head1,head2);
Node* p1 = head1, *p2 = head2;
intend = 0;
//删除链表A中公共节点
do
{
if(bin_search(crr,cn, p1->data) == 1)
{
p1= DeleteNode(p1, head1);
}
else
{
p1 = p1->next;
if(p1== head1)
{
end++;
}
}
}while(p1&&(p1 != head1 || (end <= 1 && p1 == head1)));
//删除链表B中公共节点
end= 0;
do
{
if(bin_search(crr,cn, p2->data) == 1)
{
p2= DeleteNode(p2, head2);
}
else
{
p2= p2->next;
if(p2== p2->next)
{
end++;
}
}
}while(p2&&( p2 != head2 || (end <= 1 && p2 == head2)));
printf("\n");
}
int main()
{
intarr[] = {1, 2, 3};
intan = sizeof(arr)/sizeof(arr[0]);
Node* head1 = CreateList(arr, an);
printf("链表A的元素: ");
print(head1);
intbrr[] = {1, -1, 2, 3};
intbn = sizeof(brr)/sizeof(brr[0]);
Node* head2 = CreateList(brr, bn);
printf("链表B的元素: ");
print(head2);
DeleteCommonNode(head1,head2);
printf("删除相同节点后再次遍历链表:\n");
printf("删除公共节点后链表A的剩下元素:");
print(head1);
printf("删除公共节点后链表B的剩下元素:");
print(head2);
return0;
}
至此,这个题目结束。