1,给定一个链表,要求写一个算法来判断其是否存在环?
解答:这个题目的思路还是比较明晰的,就用大步小步法(就如同在一个圆形跑道中进行长跑比赛,一个人A以速度2m/s跑步,另一个人B以速度3m/s跑步,他们从同一起点开始跑步,那么在有限的时间内,他们必然会第二次相遇)
应用到这个题目,就是我们选取两个指针p和q, p每次往前扫描1步,q每次往前扫描2步,那么如果链表存在圈,那么p和q一定会再次相遇,否则就不会相遇。循环结束的条件是要么他们俩相遇,要么其中一个指针运行到头。算法如下:
bool circle_detect(Node *head) //输入为要检测链表的指针
{
if ( head == NULL)
Node *p = head;return false;
Node *q = head;
p = p->next;
q = (q->next)->next;
while (p != NULL && q != NULL)
{
if( p == q)
return true;p = p->next;
q = (q->next)->next;
}
return false;
}
进一步还可以得到链表进入圈的起始点和圈的周长。在p和q相遇之后,将p移回到链表头部然后p和q保持1的步长前进,直到相遇,那么哪个点就是起始点,进一步通过起始点遍历一周就获得了周长
2,给定一个链表,每个节点出了next指针之外,还有一个other指针,next指针指向该节点的下一个节点,而other指针可以指向该链表中任意一个节点或者为空,要求写一个算法来复制这个链表?
解答:这个题目是Google曾经的一道面试题目,假设原始链表长度为n,这道题目一个最直接的想法就是先利用next指针重构链表,然后再遍历新链表,根据原始链表每一个元素的other指针指向的元素,然后在新链表中寻找该元素(关键字域相同),并设置当前访问的节点的other指针指向这个元素,这个过程需要 O(n^2)的访问操作,而且如果链表元素中存在key值相同的元素,这个方法就不适用了。
我们知道上面这个算法,比较耗时的一步操作是在设置新链表的other指针时,每次都要从头开始查找对应的元素,解决这个问题的一个办法就是我们在用next指针建立新链表的时候为新旧链表的元素建立一个哈希表(通过关键字key域来建立映射),这样第二次遍历的时候,查找other指针对应元素的时候只需要O(1)的哈希计算查表时间,就可以得到新链表对应的元素,而且可以解决重复元素出现的情况,因而总的访问时间就是O(2n),但是这个方案需要O(n)的存储代价。一个更好的方案是需要O(2n)的时间和O(1)的空间,就可以完成链表的复制,未完待续。
3,给定两个有序链表,要求以O(n)时间复杂度将两个链表合并,且合并后的链表还是有序的。接口定义如下
Node *Merge(Node *h1, Node *h2){}
解答:这个题目最简单的一个想法就是插入排序,就是把h2的链表的元素逐个插入到h1的链表当中,算法显然是O(n^2)的,不符合要求。
下面我们给出一个O(n)的merge过程,核心还是从把h2插入到h1当中,但是在h1中记录一个当前插入链表的指针,这个指针只需至多扫描一遍h1,就可以将h2的元素插入到h1中,并保持有序。
Node *Merge(Node *h1, Node *h2)
{
if(h1==NULL)
if(h2==NULL)return h2;
return h1;Node *prev=NULL; //记录前驱结点
Node *curr=h1; //记录当前访问的节点
while(h2!=NULL){
while(curr!=NULL && curr.key < h2.key){pre=curr;curr=curr.next;}
if(curr==NULL){//当前h1扫描完毕,直接将剩下的h2接到h1的末尾,返回h1
prev.next=h2return h1;
}else if(prev==NULL){//将链表h2的头插入到h1的头前面
prev=h2; //更新prev到新插入的元素h2=h2.next; //更新h2prev.next=curr;
}else{//将链表h2的头插入到h1中间
prev.next=h2;prev=prev.next //更新prev到新插入的元素h2=h2.next; //更新h2prev.next=curr;
}
}
return h1;
}
http://blog.163.com/kevinlee_2010/
浙公网安备 33010602011771号