一、兩個有序鏈表的公共部分
思路:雙指針
public static class Node{
int value;
Node next;
public Node(int value) {
this.value = value;
}
}
public static void f(Node head1,Node head2) {
Node first = head1;
Node second = head2;
while(first!=null && second!=null) {
if(first.value < second.value) first= first.next;
else if(first.value > second.value) second= second.next;
else {
System.out.println(first.value+" ");
first = first.next;
second = second.next;
}
}
}
public static void main(String[] args) {
Node node1 = new Node(2);
node1.next = new Node(3);
node1.next.next = new Node(5);
node1.next.next.next = new Node(6);
Node node2 = new Node(1);
node2.next = new Node(2);
node2.next.next = new Node(5);
node2.next.next.next = new Node(7);
node2.next.next.next.next = new Node(8);
f(node1,node2);
}
二、判斷一個鏈表是否是迴文結構
思路:將鏈表逆置與原鏈表比較
逆置方法一:利用棧結構:可以將鏈表所有元素都壓入棧再彈出,也可以將後半部分壓入棧再彈出
逆置方法二:可以直接改變鏈表節點的指向,不用額外的空間開銷,在原鏈表上直接將鏈表後半部分逆置
方法一實現:
public static boolean isPalind(Node head) {
Stack<Integer>stack = new Stack<>();
Node curr = head;
while(curr != null) {
stack.push(curr.value);
curr = curr.next;
}
curr = head;
while(!stack.isEmpty()) {
if(curr.value != stack.peek()) {
return false;
}
curr = curr.next;
stack.pop();
}
return true;
}
方法二實現:
//存儲鏈表後半部分
public static boolean isPalind(Node head) {
if (head == null || head.next == null) {
return true;
}
Stack<Integer>stack= new Stack<>();
Node fast = head.next;
Node slow = head;
while(fast!=null && fast.next.next!=null) {
slow = slow.next;
fast = fast.next.next;
}
while(slow!=null) {
stack.push(slow.value);
slow = slow.next;
}
slow = head;
while(!stack.isEmpty()) {
if(slow.value != stack.peek()) return false;
slow = slow.next;
stack.pop();
}
return true;
}
方法三實現:
public static boolean isPalindrome3(Node head) {
if (head == null || head.next == null) {
return true;
}
Node n1 = head;
Node n2 = head;
while (n2.next != null && n2.next.next != null) { // find mid node
n1 = n1.next; // n1 -> mid
n2 = n2.next.next; // n2 -> end
}
n2 = n1.next; // n2 -> right part first node
n1.next = null; // mid.next -> null
Node n3 = null;
while (n2 != null) { // right part convert
n3 = n2.next; // n3 -> save next node
n2.next = n1; // next of right node convert
n1 = n2; // n1 move
n2 = n3; // n2 move
}
n3 = n1; // n3 -> save last node
n2 = head;// n2 -> left first node
boolean res = true;
while (n1 != null && n2 != null) { // check palindrome
if (n1.value != n2.value) {
res = false;
break;
}
n1 = n1.next; // left to mid
n2 = n2.next; // right to mid
}
n1 = n3.next;
n3.next = null;
while (n1 != null) { // recover list
n2 = n1.next;
n1.next = n3;
n3 = n1;
n1 = n2;
}
return res;
}
三、鏈表的相交問題
情況一:兩個無環鏈表
不可能是X形狀,因爲單鏈表只能有一個指針
方法一:哈希,將第一條鏈表用哈希表存儲,遍歷第二條鏈表,找到包含的key就返 回,否則不存在
public static Node getFirstLoopNode(Node head) {
HashSet<Node>set = new HashSet<>();
while(head != null) {
if(set.contains(head)) return head;
set.add(head);
head = head.next;
}
return null;
}
方法二:快慢指針,快指針一次兩步,慢指針一次一步,如果有環第一次相遇將快指針返回頭結點,然後和慢指針以相同的速度移動,再次相遇的時候就是環入口(這種方法網上有很多詳解,大家可以查閱)
方法三:
遍歷第一個鏈表長度爲n1,遍歷第二個鏈表長度爲n2,長的鏈表先走|n2-n1|步,然後兩個鏈表一起走,肯定會同時到達相交節點
情況二:一個無環一個有環
這種情況下,不可能存在相交節點,這樣肯定有一個節點存在兩個指針
情況三:兩個有環鏈表
如何判斷是左邊還是右邊情況???
利用兩個指針,當他們相遇的時候,其中一個指針A繼續往前,另外一個指針B不動,如果A會和B相遇那麼就是第二種情況否則是第一種。
小結:
1.利用快慢指針法求出第一條鏈表的第一個入環節點loop1,第二條鏈表的第一個入環節點loop2
2.如果loop1null && loopnull 就是兩個無環鏈表的相交問題,遍歷鏈表找出最後一個節點和長度,如果最後一個鏈表不相等一定不相交,否則可以按上述方法
3.如果一個loop爲null,一定不想交
4.判斷loop1與loop2是否相等,如果相交就是答案,否則用上述判斷是否是66的形式加之判斷。