您的位置:首页 > 职场人生

面试收集--关于链表的一些面试题

2013-05-07 22:47 260 查看
链表相关的面试题是经常出现的,今天总结一下~

1.如何判断一个链表是否有环?如果有,找到环的入口?


设置快慢指针,快指针步长为2,慢指针步长为1,如果有环,最终快慢指针会相遇,代码如下:

boolhasCircle(Node*head,Node*&encounter)

{

Node*fast=head,*slow=head;

while(fast&&fast->next)

{

fast=fast->next->next;

slow=slow->next;

if(fast==low)

{

encounter=fast;

returntrue;

}

}

//fast==NULL||fast->next==NULL

encounter=NULL;

returnfalse;

}



至于如何确定环入口,请看下图







设入口点距链表头部head有x步,由于必然相遇,设相遇点距入口点有y步,环长度为r(表示环中有r+1个节点),那么有判断相遇的算法可知



(x+y)*2=nr+(x+y)=>nr=x+y=>(n-1)r+r-y=x



由上面的分析可知,设置两个指针,一个初始为head,另一个初始为encounter,同步前进,一旦相遇就是入口,于是有了下面的代码:

Node*findEntry(Node*head,Node*encounter)

{

Node*p1=head,*p2=encounter;

while(p1!=p2)

{

p1=p1->next;

p2=p2->next;

}

returnp1;

}

来源:http://hi.baidu.com/iwitggwg/item/7ad684119a27fefc9c778a5c



2.假设有两个单链表,给出头指针head1和head2,判断两个链表是否有交点?


先判断是否有环,不过题目一般会给定无环条件。如果没有给出,可以判断,判断算法如下:

如果无环,遍历两个链表,得到两个链表的长度m和n,先遍历较长链表|m-n|次,接着同步遍历两个链表,一旦相同,则找到相同节点,否则返回null。


3.只给定单链表中某个结点p(并非最后一个结点,即p->next!=NULL)指针,删除该结点。


这个题目有个trick,正常的思路是利用p前面一个节点才能删掉p,但是现在只有p的地址,得不到p前面的地址,那么只能删除p后面的节点,删除之前把p->next的内容复制到p中,注意这个算法删除不了最后一个元素。


4.只给定单链表中某个结点p,在p前面插入一个结点?


思路同3,先在p后面插入一个节点,然后将p的数据复制到p->next中,在将插入的结点的数据复制到p中


5.给定单链表的头结点head,删除链表中倒数第k个结点


同样是快慢指针问题,设置快指针p_fast,慢指针p_slow,初始均指向head,快指针先走k步,走完k步之后快慢指针同步走,每次走一步,直至快指针走到队尾。这里没有考虑有环的状况,有环这个题目就无法解了,如果严谨的话要先判断是否有环。下面是一个不太严谨的算法。

Node*(Node*head,intk)

{

assert(head!=NULL);

Node*p_fast=head;

Node*p_slow=head;

intcount=k;

while(count>0&&p_fast!=NULL)

	{

p_fast=p_fast->next;

count--;

}

if(count>0)returnNULL;

while(p_fast!=NULL)

	{

p_fast=p_fast->next;

p_slow=p_slow->next;

}

returnp_slow;

}



6.找出链表的中间元素(无环)


同样是快慢指针的问题,快指针慢指针初始都指向head,快指针每次行进两步,慢指针一步,一旦快指针指向链表尾部,慢指针指向的就是中间元素。实际写代码要考虑一些奇偶情况。


7.链表的就地逆置


头插法,但是要注意一些特殊情况,代码如下:

Node*reverseList(Node*head)

  {

  Node*p1,*p2,*p3;

  //链表为空,或是单结点链表直接返回头结点

  if(head==NULL||head->next==NULL)

  {

  returnhead;

  }

  p1=head;

  p2=head->next;

  while(p2!=NULL)

  {

  p3=p2->next;

  p2->next=p1;

  p1=p2;

  p2=p3;

  }

  head->next=NULL;

  head=p1;

  returnhead;

  }



8.复杂链表的复制


这个是程序员面试精选的第49题,思路很巧妙,题目意思见链接,链表复杂就复杂在于除了有一个m_pNext指针指向下一个结点外,还有一个m_pSibling指向链表中的任一结点或者NULL。其结点的C++定义如下:

structComplexNode

{

intm_nValue;

ComplexNode*m_pNext;

ComplexNode*m_pSibling;

};





解法一旦知晓就没什么神秘的了,答案参看链接。



9.已知两个链表head1和head2各自有序,请把它们合并成一个链表依然有序,保留所有结点,即便大小相同,递归实现,假设都是升序排列。


Node*merge(Node*head1,Node*head2)

{

	Node*ret=NULL;

	if(head1==NULL)returnhead2;

	if(head2==NULL)returnhead1;


if(head1->val<head2->val)

	{

ret=head1;

ret->next=merge(head1->next,head2);

}

else

	{

ret=head2;

ret->next=merge(head1,head2->next);

}

	returnret;

}



10.从一个未排序的链表中删除重复的元素,给出一种算法,如果要求不适用额外的空间呢?


如果能用hash表的话,只需要遍历一次链表,如果发现元素不在hash表中,就插入hash表,反之则删除。

如果不用额外的空间的话,有个O(N2)的算法:

LinkListRemoveDupNode(LinkListL)//删除重复结点的算法

{

LinkListp,q,r;

p=L->next;

while(p)//p用于遍历链表

{

q=p;

while(q->next)//q遍历p后面的结点,并与p数值比较

{

if(q->next->data==p->data)

{

r=q->next;//r保存需要删掉的结点

q->next=r->next;//需要删掉的结点的前后结点相接

free(r);

}

else

q=q->next;

}

p=p->next;

}

returnL;

}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: