前言
检查链表代码是否正确的边界条件:
- 若链表为 null,代码是否正常运行?
- 若链表只有一个结点,代码是否正常运行?
- 若链表只有两个结点,代码是否正常运行?
- 代码逻辑在处理头结点和尾结点的时候,代码是否正常运行?
链表的构造及打印
public static void main(String[] args) {
int[] arr = {
1, 3, 5, 7, 9, 11, 13, 15, 17};
LinkedListDemo linkedListDemo = new LinkedListDemo();
Node head1 = linkedListDemo.buildLinkedList(arr);
linkedListDemo.display(head1);
// 链表逆序
linkedListDemo.display(linkedListDemo.reverseList(head1));
linkedListDemo.display(head1);
}
private static class Node {
final Integer item;
Node next;
Node(Integer item, Node next) {
this.item = item;
this.next = next;
}
}
/**
* 打印链表
*
* @param header
*/
public void display(Node header) {
Node p = header;
while (p != null) {
System.out.print(p.item + " ");
p = p.next;
}
System.out.println();
}
/**
* 通过数组构造一个链表
*
* @param arr
* @return
*/
public Node buildLinkedList(int[] arr) {
if (Objects.isNull(arr) || arr.length < 1) {
throw new IllegalArgumentException("illegal arr");
}
Node head = new Node(arr[0], null);
if (arr.length >= 2) {
Node p = head;
for (int i = 1; i < arr.length; i++) {
Node temp = new Node(arr[i], null);
p.next = temp;
p = temp;
}
}
return head;
}
public void checkNull(Node head) {
if (head == null) {
throw new NullPointerException();
}
}
单链表逆序
/**
* 单链表逆序
*
* @param head
*/
public Node reverseList(Node head) {
this.checkNull(head);
Node p = head.next;
head.next = null;
Node temp;
while (p != null) {
temp = p.next;
p.next = head;
head = p;
p = temp;
}
return head;
}
合并两个有序链表
/**
* 合并两个有序链表
*
* @param head1
* @param head2
* @return
*/
public Node mergeTwoSortedLinkedList(Node head1, Node head2) {
if (head1 == null && head2 == null) {
throw new IllegalArgumentException("Illegal params");
}
if (head1 == null) {
return head2;
}
if (head2 == null) {
return head1;
}
// 设置一个哨兵结点
Node head = new Node(null, null);
Node p = head;
while (head1 != null && head2 != null) {
if (head1.item < head2.item) {
p.next = head1;
p = head1;
head1 = head1.next;
} else {
p.next = head2;
p = head2;
head2 = head2.next;
}
}
// 拼接链表的剩下部分
if (head1 != null) {
p.next = head1;
}
if (head2 != null) {
p.next = head2;
}
p = null;
return head.next;
}
删除链表倒数第 n 个结点
声明快慢两个指针,让快指针走 n 步,再让两个指针同时后移,直到快指针到指向最后一个结点时,慢指针的下一个结点就是要删除的结点。
/**
* 删除链表倒数第 n 个结点
*
* @param head
* @param n
* @return
*/
public Node removeNode(Node head, Integer n) {
if (head == null) {
return null;
}
Node slow = head, fast = head;
// 让快指针先走 n 步
int i = 0;
while (fast != null && i < n) {
fast = fast.next;
i++;
}
// 如果指定的 n 大于链表的长度,不进行删除处理
if (n > i) {
return head;
}
//如果指定的 n 等于链表的长度,删除第一个结点
if (fast == null) {
return head.next;
}
// 同时移动两个指针直到快指针指向尾结点
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
// 删除 slow.next
slow.next = slow.next.next;
return head;
}
找出链表的中间结点
声明快慢指针,慢指针只遍历一个结点,快指针速度为 2 倍,当快指针指向链表最后一个结点(链表总结点数是奇数)或快指针指向倒数第二个结点(链表总结点数是偶数)时,慢指针指向的即是链表的中间结点。
/**
* 找出链表的中间结点
*
* @param head
* @return
*/
public Node getMiddleNode(Node head) {
this.checkNull(head);
Node fast = head, slow = head;
while (fast != null && fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
链表中环的检测
参考:链表中环的检测