您的位置:首页 > 其它

Cracking the coding interview--Q2.5

2014-10-30 21:52 225 查看
原文:

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

如下图所示:



方法一:

假设链表结点数据主要为a-z英文,则可以使用一个整形变量int,因为int在java中占用32个位,用一个位代表一个字母是否出现过

则遍历当前链表,每个链表数据都判断是否已经出现过,如果没有出现过则标记1出现,直到遍历完毕不存在循环loop或者找到起始点

/**
* 假设Node结点数据只有小写英文字母
* 遍历链表数据data,并且通过weight的32位记录出现过的字母。
* 当重复出现第二次便为loop的开始点
*/
public static char findLoopStartNode(LinkList_5 list) {
char result = '#';
int weight = 0;

Node_5 cur = list.head;
while(cur != null) {
int curData = (int)cur.data;
if((weight & (1<<curData)) != 0) {
result = cur.data;
break;
} else {
weight |= (1<<curData);
}
cur = cur.next;
}
return result;
}

方法二:

《编程之美》中也有讲过。方法很tricky,设置快慢指针, (快指针速度为2,慢指针速度为1)使它们沿着链表移动,如果这个链表中存在环, 那么快指针最终会追上慢指针而指向同一个结点。接下来的问题是,快指针追上慢指针后, 怎么找到这个环的开始结点?
现在我们还没有答案,那让我们先来分析一下,快指针会在哪里追上慢指针。

其实只要证明快慢指针第一次相遇的地方和head的距离是环长度的整数倍即可:
假设相遇处离环开始点相距x个点,环长度为n个点
慢指针:  k+qn+x=m (1)
又有  2m-m=m=pn (2)
有(1),(2)可得:  k+qn+x=pn
所以k+x = (p-q)n,即相遇处离head的距离为环长的整数倍.
所以将其中一个指针移至head,同时两指针以相同速度再次相遇即为环的开始处.

/**
* 最终两者相遇点距离起始位置正好为 循环loop的整数倍
* 此时慢者从头开始,快者原位,两个开始同步骤走动,则相遇点即loop起始点
*/
public static char findLoopStartNode2(LinkList_5 list) {
Node_5 fast = list.head;
Node_5 slow = list.head;
// fast 走两步, slow 走一步
// 假设不存在Loop
while(fast != null && fast.next != null) {
// 第一次走动,判别特殊,头结点算走一步
if(fast == list.head) {
fast = list.head.next;
slow = list.head;
} else {
fast = fast.next.next;
slow = slow.next;
}
// 两者相遇
if(fast == slow) {
break;
}
}
// 不存在环结构Loop
if(fast == null && fast.next == null) {
return '#';
}

// 两者相遇之后慢者slow从头开始走
slow = null;
while(fast != slow) {
if(slow == null) {
slow = list.head;
} else {
slow = slow.next;
}
fast = fast.next;
}
return fast.data;
}

方法三:

java中使用hashMap

/**
* 使用haspMap记录每个元素是否出现过
*/
public static char findLoopStartNode3(LinkList_5 list) {
Map map = new HashMap();
char result = '#';

Node_5 cur = list.head;
while (cur != null) {
char curData = cur.data;
// 判断当前结点是否已经出现过
if(map.containsKey(curData)) {
result = curData;
break;
}
map.put(curData, curData);
cur = cur.next;
}
return result;
}

总代码如下:

package chapter_2_LinkedLists;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

class Node_5 {
public char data;
public Node_5 next;
}

class LinkList_5 {
public Node_5 head;
}

/**
*
给定一个循环链表,实现一个算法返回这个环的开始结点。
定义:
循环链表:链表中一个结点的指针指向先前已经出现的结点,导致链表中出现环。
例子:
输入:A -> B -> C -> D -> E -> C [结点C在之前已经出现过]
输出:结点C

*/
public class Question_2_5 {

/**
* 假设Node结点数据只有小写英文字母
* 遍历链表数据data,并且通过weight的32位记录出现过的字母。
* 当重复出现第二次便为loop的开始点
*/
public static char findLoopStartNode(LinkList_5 list) {
char result = '#';
int weight = 0;

Node_5 cur = list.head;
while(cur != null) {
int curData = (int)cur.data;
if((weight & (1<<curData)) != 0) {
result = cur.data;
break;
} else {
weight |= (1<<curData);
}
cur = cur.next;
}
return result;
}

/**
* 最终两者相遇点距离起始位置正好为 循环loop的整数倍
* 此时慢者从头开始,快者原位,两个开始同步骤走动,则相遇点即loop起始点
*/
public static char findLoopStartNode2(LinkList_5 list) {
Node_5 fast = list.head;
Node_5 slow = list.head;
// fast 走两步, slow 走一步
// 假设不存在Loop
while(fast != null && fast.next != null) {
// 第一次走动,判别特殊,头结点算走一步
if(fast == list.head) {
fast = list.head.next;
slow = list.head;
} else {
fast = fast.next.next;
slow = slow.next;
}
// 两者相遇
if(fast == slow) {
break;
}
}
// 不存在环结构Loop
if(fast == null && fast.next == null) {
return '#';
}

// 两者相遇之后慢者slow从头开始走
slow = null;
while(fast != slow) {
if(slow == null) {
slow = list.head;
} else {
slow = slow.next;
}
fast = fast.next;
}
return fast.data;
}

/**
* 使用haspMap记录每个元素是否出现过
*/
public static char findLoopStartNode3(LinkList_5 list) {
Map map = new HashMap();
char result = '#';

Node_5 cur = list.head;
while (cur != null) {
char curData = cur.data;
// 判断当前结点是否已经出现过
if(map.containsKey(curData)) {
result = curData;
break;
}
map.put(curData, curData);
cur = cur.next;
}
return result;
}

public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
scanner.nextLine();
while(num-- > 0) {
LinkList_5 list = new LinkList_5();
int start = scanner.nextInt();
scanner.nextLine();
String str = scanner.nextLine();
Node_5 cur = null;
Node_5 loopStart = null;

for(int i=0; i<str.length() -1; i++) {
Node_5 node = new Node_5();
node.data = str.charAt(i);
if((i+1) == start) {
loopStart = node;
}
if(cur == null) {
list.head = node;
cur = node;
} else {
cur.next = node;
cur = node;
}
}
// 最后字符结点不算进去,直接接回循环起点处
cur.next = loopStart;

char ch = findLoopStartNode3(list);
if(ch == '#') {
System.out.format("不存在Loop循环");
} else {
System.out.format("Loop循环入口点:%5c", ch);
}
}
}
}


参考自:http://hawstein.com/posts/2.5.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法