链表相关知识

92 阅读2分钟

一、单向链表

建立一个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();
    }
}