首先,定义链表的节点,以及链表打印函数。
class Node {
public Integer value;
public Node next;
}
public static void printList(Node head) {
Node node = head;
while (node != null) {
System.out.println(node.value);
node = node.next;
}
}
1. 在O(1)的时间删除单向链表节点 (《剑指offer》面试题13)
书中给出的方法是,O(1)的时间复杂度,将下一个节点的值复制到待删除的节点,然后直接删除下一个节点。
但是这种方法可能不合适,因为或许会有其他的指针指向下一个节点。
2. 两个单向链表的第一个公共节点 (《剑指offer》面试题37)
主要思路:由于链表是单向的,因此从第一个公共节点开始,后面的节点都是公共的。
分别遍历两个链表,得到其长度,并计算出长度差多少,然后长的链表先走这么几步。
3. 只遍历一遍,得到单向链表中倒数第k个节点 (《剑指offer》面试题15)
主要思路:两个指针,第一个指针先走k步,然后两个指针一起走,当前一个指针走到结尾的时候,后一个指针就到了倒数第k个节点。
注意边界情况,比如可能链表的长度小于k。
4. 合并两个排序的链表 (《剑指offer》面试题17)
其实就是归并排序中的归并操作。
以下代码,不改变输入参数,merge后的结果为一个新的链表。
public static Node merge(Node headA, Node headB) {
if (headA == null && headB == null) return null;
if (headA == null) return headB; // 这里应该复制到一个新的链表,这里为省事,不写了
if (headB == null) return headA;
Node n1 = headA;
Node n2 = headB;
Node node = new Node();
Node result = node;
while (n1 != null || n2 != null) {
if (n1 == null) {
node.next = new Node(n2);
n2 = n2.next;
node = node.next;
continue;
}
if (n2 == null) {
node.next = new Node(n1);
n1 = n1.next;
node = node.next;
continue;
}
if (n1.value < n2.value) {
node.next = new Node(n1);
n1 = n1.next;
} else {
node.next = new Node(n2);
n2 = n2.next;
}
node = node.next;
}
return result.next;
}
以上是迭代实现,也可以用递归。
5 反转链表 (《剑指offer》面试题16)
最容易的方法应该是用递归实现,如下:
public static Node reverse(Node head) {
if (head == null || head.next == null) return head;
Node next = head.next;
Node newHead = reverse(next);
next.next = head;
head.next = null;
return newHead;
}
改成迭代:
public static Node reverse(Node head) {
if (head == null || head.next == null) return head;
Node prevNode = null;
Node curNode = head;
Node nextNode = curNode.next;
while (nextNode != null) {
curNode.next = prevNode;
prevNode = curNode;
curNode = nextNode;
nextNode = nextNode.next;
}
curNode.next = prevNode;
return curNode;
}
总之,在写代码的时候,考虑问题要全面,并且处理好各种边界情况。