数据结构与算法之链表
1. 基本知识
1.1 概念
通过指针将一组零散的内存块串联起来。
1.2 分类
- 单链表
- 单向循环链表
- 双向链表
- 双向循环链表
1.3 链表 VS 数组
- 数组需要一组连续的内存空间;链表只需要零散的内存空间;
- 数组的随机访问效率非常高,时间复杂度为 O(1),链表的查找需要遍历,时间复杂度为O(n);
- 数组的插入和删除涉及到数据的移动效率低下,而链表只需要相邻节点指针的改变,效率较高;
- 数组在声明时要指定长度,链表不需要。
1.4 链表的实现
package com.baidu.test.construct;
public interface List<T> {
void add(T data);
void set(int index, T data);
T get(int index);
void remove(int index);
void remove(T data);
boolean isEmpty();
int size();
void print();
}
1.4.1 单链表
package com.baidu.test.construct.linkedlist;
import com.baidu.test.construct.List;
/**
* 单链表
*/
public class SinglyLinkedList<T> implements List<T> {
// 头节点
private Node<T> head;
// 当前节点
private Node<T> current;
// 长度
private int size;
public SinglyLinkedList() {
this.head = this.current = new Node(null, null);
this.size = 0;
}
/**
* 添加
* @param data
*/
@Override
public void add(T data) {
index(size-1);
current.next = new Node(data, null);
size++;
}
/**
* 添加
* @param index
* @param data
*/
@Override
public void set(int index, T data) {
index(index-1);
Node node = new Node(data, current.next);
current.next = node;
size++;
}
/**
* 获取
* @param index
* @return
*/
@Override
public T get(int index) {
index(index);
return current.data;
}
/**
* 删除
*/
@Override
public void remove(T data) {
if (size == 0) {
return;
}
current = head;
while (current.next != null && current.next.data != data) {
current = current.next;
}
current.next = current.next.next;
size--;
}
/**
* 删除
*/
@Override
public void remove(int index) {
index(index-1);
current.next = current.next.next;
size--;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public void print() {
current = head.next;
while (current != null) {
System.out.print(current.data + ",");
current = current.next;
}
System.out.println();
}
/**
* 定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点,
* 该定位为正序定位,其实应该判断index位置,在进行正序或反序定位
* @param index
*/
private void index(int index) {
if (index < -1 && index > size -1) {
throw new IndexOutOfBoundsException();
}
// 说明当前单链表为空链表
if (index == -1) {
current = head;
return;
}
current = head.next;
int j = 0;
while (j < index) {
current = current.next;
j++;
}
}
/**
* 单链表反转
* 1 -> 2 -> 3 -> 4
* 4 -> 3 -> 2 -> 1
* @return
*/
public Node reverseList(Node head) {
Node pre = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
private static class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
public static void main(String[] args) {
SinglyLinkedList<String> list = new SinglyLinkedList<>();
list.add("a");
list.add("c");
list.add("d");
list.set(1, "b");
list.print();
System.out.println(list.size());
System.out.println(list.get(2));
list.remove("c");
list.print();
list.remove(1);
list.print();
list = new SinglyLinkedList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.print();
Node node = list.reverseList(list.head);
while (node != null && node.data != null) {
System.out.print(node.data +",");
node = node.next;
}
System.out.println();
}
}
1.4.2 单向循环链表
package com.baidu.test.construct.linkedlist;
import com.baidu.test.construct.List;
/**
* 单向循环链表
* @param <T>
*/
public class SinglyCycleLinkedList<T> implements List<T> {
// 头节点
private Node<T> head;
// 当前节点
private Node<T> current;
// 长度
private int size;
public SinglyCycleLinkedList() {
this.head = this.current = new Node<>(null, null);
this.head.next = this.head;
this.size = 0;
}
@Override
public void add(T data) {
index(size-1);
Node<T> node = new Node<>(data, current.next);
current.next = node;
size++;
}
@Override
public void set(int index, T data) {
index(index-1);
Node<T> node = new Node<>(data, current.next);
current.next = node;
size++;
}
@Override
public T get(int index) {
index(index);
return current.data;
}
@Override
public void remove(int index) {
index(index-1);
current.next = current.next.next;
size--;
}
@Override
public void remove(T data) {
if (size == 0) {
return;
}
current = head;
while (current.next != head && current.next.data != data) {
current = current.next;
}
current.next = current.next.next;
size--;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public void print() {
current = head.next;
while (current != head) {
System.out.print(current.data);
current = current.next;
}
System.out.println();
}
/**
* 定位函数,实现当前操作对象的前一个结点,也就是让当前结点对象定位到要操作结点的前一个结点,
* 该定位为正序定位,其实应该判断index位置,在进行正序或反序定位
* @param index
*/
private void index(int index) {
if (index < -1 && index > size -1) {
throw new IndexOutOfBoundsException();
}
if (index == -1) {
current = head;
return;
}
current = head.next;
int j=0;
while (current != head && j < index) {
current = current.next;
j++;
}
}
private class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
}
public static void main(String[] args) {
SinglyCycleLinkedList list = new SinglyCycleLinkedList();
list.add("a");
list.add("c");
list.add("d");
list.set(1, "b");
list.print();
System.out.println(list.size());
System.out.println(list.get(2));
list.remove("c");
list.print();
list.remove(1);
list.print();
}
}
1.4.3 双向链表
package com.baidu.test.construct.linkedlist;
import com.baidu.test.construct.List;
/**
* 双向链表
* @param <T>
*/
public class DoubleLinkedList<T> implements List<T> {
// 头节点
private Node<T> head;
// 当前节点
private Node<T> current;
// 长度
private int size;
public DoubleLinkedList() {
this.head = this.current = new Node<>(null, null, null);
size = 0;
}
@Override
public void add(T data) {
index(size-1);
Node<T> node = new Node<>(data, current, current.next);
current.next = node;
size++;
}
@Override
public void set(int index, T data) {
index(index-1);
Node<T> node = new Node<>(data, current, current.next);
current.next = node;
size++;
}
@Override
public T get(int index) {
index(index);
return current.data;
}
@Override
public void remove(int index) {
index(index-1);
current.next.next.prior = current;
current.next = current.next.next;
size--;
}
@Override
public void remove(T data) {
if (size == 0) {
return;
}
current = head;
if (current.next != null && current.next.data != data) {
current = current.next;
}
current.next.next.prior = current;
current.next = current.next.next;
size--;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public void print() {
current = head.next;
while (current != null) {
System.out.print(current.data);
current = current.next;
}
System.out.println();
}
private void index(int index) {
if (index < -1 && index > size -1) {
throw new IndexOutOfBoundsException();
}
if (index == -1) {
current = head;
return;
}
current = head.next;
int j=0;
while (j<index) {
current = current.next;
j++;
}
}
private class Node<T> {
private T data;
private Node<T> prior;
private Node<T> next;
public Node(T data, Node<T> prior, Node<T> next) {
this.data = data;
this.prior = prior;
this.next = next;
}
}
public static void main(String[] args) {
DoubleLinkedList list = new DoubleLinkedList();
list.add("a");
list.add("c");
list.add("d");
list.set(1, "b");
list.print();
System.out.println(list.size());
System.out.println(list.get(2));
list.remove("c");
list.print();
list.remove(1);
list.print();
}
}
1.4.4 双向循环链表
package com.baidu.test.construct.linkedlist;
import com.baidu.test.construct.List;
/**
* 双向循环链表
*/
public class DoubleCycleLinkedList<T> implements List<T> {
// 头节点
private Node<T> head;
// 当前节点
private Node<T> current;
// 长度
private int size;
public DoubleCycleLinkedList() {
this.head = this.current = new Node<>(null, null, null);
this.head.prior = this.head;
this.head.next = this.head;
this.size = 0;
}
@Override
public void add(T data) {
index(size-1);
Node<T> node = new Node<>(data, current, current.next);
current.next = node;
size++;
}
@Override
public void set(int index, T data) {
index(index-1);
Node<T> node = new Node<>(data, current, current.next);
current.next.next.prior = node;
current.next = node;
size++;
}
@Override
public T get(int index) {
index(index);
return current.data;
}
@Override
public void remove(int index) {
index(index-1);
current.next.next.prior = current;
current.next = current.next.next;
size--;
}
@Override
public void remove(T data) {
if (size == 0) {
return;
}
current = head;
while (current.next != head && current.next.data != data) {
current = current.next;
}
current.next.next.prior = current;
current.next = current.next.next;
size--;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public void print() {
current = head.next;
while (current != head) {
System.out.print(current.data);
current = current.next;
}
System.out.println();
}
private void index(int index) {
if (index < -1 && index > size-1) {
throw new IndexOutOfBoundsException();
}
if (index == -1) {
current = head;
return;
}
current = head.next;
int j=0;
while (current != head && j<index) {
current = current.next;
j++;
}
}
private class Node<T> {
private T data;
private Node<T> prior;
private Node<T> next;
public Node(T data, Node<T> prior, Node<T> next) {
this.data = data;
this.prior = prior;
this.next = next;
}
}
public static void main(String[] args) {
DoubleCycleLinkedList list = new DoubleCycleLinkedList();
list.add("a");
list.add("c");
list.add("d");
list.set(1, "b");
list.print();
System.out.println(list.size());
System.out.println(list.get(2));
list.remove("c");
list.print();
list.remove(1);
list.print();
}
}
2. 链表的应用
2.1 通过链表实现LRU缓存淘汰算法
package com.baidu.test.construct.linkedlist;
import java.util.Scanner;
/**
* Least Recently Used 最近最少使用
*/
public class LRUWithLInkedList<T> {
// 链表默认容量
private final static int DEFAULT_CAPACITY = 10;
// 头节点
private Node<T> head;
// 当前节点
private Node<T> current;
// 链表长度
private int size;
// 链表容量
private int capacity;
public LRUWithLInkedList() {
this.head = this.current = new Node<>(null, null);
this.capacity = DEFAULT_CAPACITY;
this.size = 0;
}
public LRUWithLInkedList(int capacity) {
this.head = this.current = new Node<>(null, null);
this.capacity = capacity;
this.size = 0;
}
/**
* 添加元素,链表中存在,则删除原节点,在插入链表头部;
* 不存在,则判断链表长度,如果长度超长,删除尾节点,否则直接头部插入
* @param data
*/
public void add(T data) {
Node preNode = findValue(data);
if (preNode == null) { // 不存在
if (size >= capacity) {
deleteEndNode();
}
insertElementToHead(data);
} else { // 存在
deleteByPreNode(preNode);
insertElementToHead(data);
}
}
/**
* 根据value在链表中查找是否存在
* @param data
* @return 返回该节点的上一个节点
*/
private Node findValue(T data) {
if (size == 0) {
return null;
}
current = head;
while (current.next != null) {
if (current.next.data.equals(data)) {
return current;
}
current = current.next;
}
return null;
}
/**
* 向链表头部插入数据
*/
private void insertElementToHead(T data) {
head.next = new Node<>(data, head.next);
size++;
}
/**
* 删除尾节点
*/
private void deleteEndNode() {
index(size-2);
current.next = null;
size--;
}
/**
* 删除节点
*/
private void deleteByPreNode(Node preNode) {
preNode.next = preNode.next.next;
size--;
}
/**
* 定位函数
* @param index
*/
private void index(int index) {
if (index < -1 || index > size -1) {
throw new IndexOutOfBoundsException();
}
if (index == -1) {
current = head;
return;
}
current = head.next;
int i=0;
while (i<index) {
current = current.next;
i++;
}
}
private void print() {
current = head.next;
while (current != null) {
System.out.print(current.data + "->");
current = current.next;
}
System.out.println();
}
private static class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
}
public static void main(String[] args) {
LRUWithLInkedList<Integer> list = new LRUWithLInkedList<>();
Scanner scanner = new Scanner(System.in);
while (true) {
list.add(scanner.nextInt());
list.print();
}
}
}
#测试结果如下:
1
1->
2
2->1->
3
3->2->1->
4
4->3->2->1->
5
5->4->3->2->1->
6
6->5->4->3->2->1->
7
7->6->5->4->3->2->1->
8
8->7->6->5->4->3->2->1->
2
2->8->7->6->5->4->3->1->
9
9->2->8->7->6->5->4->3->1->
0
0->9->2->8->7->6->5->4->3->1->
10
10->0->9->2->8->7->6->5->4->3->
上面算法的在查找数据的时候,效率不高,因为链表需要一个节点一个节点的循环遍历,因此可以使用双向循环链表 + HashMap 的实现进行改进,实现 O(1) 的时间复杂度。
package com.baidu.test.construct.linkedlist;
import java.util.HashMap;
import java.util.Scanner;
/**
* 基于双向循环链表 + hashMap 实现的 LRU 算法
* get:获取时,将数据移到链表头部
* add:添加时,长度不超限时,链表头部直接插入;超限时,插入链表头部,同时删除链表尾部数据
* @param <K>
* @param <V>
*/
public class LRUWithHashMap<K, V> {
private static final int DEFAULT_CAPACITY = 10;
// 头节点
private Node<K, V> head;
// 尾节点
private Node<K, V> tail;
// 链表长度
private int length;
// 链表容量
private int capacity;
private HashMap<K, Node<K, V>> map;
public LRUWithHashMap() {
this(DEFAULT_CAPACITY);
}
public LRUWithHashMap(int capacity) {
this.head = new Node<>();
this.tail = new Node<>();
head.next = tail;
tail.pre = head;
this.length = 0;
this.capacity = capacity;
map = new HashMap<>();;
}
/**
* 添加,是否存在,长度小于等于容量,头插法;否则头插法并且删除表尾节点
* @param key
* @param value
*/
public void add(K key, V value) {
Node<K, V> node = map.get(key);
if (node == null) {
node = new Node<>(key, value);
map.put(key, node);
addNode(node);
if (++length > capacity) {
Node<K, V> tailNode = popTail();
map.remove(tailNode.key);
length --;
}
} else {
node.value = value;
moveToHead(node);
}
}
/**
* 获取,将数据移动到表头
* @param key
* @return
*/
public V get(K key) {
Node<K, V> node = map.get(key);
if (node == null) {
return null;
}
moveToHead(node);
return node.value;
}
/**
* 将节点插入头部
*/
private void addNode(Node<K, V> node) {
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
}
/**
* 将节点移动到头部:先删除该节点,然后在头部新增该节点
* @param node
*/
private void moveToHead(Node<K, V> node) {
removeNode(node);
addNode(node);
}
/**
* 删除节点
* @param node
*/
private void removeNode(Node<K, V> node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
/**
* 弹出尾节点
* @return
*/
private Node<K, V> popTail() {
Node<K, V> tailNode = tail.pre;
removeNode(tailNode);
return tailNode;
}
private void print() {
Node<K, V> current = head.next;
while (current != null && current.value != null) {
System.out.print(current.value + " -> ");
current = current.next;
}
System.out.println();
}
/**
* 双向循环链表
* @param <K>
* @param <V>
*/
private static class Node<K, V> {
private K key;
private V value;
private Node<K, V> pre;
private Node<K, V> next;
public Node() {
}
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
public static void main(String[] args) {
LRUWithHashMap<Integer, Integer> lru = new LRUWithHashMap(6);
Scanner scanner = new Scanner(System.in);
while (true) {
int i = scanner.nextInt();
if (i == -1) {
break;
}
lru.add(i, i);
lru.print();
}
int value = lru.get(5);
lru.print();
}
}
### 运行结果如下:
1
1 ->
3
3 -> 1 ->
5
5 -> 3 -> 1 ->
7
7 -> 5 -> 3 -> 1 ->
9
9 -> 7 -> 5 -> 3 -> 1 ->
10
10 -> 9 -> 7 -> 5 -> 3 -> 1 ->
20
20 -> 10 -> 9 -> 7 -> 5 -> 3 ->
30
30 -> 20 -> 10 -> 9 -> 7 -> 5 ->
5
5 -> 30 -> 20 -> 10 -> 9 -> 7 ->
8
8 -> 5 -> 30 -> 20 -> 10 -> 9 ->
5
5 -> 8 -> 30 -> 20 -> 10 -> 9 ->
-1
5 -> 8 -> 30 -> 20 -> 10 -> 9 ->
2.2 链表常见面试题
- 单链表反转
- 链表中环的检测
- 两个有序的链表合并
- 删除链表倒数第n个结点
- 求链表的中间结点
package com.baidu.test.construct.linkedlist;
import java.util.HashSet;
import java.util.Set;
/**
* 链表方面练习:
* 1. 链表反转
*/
public class LinkedListDemo {
private static class Node {
private int data;
private Node next;
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
/**
* 链表反转
* @param head
* @return
*/
public static Node reverseList(Node head) {
Node pre = null;
Node next = null;
while (null != head) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
/**
* 检测链表中是否存在环,快慢指针法
* @param head
* @return
*/
public static boolean verifyCircle(Node head) {
if (head == null) {
return false;
}
Node fastNode = head.next;
Node slowNode = head;
while (fastNode != null && fastNode.next != null) {
fastNode = fastNode.next.next;
slowNode = slowNode.next;
if (fastNode == slowNode) {
System.out.println("存在中环,重复节点为:" + fastNode.data);
return true;
}
}
return false;
}
/**
* 检测链表中是否存在环,足迹法
* @param head
* @return
*/
public static boolean verifyCircleWithMap(Node head) {
Set set = new HashSet();
while (null != head) {
if (set.contains(head.data)) {
return true;
}
set.add(head.data);
head = head.next;
}
return false;
}
/**
* 合并两个有序链表
* @param node1
* @param node2
* @return
*/
public static Node merge2LinkedList(Node node1, Node node2) {
Node head = new Node(0, null);
Node current = head;
while (node1 != null && node2 != null) {
if (node1.data < node2.data) {
current.next = node1;
node1 = node1.next;
} else {
current.next = node2;
node2 = node2.next;
}
current = current.next;
}
if (node1 != null) {
current.next = node1;
}
if (node2 != null) {
current.next = node2;
}
return head.next;
}
/**
* 求链表中间节点,快慢指针法
* @param head
* @return
*/
public static Node findMiddleNode(Node head) {
Node fastNode = head;
Node slowNode = head;
while (fastNode != null && fastNode.next != null) {
fastNode = fastNode.next.next;
slowNode = slowNode.next;
}
return slowNode;
}
/**
* 删除链表倒数第K个节点,快慢指针法
* @param head
* @return
*/
public static Node deleteLastKth(Node head, int k) {
Node fastNode = head;
int i=0;
while (i<k && fastNode != null) {
fastNode = fastNode.next;
i++;
}
if (fastNode == null) {
return fastNode;
}
while (fastNode != null) {
fastNode = fastNode.next;
head = head.next;
}
return head;
}
/**
* 约瑟夫问题,N个人围成一圈,第一个人从1开始报数,报M的被杀掉,下一个人接着从1开始报,
* 循环反复,直到剩下最后一个,那最后胜利者的初始位置在哪里
*/
public static void yuesefu(Node head, int n) {
int i=1;
while (head != head.next) {
i++;
if (i%n == 0) {
i++;
System.out.print(head.next.data + "->");
head.next = head.next.next;
}
head = head.next;
}
System.out.println("安全位置为:" + head.data);
}
public static void testYuesefu() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
node11.next = node1;
yuesefu(node1, 3);
}
public static void testReverseList() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
Node node = reverseList(node1);
while (node != null) {
System.out.print(node.data + "->");
node = node.next;
}
System.out.println();
}
public static void testVerifyCircle() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
node11.next = node5;
boolean result = verifyCircle(node1);
System.out.println("检测链表中是否存在环:" + result);
}
public static void testVerifyCircleWithMap() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
node11.next = node5;
boolean result = verifyCircleWithMap(node1);
System.out.println("检测链表中是否存在环:" + result);
}
public static void testMerge2List() {
Node node4 = new Node(40, null);
Node node3 = new Node(30, node4);
Node node2 = new Node(20, node3);
Node node1 = new Node(1, node2);
Node node11 = new Node(51, null);
Node node10 = new Node(40, node11);
Node node9 = new Node(39, node10);
Node node8 = new Node(28, node9);
Node node7 = new Node(17, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node = merge2LinkedList(node1, node5);
while (node != null) {
System.out.print(node.data + "->");
node = node.next;
}
}
public static void testFindMiddleNode() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
Node middleNode = findMiddleNode(node1);
System.out.println(middleNode.data);
}
public static void testDeleteLastKth() {
Node node11 = new Node(11, null);
Node node10 = new Node(10, node11);
Node node9 = new Node(9, node10);
Node node8 = new Node(8, node9);
Node node7 = new Node(7, node8);
Node node6 = new Node(6, node7);
Node node5 = new Node(5, node6);
Node node4 = new Node(4, node5);
Node node3 = new Node(3, node4);
Node node2 = new Node(2, node3);
Node node1 = new Node(1, node2);
Node node = deleteLastKth(node1, 7);
System.out.println(node.data);
}
public static void main(String[] args) {
System.out.println("约瑟夫问题:");
testYuesefu();
System.out.println("链表反转:");
testReverseList();
System.out.println("检测链表中是否存在环:");
testVerifyCircle();
testVerifyCircleWithMap();
System.out.println("合并两个有序链表:");
testMerge2List();
System.out.println("求链表中间节点:");
testFindMiddleNode();
System.out.println("删除倒数第K个节点:");
testDeleteLastKth();
}
}
# 测试结果:
约瑟夫问题:
3->6->9->1->5->10->4->11->8->2->安全位置为:7
链表反转:
11->10->9->8->7->6->5->4->3->2->1->
检测链表中是否存在环:
存在中环,重复节点为:7
检测链表中是否存在环:true
检测链表中是否存在环:true
合并两个有序链表:
1->5->6->17->20->28->30->39->40->40->51->求链表中间节点:
6
删除倒数第K个节点:
5