(“数据结构与算法之美” 专栏的课后思考题)
在做链表相关的算法题时, 要重点留意边界条件的处理
- 如果链表为空时, 代码能否正常工作
- 如果链表只包含一个结点时, 代码是否能正常工作
- 如果链表只包含两个结点时, 代码是否能正常工作
- 代码逻辑在处理头结点和尾结点的时候, 是否能正常工作
单链表反转
- 先处理头节点信息
- 循环到尾节点
- 处理尾节点信息
public static Node reverseList(Node head) {
if (head == null || head.next == null) return head;
Node curr = head.next;
Node prep = head;
head.next = null;
Node next = null;
while (curr.next != null) {
next = curr.next;
curr.next = prep;
prep = curr;
curr = next;
}
curr.next = prep;
return curr;
}
链表中环的检测
- 慢指针一次走一步, 快指针一次走两步
- while 循环 快指针碰到null, 无环
- while 循环, 两指针相交, 有环.
public static boolean hasCycle(Node head) {
if (head == null || head.next == null) return false;
Node low = head;
Node fast = head.next.next;
while (fast != null && fast.next != null) {
if (low == fast) return true;
low = low.next;
fast = fast.next.next;
}
return false;
}
合并两个有序链表
递归终止条件
l1 == null
l2 == null
递推公式
l1.next = merge(l1.next, l2)
l2.next = merge(l1, l2.next)
递归这么屌的吗?
public static Node mergeTwoLists(Node l1, Node l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
if (l1.val <= l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
删除链表倒数第n大元素
- 建立哨兵 fir, sec 均指向哨兵
- fir 走n+1步
- fir, sec一起平移,直到fir -> null
- sec.next 为要删除的节点,直接跳过即可
public static Node ahaha(Node head, int n) {
Node help = new Node(0);
help.next = head;
Node fir = help;
Node sec = help;
//fir 前进n + 1步
for (int i = 0; i <= n; i++) {
fir = fir.next;
}
while (fir != null) {
fir = fir.next;
sec = sec.next;
}
sec.next = sec.next.next;
return help.next;
}
寻找链表中间节点
不用哨兵项,fast跳到null, 为偶数个数结点。 fast 跳到倒数第一个结点, 为奇数个数结点。这两种情况下 low 前进一步。
public static Node middleNode(Node head) {
Node low = head;
Node fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
low = low.next;
}
return low;
}
快慢指针好像都涉及到
while(fast ! null && fast.next != null) 这个判断