Cracking the coding interview--Q2.5
题目
原文:
Given a circular linked list, implement an algorithm which returns node at the beginning of the loop.
DEFINITION
Circular linked list: A (corrupt) linked list in which a node’s next pointer points to an earlier node, so as to make a loop in the linked list.
EXAMPLE
Input: A –> B –> C –> D –> E –> C [the same C as earlier]
Output: C
译文:
给定一个循环链表,实现一个算法返回这个环的开始结点。
定义:
循环链表:链表中一个结点的指针指向先前已经出现的结点,导致链表中出现环。
例子:
输入:A –> B –> C –> D –> E –> C [结点C在之前已经出现过]
输出:结点C
解答
关于带环链表的题目,《编程之美》中也有讲过。方法很tricky,设置快慢指针, (快指针速度为2,慢指针速度为1)使它们沿着链表移动,如果这个链表中存在环, 那么快指针最终会追上慢指针而指向同一个结点。接下来的问题是,快指针追上慢指针后, 怎么找到这个环的开始结点? 现在我们还没有答案,那让我们先来分析一下,快指针会在哪里追上慢指针。
无图无真相,先上图:
设环的开始结点(图中的D)前有k个结点,环有n个结点(上图中n从D到K共8个结点)。 快指针fast和慢指针slow一开始都指向头结点head,它们移动k步可到环的开始结点。 假设慢指针走过m个结点后,快指针追上了它,这时快指针走过了2m个结点。 快指针比慢指针多走过的结点都在环里转圈了,是环中结点数n的整数倍,即:
2m - m = pn --> m = pn, p为正整数
如果头结点是第一个结点的话,那么相遇结点就是第m+1=pn+1个结点。减去环之前的k 个结点,得到从环开始结点到相遇结点的结点数pn+1-k。相遇结点需要再经过 n-(pn+1-k)+1=(1-p)n+k个结点,才能回到环的开始结点(图中结点D)。 如果让快指针在相遇结点继续走,不过这次把速度变成了慢指针一样, 那么它要走(1-p)n+k步到达环开始结点,让慢指针从头结点head开始走, 它要走k步到达环开始结点。最后,它们将在环开始结点处相遇。
这个是怎么得出来的呢?假设快指针走了(1-p)n+k个结点到达环的开始结点,这时, 慢指针也走了(1-p)n+k步,它离环的开始结点还有
k - [ (-p)n + k ] = (p-)n (步)
而这正好是环中结点数的整数倍,所以当慢指针到达环的开始结点时, 快指针(此时它的速度也是1)刚好在环中转了(p-1)圈,然后和慢指针在环的开始结点处相遇。
代码如下:
node* loopstart(node *head){
if(head==NULL) return NULL;
node *fast = head, *slow = head;
while(fast->next!=NULL){
fast = fast->next->next;
slow = slow->next;
if(fast==slow) break;
}
if(fast->next==NULL) return NULL;
slow = head;
while(fast!=slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
这个思路确实很巧很tricky。但,还有没有别的方法呢?更直观更简单的方法。 既然这么问了,当然是有了。:p一个无环的链表,它每个结点的地址都是不一样的。 但如果有环,指针沿着链表移动,那这个指针最终会指向一个已经出现过的地址。 答案是不是已经呼之欲出了。嗯,没错,哈希表!
以地址为哈希表的键值,每出现一个地址,就将该键值对应的实值置为true。 那么当某个键值对应的实值已经为true时,说明这个地址之前已经出现过了, 直接返回它就OK了。
由于C++标准中没有哈希表的操作,我用map进行模拟。不过哈希表的插入和取值操作是O(1) 的时间。而map是由一个RB tree组织,为了维护这个RB tree,插入和取值都会花更多的时 间。
代码如下:
map<node*, bool> hash;
node* loopstart1(node *head){
while(head){
if(hash[head]) return head;
else{
hash[head] = true;
head = head->next;
}
}
return head;
}
完整代码如下:
#include <iostream>
#include <map>
using namespace std; typedef struct node{
int data;
node *next;
}node; node* init(int a[], int n, int m){
node *head, *p, *q;
for(int i=; i<n; ++i){
node *nd = new node();
nd->data = a[i];
if(i==m) q = nd;
if(i==){
head = p = nd;
continue;
}
p->next = nd;
p = nd;
}
p->next = q;
return head;
} node* loopstart(node *head){
if(head==NULL) return NULL;
node *fast = head, *slow = head;
while(fast->next!=NULL){
fast = fast->next->next;
slow = slow->next;
if(fast==slow) break;
}
if(fast->next==NULL) return NULL;
slow = head;
while(fast!=slow){
fast = fast->next;
slow = slow->next;
}
return fast;
} map<node*, bool> hash;
node* loopstart1(node *head){
while(head){
if(hash[head]) return head;
else{
hash[head] = true;
head = head->next;
}
}
return head;
}
int main(){
int n = , m = ;// m<n
int a[] = {
, , , , , , , , ,
};
node *head = init(a, n, m);
//node *p = loopstart(head);
node *p = loopstart1(head);
if(p)
cout<<p->data<<endl;
return ;
}
Cracking the coding interview--Q2.5的更多相关文章
- Cracking the coding interview
写在开头 最近忙于论文的开题等工作,还有阿里的实习笔试,被虐的还行,说还行是因为自己的水平或者说是自己准备的还没有达到他们所需要人才的水平,所以就想找一本面试的书<Cracking the co ...
- Cracking the coding interview 第一章问题及解答
Cracking the coding interview 第一章问题及解答 不管是不是要挪地方,面试题具有很好的联系代码总用,参加新工作的半年里,做的大多是探索性的工作,反而代码写得少了,不高兴,最 ...
- Cracking the Coding Interview(Trees and Graphs)
Cracking the Coding Interview(Trees and Graphs) 树和图的训练平时相对很少,还是要加强训练一些树和图的基础算法.自己对树节点的设计应该不是很合理,多多少少 ...
- Cracking the Coding Interview(Stacks and Queues)
Cracking the Coding Interview(Stacks and Queues) 1.Describe how you could use a single array to impl ...
- 《Cracking the Coding Interview》读书笔记
<Cracking the Coding Interview>是适合硅谷技术面试的一本面试指南,因为题目分类清晰,风格比较靠谱,所以广受推崇. 以下是我的读书笔记,基本都是每章的课后习题解 ...
- Cracking the coding interview目录及资料收集
前言 <Cracking the coding interview>是一本被许多人极力推荐的程序员面试书籍, 详情可见:http://www.careercup.com/book. 第六版 ...
- 《Cracking the Coding Interview》——第13章:C和C++——题目6
2014-04-25 20:07 题目:为什么基类的析构函数必须声明为虚函数? 解法:不是必须,而是应该,这是种规范.对于基类中执行的一些动态资源分配,如果基类的析构函数不是虚函数,那么 派生类的析构 ...
- 《Cracking the Coding Interview》——第5章:位操作——题目7
2014-03-19 06:27 题目:有一个数组里包含了0~n中除了某个整数m之外的所有整数,你要设法找出这个m.限制条件为每次你只能用O(1)的时间访问第i个元素的第j位二进制位. 解法:0~n的 ...
- 二刷Cracking the Coding Interview(CC150第五版)
第18章---高度难题 1,-------另类加法.实现加法. 另类加法 参与人数:327时间限制:3秒空间限制:32768K 算法知识视频讲解 题目描述 请编写一个函数,将两个数字相加.不得使用+或 ...
- 《Cracking the Coding Interview 》之 二叉树的创建 与 遍历(非递归+递归version)
#include <iostream> #include <cstdio> #include <vector> #include <stack> #de ...
随机推荐
- Android HTTP通讯
这里有一个非常棒的http通讯的总结,我看了以后茅塞顿开. 先贴代码: 01 public class Activity1 extends Activity { 02 03 private ...
- javaBean与map类型相互转换
/** * 把Map键值对转化为javaBean对象 * * @param type * @param map * @return * @throws Exception */ private Obj ...
- python列表推导式详解 列表推导式详解 字典推导式 详解 集合推导式详解 嵌套列表推导式详解
推导式是Python中很强大的.很受欢迎的特性,具有语言简洁,简化代码,速度快等优点.推导式包括:1.列表推导式2.字典推导式3.集合推导式4.嵌套列表推导式注意: 字典和集合推导是最近才加入到Pyt ...
- GM11灰色模型
作者:桂. 时间:2017-08-12 08:34:06 链接:http://www.cnblogs.com/xingshansi/p/7348714.html 前言 灰色模型(Gray model ...
- servlet中 getRealPath deprecated(被废弃)
servlet中, HttpRequestServlent req; req.getRealPath 被废弃,使用this.getServletContext().getRealPath()替代:
- Java:多线程,线程池,ThreadPoolExecutor详解
1. ThreadPoolExecutor的一个常用的构造方法 ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepA ...
- 57. 3Sum【medium】
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...
- hdoj1171 Big Event in HDU
Big Event in HDU Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- maven导入外部包pom.xml配置
<dependency> <groupId>com.hadoop</groupId> <artifactId>hadoop-lzo</artifa ...
- js用new Object创建json数据
var str = '';var json = new Object;var arr =new Array(); for(var i =0; i<4;i++){ var jsons ...