无聊在版上闲逛发现类似 Cyclic Linked-List的题,自己想了一个试着写一下。
Find Longest Unrepeated Letters Length in Cyclic Linked-List (The List only Contain Upper letter from 'A' - 'Z')
For Example: ...A(head)-B-C-A-B-C-B-D-A(head)..... The Linked-List will start from A and traverse through B,C,A,B,C,B,D and Go back to A.
The Longest Unrepeated Letters Length is 4. (C-B-D-A)
我们可以把这道题分成几部分来进行解答:
1. 如何保存我们遍历到哪些结点的值
2. 如果是不带环的Linked-List,我们怎么找最长不重复子串
3. 如果Linked-List成环,我们需要注意哪些问题
对于第一问,因为题目里结点保存的值为'A' - 'Z', 所以我们可以用一个26位的boolean数组还保存某个值我们是否Visit过
先考虑不带环的Linked-List,我们怎么解决。比较直观的方法就是用两个Pointer,比如 i 和 j,分别指向当前子串的首和尾
1. 我们移动尾字符串,如果此结点的值是不重复的,更新boolean数组相对应位为True,并且将尾指针往后移动
2. 当尾指针遍历到一个值发现重复,首先,这是一个潜在的最大长度,而且说明,在之前我们肯定遍历过相同的值,那么这个值会落在从指针i 到指针 j 之间的位置 (第三点也会保证这一属性)
3. 此时,我们知道重复的字符在i 和 j 之间,那么我们不断移动指针i 去找到这一重复的字符。需要注意的是,在我们找到重复字符之前,首指针跳过的结点对应的字符,我们要给boolean[] 置False, 因为很简单,从i到j的子串中已经不包括i指针刚才跳过的字符了。
4. 当找到重复的字符后,两个指针同时向后移动1个位置,进行下一回的判断
看上面的3个图,当j指针移动到图2中B的位置的时候,首先从A-B-C是一个潜在的最大长度,其次,我们移动i指针去找之前重复的B,在找B的过程中,我们跳过了A,子串里面不包括A,所以我们要给boolean数组相对应的位置置False. 当我们发现重复的B之后,i 和 j各向后移动一位,继续这个过程。
好,现在我们知道了怎么在无环的Linked-List里面怎么找最大不重复子串,那么如果如图所示,闭环的Linked-List怎么办呢。
我们自然想到什么时候停的问题,如果不带环的Linked-List,只要尾指针移动的队尾就可以了,但是如果有环,如图所示,完全可能出现 C-B-D-A才是最大的不重复子串,换句话说,这回,我们需要首指针移动到队尾才能完成对所有可能的验证(因为最长子串的起始点可能在Linked-List里面任意一个节点)
OK,原理我们明白了,看一下实现这个功能的代码:
public int longestUnrepeat(){ if (head==null) return 0; if (head.next==head) return 1; Node i = head; Node j = head; boolean imove = false; boolean[] visit = new boolean[26]; int maxLen = 0; int Len = 0; while(true){ if (i!=head) imove = true; if (i==head&&imove) break; if (!visit[j.val-'A']){ visit[j.val-'A'] = true; j = j.next; Len++; }else{ maxLen = Math.max(maxLen, Len); while(i.val!=j.val){ visit[i.val-'A'] = false; i = i.next; Len--; if (i==head) return maxLen; } i = i.next; j = j.next; } } maxLen = Math.max(maxLen, Len); return maxLen; }
1. 计算Len的时候,当尾指针j向前移动的时候,Len++,当首指针i向前移动的时候,Len--;
2. 当发现重复的时候,之前的字符子串是一个潜在的最大长度,我们要和之前maxLen进行比较,而当最后退出循环的时候,记得还要和maxLen比较一下当前的长度
3. 当Pointer i 循环完一圈,再次回到Head的时候,跳出循环,那么我们怎么判断它是第二次到达Head的呢? 我用了一个imove boolean变量默认False,当i从Head出发的时候,我们给它置True,说明它确实移动了,再次到达Head的时候便是第二次到达
4. node.val - 'A' 转化为数组index
5. 要小心没有结点,或者一个结点自成环的情况
完整直接能运行的版本请看下面:
public class LoopLinkedList { private Node head = null; private class Node{ char val; Node next; public Node(char c){ this.val = c; } } public void put(char c){ if (head==null) head = new Node(c); else { Node x = head; while(x.next!=null) x = x.next; x.next = new Node(c); } } public void closeloop(){ Node x = head; while(x.next!=null) x = x.next; x.next = head; } public int longestUnrepeat(){ if (head==null) return 0; if (head.next==head) return 1; Node i = head; Node j = head; boolean imove = false; boolean[] visit = new boolean[26]; int maxLen = 0; int Len = 0; while(true){ if (i!=head) imove = true; if (i==head&&imove) break; if (!visit[j.val-'A']){ visit[j.val-'A'] = true; j = j.next; Len++; }else{ maxLen = Math.max(maxLen, Len); while(i.val!=j.val){ visit[i.val-'A'] = false; i = i.next; Len--; if (i==head) return maxLen; } i = i.next; j = j.next; } } maxLen = Math.max(maxLen, Len); return maxLen; } public static void main(String[] args){ LoopLinkedList list1 = new LoopLinkedList(); list1.put('A'); list1.put('A'); list1.put('A'); list1.closeloop(); int len = list1.longestUnrepeat(); System.out.println("The Longest Unrepeated Length of Cyclic Linked-List -A-A-A- is ---->" + len); LoopLinkedList list2 = new LoopLinkedList(); list2.put('A'); list2.put('B'); list2.put('C'); list2.put('A'); list2.put('B'); list2.put('C'); list2.put('B'); list2.put('D'); list2.closeloop(); len = list2.longestUnrepeat(); System.out.println("The Longest Unrepeated Length of Cyclic Linked-List -A-B-C-A-B-C-B-D- is ---->" + len); } }
The Longest Unrepeated Length of Cyclic Linked-List -A-A-A- is ---->1
The Longest Unrepeated Length of Cyclic Linked-List -A-B-C-A-B-C-B-D- is ---->4
只测试了几个例子,不知道还有没有别的情况没有考虑充分,欢迎来捉虫 ^_^