ปัญหา: หา Node กลางของ LinkedList
LinkedList ไม่มี Random Access (ไม่มี get(index) O(1)) วิธีธรรมดาคือ นับจำนวน Node ก่อน (1 pass) แล้วเดินไปครึ่งหนึ่ง (อีก 1 pass) = 2 Passes
Two Pointer Technique (Slow & Fast Pointers) ทำได้ใน 1 Pass O(n) Time, O(1) Space
อัลกอริทึม Two Pointers (Tortoise & Hare)
- สร้าง 2 Pointers: slow และ fast เริ่มที่ Head
- slow เดินทีละ 1 Node, fast เดินทีละ 2 Nodes
- เมื่อ fast ถึงปลาย (null หรือ next เป็น null) → slow จะอยู่ที่ Node กลาง
กรณีคี่ (Odd Length): 1→2→3→4→5
Slow: 1 → 2 → 3 (Middle)
Fast: 1 → 3 → 5 → null (Stop)
Middle = 3
กรณีคู่ (Even Length): 1→2→3→4→5→6
Slow: 1 → 2 → 3 (First Middle)
Fast: 1 → 3 → 5 → null (Stop)
Middle = 3 (First middle of two)
ตัวอย่างที่ 1: ใน main โดยตรง
public class LinkedList {
Node head;
static class Node {
int value;
Node next;
Node(int d) { value = d; next = null; }
}
public static void main(String[] args) {
LinkedList inputList = new LinkedList();
// สร้าง List: 100 → 200 → 330
inputList.head = new Node(100);
Node second = new Node(200);
Node third = new Node(330);
inputList.head.next = second;
second.next = third;
// แสดง List
Node current = inputList.head;
System.out.print("The linked list is defined as: ");
while (current != null) {
System.out.print(current.value + " ");
current = current.next;
}
// Two Pointer Technique
Node slow = inputList.head;
Node fast = inputList.head;
while (fast != null && fast.next != null) {
fast = fast.next.next; // Fast เดิน 2 ก้าว
if (fast != null) { // ตรวจสอบก่อนย้าย Slow
slow = slow.next; // Slow เดิน 1 ก้าว
}
}
System.out.println("\nThe middle element of the list is: " + slow.value);
}
}
ผลลัพธ์
The linked list is defined as: 100 200 330
The middle element of the list is: 200
ตัวอย่างที่ 2: แยกเป็นเมธอด (OOP)
public class LinkedList {
Node head;
static class Node {
int value;
Node next;
Node(int d) { value = d; next = null; }
}
// เมธอดหา Middle Element
static void getMiddleItem(LinkedList list) {
Node slow = list.head;
Node fast = list.head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
if (fast != null) {
slow = slow.next;
}
}
System.out.println("\nThe middle element of the list is: " + slow.value);
}
public static void main(String[] args) {
LinkedList inputList = new LinkedList();
inputList.head = new Node(100);
Node second = new Node(200);
Node third = new Node(330);
inputList.head.next = second;
second.next = third;
Node current = inputList.head;
System.out.print("The linked list is defined as: ");
while (current != null) {
System.out.print(current.value + " ");
current = current.next;
}
getMiddleItem(inputList);
}
}
Java Built-in: LinkedList.get(size/2)
java.util.LinkedList มี get(int index) แต่เป็น O(n) ต้อง Traverse จาก Head หรือ Tail
// ใช้ Built-in (ยังคงเป็น O(n) แต่เขียนสั้น)
LinkedList list = new LinkedList<>(Arrays.asList(100, 200, 330));
int middle = list.get(list.size() / 2); // O(n) - Traverse จาก Head/Tail
System.out.println(middle); // 200
Variations: หาจุดเริ่มต้นของ Loop (Cycle Detection)
Two Pointers ยังใช้หาจุดเริ่ม Loop ได้ (Floyd's Algorithm):
// Phase 1: Detect Loop
slow = head; fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) break; // Loop detected
}
// Phase 2: Find Loop Start
if (fast != null && fast.next != null) {
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
// slow/fast ตอนนี้คือ Start of Loop
}
การประยุกต์ใช้ Two Pointers อื่นๆ
| ปัญหา | Technique |
|---|---|
| หา Node กลาง | Slow 1x, Fast 2x |
| หา Node ที่ k จากท้าย | Fast เริ่มนำหน้า k steps, แล้วเดินคู่กัน |
| ตรวจจับ Cycle | Slow 1x, Fast 2x (Floyd's) |
| หาจุดเริ่ม Cycle | Floyd's Phase 2 |
| Palindrome LinkedList | หากลาง → Reverse ครึ่งหลัง → Compare |
สรุป
- Two Pointer (Slow/Fast) หา Middle Element ใน 1 Pass O(n) Time, O(1) Space
- Slow เดิน 1 ก้าว, Fast เดิน 2 ก้าว
- เมื่อ Fast ถึงปลาย Slow อยู่กลาง
- Odd Length: Middle จริง | Even Length: First Middle (Lower Middle)
- Technique นี้ใช้กันทั่วไปใน LinkedList Problems (LeetCode, Interviews)