1.判断是否有环
Leetcode 141;easy;
定义两个指针slow和fast,只要slow指针追上每次走两步的fast指针的话就有环,否则就没!
package linkedlist;
public class Main0141环形链表 {
public static void main(String[] args) {
}
}
class Solution141 {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast.next == null || fast.next.next == null) {//无环返回false
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
class Solution0141 {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode p1 = head;
ListNode p2 = head;
while (p2 != null && p2.next != null) {
p1 = p1.next;
p2 = p2.next.next;
if (p1 == p2) {
return true;
}
}
return false;
}
}
2.返回环形链表的入口结点
Leetcode 142;medium;
分两个阶段。
第一个阶段:定义slow和fast指针,让slow指针追上fast指针,说明有环;
第二个阶段:让slow指针重新指向第一个head结点,然后slow和fast指针相同速度移动,当slow和fast指向同一个节点时,就是环入口结点。
package linkedlist;
public class Main0142环形链表Ⅱ {
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode node = new ListNode(2);
ListNode node1 = new ListNode(3);
head.next = node;
node.next = node1;
node1.next = node;
ListNode cycle = new Solution142().detectCycle(head);
System.out.println(cycle.val);
}
}
class Solution142 {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null)
return null;
ListNode p1 = head;// p1和p2必须都指向头节点
ListNode p2 = head;
while (p2.next != null && p2.next.next != null) {
p1 = p1.next;
p2 = p2.next.next;
if (p1 == p2) {
break;
}
}
if (p2.next == null || p2.next.next == null) {// 无环
return null;
}
p1 = head;
while (p1 != p2) {// p1=p2时退出循环,即都指向环入口结点
p1 = p1.next;
p2 = p2.next;
}
return p1;
}
}
3.约瑟夫环形链表自杀问题
程序员代码面试指南(第2版)
这个问题比较有意思,这个题的大致意思:有n个人,围成一个环,编号1-n,
指定一个k值,从编号1开始,每次报数,当喊到k时,这个人就要自杀,下一个人从1开始,…最后只会剩下一个幸运儿,幸运儿自己选择自己的命运!
此题最后求得的就是哪个人会是“幸运儿”!
下面代码有两种方法求得:法1是按报数每次删除一结点,最后会剩下一个节点;法2利用推导公式会直接求出幸运儿,找到返回即可!
package linkedlist;
public class Main环形单链表的约瑟夫问题 {
public static void main(String[] args) {
ListNode head = new ListNode(1);
ListNode node = new ListNode(2);
ListNode node1 = new ListNode(3);
head.next = node;
node.next = node1;
node1.next = head;
ListNode josephus1 = josephus1(head, 2);
System.out.println(josephus1.val);
int live = getLive(3, 2);
System.out.println("最后留下的节点:" + live);
}
// 每次都删除报数为 m 的节点,最后返回一个节点
public static ListNode josephus1(ListNode head, int m) {
if (head == null || head.next == null || m < 1)
return head;
ListNode last = head;
while (last.next != head) {// 走到head前,用于删除
last = last.next;
}
int count = 0;
while (head != last) {
count++;
if (count == m) {
last.next = head.next;// 删除节点但last指针并未移动
count = 0;
} else {
last = last.next;
}
head = last.next;
}
return head;
}
// 公式证明直接返回存活的节点
public static ListNode josephusKill2(ListNode head, int m) {
if (head == null || head.next == null || m < 1)
return head;
int len = 1;
ListNode cur = head.next;
while (cur != head) {
len++;
cur = cur.next;
}
int live = getLive(len, m);
while (live != 1) {
live--;
head = head.next;
}
head.next = head;
return head;
}
// 利用链表长度和报数数字计算出存活的节点
public static int getLive(int len, int m) {
if (len == 1)
return 1;
return (getLive(len - 1, m) + m - 1) % len + 1;
}
}