一、单向链表
建立一个SinglyLinkedList类,实现单向链表的主要功能
1、不带哨兵的单链表
即第一个节点数据域val有实际值,所以很多操作都需要判断head是否为null
import java.util.function.Consumer;
class Node {
int val; // 存储数据
Node next; // 指向下一个节点的指针
Node(int val, Node next) {
this.val = val;
this.next = next;
}
}
public class SinglyLinkedList {
private Node head = null; // 头节点
/**
* 创建链表
* @param array 用于创建链表的数组
*/
public SinglyLinkedList(int[] array) {
if(array == null || array.length == 0) {
return;
}
head = new Node(array[0], null);
Node p = head;
for(int i= 1; i < array.length; i++) {
p.next = new Node(array[i], null);
p = p.next;
}
}
/**
* 添加到链表头部
* @param val 要添加的数据
*/
public void insertToHead(int val) {
head = new Node(val, head);
}
/**
* 添加到链表尾部
* @param val 要添加的数据
*/
public void insertToTail(int val) {
Node last = findLast();
if(last == null) {
insertToHead(val);
}else {
last.next = new Node(val, null);
}
}
/**
* 查找指定元素
* @param val 要查找的元素
* @return 返回找到的节点
*/
public Node findByValue(int val) {
Node p = head; // 从头节点开始遍历
while(p != null) {
if(p.val == val) {
return p;
}
p = p.next;
}
return null; // 没找到
}
/**
* 遍历链表
* @param consumer 一个函数式接口
*/
public void printAll(Consumer<Integer> consumer) {
Node p = head;
while(p != null) {
consumer.accept(p.val); // 调用函数式接口的 accept 方法
p = p.next;
}
}
/**
* 查找指定索引的节点
* @param index 要查找的索引 0 <= index < size
* @return 返回找到的节点
*/
public Node findByIndexFor(int index) {
Node p = head;
int pos = 0;
for(; p != null; p = p.next, pos++) {
if(pos == index) {
return p;
}
}
return null; // 没找到(链表为空或index 越界)
}
public Node findByIndexWhile(int index) {
Node p = head;
int cnt = 0;
while (p != null && index != cnt) {
cnt++;
p = p.next;
}
return p;
}
/**
* 查找指定索引的节点的值
* @param index 要查找的索引 0 <= index < size
* @return 返回找到的节点的值
*/
public int findValueByIndex(int index) {
Node p = findByIndexFor(index);
if(p == null) {
throw new IllegalArgumentException(String.format("链表为空 或 index [%d] 不合法", index)); // 抛出异常
}
return p.val;
}
/**
* 在指定索引处插入节点
* @param index 要插入的索引
* @param value 要插入的值
*/
public void insertValueByIndex(int index, int value) {
// 如果 index == 0,相当于在头部插入
if(index == 0) {
insertToHead(value);
return;
}
// 找到 index - 1 处的节点
Node prev = findByIndexFor(index - 1);
if(prev == null) {
throw new IllegalArgumentException(String.format("链表为空 或 index [%d] 不合法", index)); // 抛出异常
}else {
// 在 prev 后面插入, prev.next 为 null 时,相当于在尾部插入
prev.next = new Node(value, prev.next);
}
}
/**
* 查找最后一个节点
*/
public Node findLast() {
Node p = head;
if(p == null) {
return null;
}
while(p.next != null) {
p = p.next;
}
return p;
}
/**
* 删第一个节点
*/
public void removeFirst() {
if(head != null) {
// 直接将 head 指向下一个节点
head = head.next;
}
}
/**
* 删除指定索引的节点
* @param index 要删除的索引
*/
public void removeByIndex(int index) {
if(index == 0) {
removeFirst();
return;
}
// 找到 index - 1 处的节点
Node prev = findByIndexFor(index - 1);
if(prev == null) {
throw new IllegalArgumentException(String.format("链表为空 或 index [%d] 不合法", index)); // 抛出异常
}
// 删除 prev 后面的节点
prev.next = prev.next.next;
}
/**
* 计算链表长度
* @return 返回链表长度
*/
public int size() {
if(head == null) return 0;
int cnt = 0;
Node p = head;
while(p != null) {
cnt++;
p = p.next;
}
return cnt;
}
public static void main(String[] args) {
int[] num = {-10,0,10,20,-10,-40};
SinglyLinkedList list = new SinglyLinkedList(num);
//int val = list.findValueByIndex(5);
//Node node = list.findByIndexFor(1);
//Node node = list.findByIndexWhile(0);
//Node node = list.findLast();
//System.out.println(node.val);
//list.insertToTail(100);
//list.insertValueByIndex(5, 100);
//list.removeFirst();
//list.removeByIndex(6);
//list.printAll(System.out::println);
System.out.println(list.size());
}
}
2、带哨兵的单链表
即第一个节点的数据域val的值不具有实际意义,head作为哨兵节点,head.next作为头结点
/**
* @version 1.0
* @Author zzps
* @Date 2023/8/8 14:50
* @注释
*/
public class SinglyLinkedListSentinel {
static class Node {
int val; // 存储数据
Node next; // 指向下一个节点的指针
Node(int val, Node next) {
this.val = val;
this.next = next;
}
}
private final Node head = new Node(-1, null); // 头哨兵,值为-1,指向null
/**
* 创建链表
* @param array 用于创建链表的数组
*/
public SinglyLinkedListSentinel(int[] array) {
if (array == null || array.length == 0) {
return;
}
Node p = head; // p指向头哨兵
for (int j : array) {
p.next = new Node(j, null);
p = p.next;
}
}
/**
* 添加到链表头部
* @param val 要添加的数据
*/
public void insertToHead(int val) {
// head.next指向原来的第一个节点
// 创建新节点,新节点的next指向原来的第一个节点
// head.next指向新节点
head.next = new Node(val, head.next);
}
/**
* 添加到链表尾部
* @param val 要添加的数据
*/
public void insertToTail(int val) {
// 找到最后一个节点
Node last = findLast();
if (last == null) {
insertToHead(val);
} else {
last.next = new Node(val, null);
}
}
/**
* 查找指定元素
* @param val 要查找的元素
* @return 返回找到的节点
*/
public Node findByValue(int val) {
Node p = head.next; // 从头节点开始遍历
while (p != null) {
if (p.val == val) {
return p;
}
p = p.next;
}
return null; // 没有找到返回null
}
/**
* 查找指定元素的前一个节点
* @param val 要查找的元素
* @return 返回找到的节点
*/
public Node findPrev(int val) {
Node p = head; // 从哨兵节点开始遍历
while (p.next != null) {
if (p.next.val == val) {
return p;
}
p = p.next;
}
return null; // 没有找到返回null
}
/**
* 删除指定元素
* @param val 要删除的元素
*/
public void deleteByValue(int val) {
// 找到要删除元素的前一个节点
Node prev = findPrev(val);
if (prev == null) {
return;
}
prev.next = prev.next.next;
}
/**
* 打印链表
*/
public void printAll() {
Node p = head.next;
while (p != null) {
System.out.print(p.val + " ");
p = p.next;
}
System.out.print("\n");
}
/**
* 查找最后一个节点
* @return 返回最后一个节点
*/
private Node findLast() {
Node p = head;
while (p.next != null) {
p = p.next;
}
return p;
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4};
SinglyLinkedListSentinel singlyLinkedList = new SinglyLinkedListSentinel(array);
singlyLinkedList.insertToHead(0);
singlyLinkedList.insertToTail(5);
singlyLinkedList.deleteByValue(0);
// singlyLinkedList.deleteByValue(5);
// singlyLinkedList.deleteByValue(3);
singlyLinkedList.printAll();
}
}
二、双向链表
建立一个带头哨兵和尾哨兵的双向链表
public class DoublyLinkedList {
static class ListNode {
int val; // 存储数据
ListNode prev; // 指向前一个节点的指针
ListNode next; // 指向下一个节点的指针
ListNode(int val, ListNode prev, ListNode next) {
this.val = val;
this.prev = prev;
this.next = next;
}
}
private ListNode head; // 头哨兵
private ListNode tail; // 尾哨兵
/**
* 创建一个双向链表
*/
public DoublyLinkedList() {
// 创建头尾哨兵
head = new ListNode(-1, null, null);
tail = new ListNode(-1, head, null);
// 头尾哨兵相互指向
head.next = tail;
}
/**
* 查找指定索引的元素
* @param index 要查找的索引 假设哨兵节点的索引为-1
*/
public ListNode findNodeByIndex(int index) {
int i = -1;
for(ListNode p = head; p != tail; p = p.next, i++) {
if(index == i) {
return p;
}
}
return null;
}
/**
* 添加到头部
*/
public void insertToHead(int value) {
ListNode newNode = new ListNode(value, head, head.next);
head.next.prev = newNode;
head.next = newNode;
}
/**
* 添加到尾部
*/
public void insertToTail(int value) {
// 找到尾节点
ListNode last = tail.prev;
// 创建新节点
ListNode newNode = new ListNode(value, last, tail);
// 修改前一节点的next指针
last.next = newNode;
// 修改尾节点的prev指针
tail.prev = newNode;
}
/**
* 在指定索引处插入节点
* @param index 要插入的索引
* @param value 要插入的值
*/
public void insertToIndex(int index, int value) {
// 找到要插入的位置
ListNode p = findNodeByIndex(index);
if(p == null) {
throw new IndexOutOfBoundsException("index = " + index + "不合法");
}
// 创建新节点
ListNode newNode = new ListNode(value, p.prev, p);
// 修改前一个节点的指针
p.prev.next = newNode;
// 修改后一个节点的指针
p.prev = newNode;
}
/**
* 删除指定索引的节点
* @param index 要删除的索引
*/
public void removeByIndex(int index) {
// 找到要删除的节点
ListNode p = findNodeByIndex(index);
if(p == null) {
throw new IndexOutOfBoundsException("index = " + index + "不合法");
}
// 修改前一个节点的指针
p.prev.next = p.next;
// 修改后一个节点的指针
p.next.prev = p.prev;
}
/**
* 删除第一个节点
*/
public void removeFirst() {
if(head.next != tail) { // 链表不为空
// 修改第一个节点的指针
head.next = head.next.next;
// 修改第二个节点的指针
head.next.prev = head;
}
}
/**
* 删除最后一个节点
*/
public void removeLast() {
if(tail.prev != head) {
tail.prev = tail.prev.prev;
tail.prev.next = tail;
}
}
/**
* 打印链表
*/
public void print() {
for (ListNode p = head.next; p != tail; p = p.next) {
System.out.print(p.val + " ");
}
System.out.println();
}
/**
* 迭代器
*/
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
ListNode p = head.next;
@Override
public boolean hasNext() {
return p != tail;
}
@Override
public Integer next() {
int val = p.val;
p = p.next;
return val;
}
};
}
public static void main(String[] args) {
DoublyLinkedList list = new DoublyLinkedList();
list.insertToHead(1);
list.insertToHead(2);
list.insertToHead(3);
list.insertToTail(4);
list.insertToTail(5);
list.insertToTail(6);
list.insertToIndex(3, 7);
list.print(); // 3 2 1 7 4 5 6
list.removeByIndex(3);
list.print(); // 3 2 1 4 5 6
list.removeFirst();
list.print(); // 2 1 4 5 6
list.removeLast();
list.print(); // 2 1 4 5
}
}
三、双向环形链表
实现一个带哨兵节点的双向环形链表
import java.util.Iterator;
/**
* @version 1.0
* @Author zzps
* @Date 2023/8/8 21:24
* @注释 双向环形链表的实现
*/
public class DoublyCircularLinkedList {
static class ListNode {
int val;
ListNode prev;
ListNode next;
ListNode(int val, ListNode prev, ListNode next) {
this.val = val;
this.prev = prev;
this.next = next;
}
}
// 哨兵节点,既作为头哨兵,也作为尾哨兵
private final ListNode sentinel;
/**
* 创建一个双向环形链表
*/
public DoublyCircularLinkedList() {
sentinel = new ListNode(-1, null, null);
sentinel.prev = sentinel;
sentinel.next = sentinel;
}
/**
* 查找指定索引的元素
* @param index 要查找的索引 假设哨兵节点的索引为-1
*/
public ListNode findNodeByIndex(int index) {
int i = -1;
for(ListNode p =sentinel.next; p != sentinel; p = p.next) {
i++;
if(i == index) {
return p;
}
}
return null;
}
/**
* 添加到头部
*/
public void insertToHead(int value) {
ListNode newNode = new ListNode(value, sentinel, sentinel.next);
sentinel.next.prev = newNode;
sentinel.next = newNode;
}
/**
* 添加到尾部
*/
public void insertToTail(int value) {
ListNode last = sentinel.prev;
ListNode newNode = new ListNode(value, last, sentinel);
last.next = newNode;
sentinel.prev = newNode;
}
/**
* 删除指定索引的节点
*/
public void removeByIndex(int index) {
ListNode p =findNodeByIndex(index);
if(p == null) {
throw new IndexOutOfBoundsException("index = " + index + "不合法");
}
p.prev.next = p.next;
p.next.prev= p.prev;
}
/**
* 在指定索引处插入节点
*/
public void insertToIndex(int index, int value) {
ListNode p = findNodeByIndex(index);
if(p == null) {
throw new IndexOutOfBoundsException("index = " + index + "不合法");
}
ListNode newNode = new ListNode(value, p.prev, p);
p.prev.next = newNode;
p.prev = newNode;
}
/**
* 打印链表
*/
public void printList() {
for (ListNode p = sentinel.next; p != sentinel; p = p.next) {
System.out.print(p.val + " ");
}
System.out.println();
}
/**
* 迭代器
*/
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
ListNode p = sentinel.next;
@Override
public boolean hasNext() {
return p != sentinel;
}
@Override
public Integer next() {
int val = p.val;
p = p.next;
return val;
}
};
}
public static void main(String[] args) {
DoublyCircularLinkedList list = new DoublyCircularLinkedList();
list.insertToHead(1);
list.insertToHead(2);
list.insertToHead(3);
list.insertToTail(4);
list.insertToTail(5);
list.insertToTail(6);
list.printList(); // 3 2 1 4 5 6
list.insertToIndex(3, 7); // 3 2 1 7 4 5 6
list.printList(); // 3 2 1 7 4 5 6
list.removeByIndex(3); // 3 2 1 4 5 6
list.printList();
}
}