SinglyLinkedList
与数组相比,链表的插入删除不需要搬移数据,时间复杂度为 O(1)
插入:在结点 p 后插入 结点 x
x.next = p.next;
p.next = x;
删除:删除结点 p 后续第一个结点
if(p.next != null) {
p.next=p.next.next;
}
DoublyLinkedList、CircularLinkedList
双向链表:每个结点除了后继指针外,还存储了前驱指针 previous
循环链表:尾部结点指向头部结点,适合处理环形结构的数据
双向循环链表:结合了双向链表和循环链表的特点
双向链表比单链表灵活,删除性能更高
删除给定指针指向的结点时,单链表需要遍历找到该结点的前驱结点,双链表可以直接获取前驱结点
这是一种用空间换时间的设计思想
链表实现 最近最久未使用淘汰算法
维护一个单链表,越靠近链表尾部的结点,是越早之前访问的,当有新数据被访问时,从头遍历单链表
public class SinglyList {
// 哨兵结点
ListNode head = new ListNode(0);
// 在 pre 结点后插入 add 结点
public void add(ListNode pre, ListNode add) {
add.next = pre.next;
pre.next = add;
}
// 删除 node 后第一个结点
public void remove(ListNode node) {
if (node.next != null) {
node.next = node.next.next;
}
}
public String toString() {
String str = "head->";
ListNode temp = head;
while (temp.next != null) {
str += temp.next + " ";
temp = temp.next;
}
return str;
}
}
public class ListNode {
ListNode next;
int value;
public ListNode(int value) {
this.value = value;
}
@Override
public String toString() {
return value + "";
}
}
public class SinglyListTest {
public static void main(String[] args) {
ListNode node1 = new ListNode(1);
ListNode node2 = new ListNode(2);
ListNode node3 = new ListNode(3);
ListNode node4 = new ListNode(4);
// 创建单链表加入结点
SinglyList list = new SinglyList();
list.add(list.head, node1);
list.add(node1, node2);
list.add(node2, node3);
list.add(node3, node4);
// 打印
System.out.println(list);
}
}
head->1 2 3 4
public static void reverse(SinglyList list) {
SinglyList result = new SinglyList();
ListNode temp = list.head;
while (temp.next != null) {
temp = temp.next;
result.add(result.head, new ListNode(temp.value));
}
System.out.println(result);
}
快慢指针法,结点数分奇偶两种情况
public static void middle(SinglyList list) {
ListNode quick = list.head, slow = list.head;
boolean isEven = true;//结点是否为偶数个
while (quick.next != null) {
if (quick.next.next != null) quick = quick.next.next;//偶数
else {//奇数
quick = quick.next;
isEven = false;
}
slow = slow.next;
}
if (isEven) System.out.println(slow + "," + slow.next);
else System.out.println(slow);
}
public static void delete(SinglyList list, int n) {
ListNode p = list.head;
int len = 0;
while (p.next != null) {
p = p.next;
len++;
}
p = list.head;
for (int i = 0; i < len - n; i++) {
p = p.next;
}
list.remove(p);
System.out.println(list);
}
快慢指针法,快慢指针重复则有环
也可以使用一个 Set 去装结点,判断是否重复
public static void circle(SinglyList list) {
ListNode quick = list.head, slow = list.head;
while (true) {
quick = quick.next.next;
if (quick == null) {
System.out.println("have no circle");
break;
}
slow = slow.next;
if (quick == slow) {
System.out.println("have circle");
break;
}
}
}
带头链表遍历时需要注意起始结点
public static void merge(SinglyList l1, SinglyList l2) {
SinglyList result = new SinglyList();
ListNode tail = result.head;
ListNode head1 = l1.head;
ListNode head2 = l2.head;
while (head1.next != null || head2.next != null) {
if (head1.next != null && head2.next != null) {
if (head1.next.value < head2.next.value) {
head1 = head1.next;
result.add(tail, new ListNode(head1.value));
tail = tail.next;
} else {
head2 = head2.next;
result.add(tail, new ListNode(head2.value));
tail = tail.next;
}
}
if (head1.next == null && head2.next != null) {
head2 = head2.next;
result.add(tail, new ListNode(head2.value));
tail = tail.next;
}
if (head1.next != null && head2.next == null) {
head1 = head1.next;
result.add(tail, new ListNode(head1.value));
tail = tail.next;
}
}
System.out.println(result);
}