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


如下图所示:




方法一:

假设链表结点数据主要为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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值