链表分为单向链表和双向链表,本文分享Java如何构建单向链表。

单向链表数据结构

单向链表的每一个节点都有两块区域,一个区域放着当前节点的值,另一个区域指向下一个节点,数据结构如下

按照此图示,代码实例展示了一个只有初步构建功能的单向链表

基础构建代码

/**
 * 单向链表构建
 * @param <T>
 */
public class Linked<T> {

    /**
     * 链表节点的构建
     */
    public class Node{
        private Node next;
        private T t;

        public Node(T t, Node next){
            this.t = t;
            this.next = next;
        }

        public Node(T t){
            this(t, null);
        }

        public Node(){

        }

        /**
         * 获取当前节点的值
         * @return
         */
        public T get(){
            return t;
        }
      
      	/**
         * 修改当前节点的值
         */
        public void set(T t){
            this.t = t;
        }
    }

    private Node head;
    private int size;

    /**
     * 默认链表构造函数
     */
    public Linked(){
        this.head = null;
        this.size = 0;
    }
}

链表节点增加

链表的增加要利用next转变指向,即在增加的位置,增加节点的前一个节点的next指向增加节点,增加节点的next指向增加节点后一个节点,这样就完成了指向的转变,实现链表的增加

示例代码

/**
 * 增加一个头节点
 * @param t
 */
public void addFirst(T t){
    Node node = new Node(t);
    node.next = this.head;
    this.head = node;
    this.size++;
}

/**
 * 增加一个尾节点
 * @param t
 */
public void addLast(T t){
    add(t, this.size);
}

/**
 * 根据index增加一个节点,如index=1,即增加第二个节点为...
 * @param t
 * @param index
 */
public void add(T t, int index){
    if(index < 0 || index > this.size){
        throw new IllegalArgumentException("index is error");
    }

    if(index == 0){
        addFirst(t);
        return;
    }

    Node prevNode = this.head;
    for(int i=0; i<index-1; i++){
        prevNode = prevNode.next;
    }

    Node newNode = new Node(t);
    newNode.next = prevNode.next;
    prevNode.next = newNode;

    this.size++;
}

链表节点删除

单向链表的删除与增加正好相反,被删除节点的前一个节点的next,指向被删除节点的后一个节点即可完成删除操作

示例代码

/**
 * 删除头节点
 */
public void deleteFirstNode(){
    if(this.head == null){
        System.out.println("没有元素可以删除");
    }
    this.head = head.next;
    this.size--;
}

/**
 * 删除尾节点
 */
public void deleteLastNode(){
    if(this.head == null){
        System.out.println("没有元素可以删除");
    }
    Node temp = this.head;
    while(temp.next != null){
        temp = temp.next;
    }

    temp.next = null;
    this.size--;
}

/**
 * 根据index删除节点
 * @param index
 */
public void delete(int index){
    if(index < 0 || index > this.size){
        throw new IllegalArgumentException("index超限或小于0");
    }

    if(index == 0){
        this.deleteFirstNode();
        return;
    }else if(index == this.size-1){
        this.deleteLastNode();
        return;
    }else{
        Node prev = this.head;
        Node temp = this.head;

        for(int i=0; i<index; i++){
            prev = temp;
            temp = temp.next;
        }

        prev.next = temp.next;
        this.size--;
    }
}

已知节点的情况下,删除本节点

这是剑指Offer的一个链表题目,要求在已知节点是什么的情况下,删除本节点,要求时间复杂度为O(1);在未知节点,或者只知道index的情况下,我们肯定要从头开始循环找到相应的节点,然后操作被删除节点的前一个节点的next来完成删除操作,但在已知节点的情况下,可以换个思路

在已知节点的情况,我们也就能知道已知节点的下一个节点是什么,那么我们可以让已知节点的值等于下一个节点的值,已知节点的next等于下一个节点的next,这样就相当于跳过了已知节点,实现删除操作

实例代码

/**
 * 给定一个节点,按时间复杂度O(1)删除本节点
 * @param node
 */
public void deleteByNode(Node node){

    if(node.next == null){
        Node temp = this.head;
        while(temp.next != node){
            temp = temp.next;
        }
        temp.next = null;
    }else if(node == head){
        this.head = head.next;
    }else{
        node.t = node.next.t;
        node.next = node.next.next;
    }

    this.size--;
}

链表节点修改

链表节点的修改这里给出的是修改值,简单的使用构造函数就可以了,如果是要修改节点的next,那实际上和删除没什么区别,这里不再给出实例代码

示例代码

/**
 * 根据index修改节点的值
 * @param t
 * @param index
 */
public void update(T t, int index){
    if(index < 0 || index > this.size){
        throw new IllegalArgumentException("index超限或小于0");
    }

    if(index == 0){
        this.updateFirstNode(t);
        return;
    }else if(index == this.size-1){
        this.updateLastNode(t);
        return;
    }else{
        Node temp = this.head;

        for(int i=0; i<index; i++){
            temp = temp.next;
        }

        //temp.set(t); 这个也可以
        temp.t = t;
    }
}

/**
 * 修改头结点的值
 * @param t
 */
public void updateFirstNode(T t){
    //this.head.set(t); 这个也可以
    this.head.t = t;
}

/**
 * 修改尾节点的值
 * @param t
 */
public void updateLastNode(T t){
    Node temp = this.head;

    for(int i=0; i<this.size-1; i++){
        temp = temp.next;
    }

    //temp.set(t); 这个也可以
    temp.t = t;
}

因为Node的方法中暴露的一个set,且为public,所以在使用这个链表的时候,也可以获得节点后通过set方法修改节点的值

实例代码

public static void main(String[] args){
    Linked<String> linked = new Linked<>();

    linked.addFirst("no1");
    linked.addLast("no2");
    linked.addLast("no3");
    linked.addLast("no4");
    linked.addLast("no5");
    linked.addLast("no6");
    linked.addLast("no7");
    linked.addLast("no8");

    //可以将 no4 修改为 all
    linked.getNode(3).set("all");
}

链表节点查看

链表节点的查询这里给出的方法是获取节点,和update的方法类似,将最后的return temp改成return temp.t即可返回节点的值,在这里给出的实例代码需要再使用时使用Node类暴露的get()方法

示例代码

/**
 * 根据index获取节点
 * @param index
 * @return
 */
public Node getNode(int index){
    if(index < 0 || index > this.size){
        throw new IllegalArgumentException("index超限或小于0");
    }

    if(index == 0){
        return this.getFirstNode();
    }else if(index == this.size-1){
        return this.getLastNode();
    }else{
        Node temp = this.head;

        for(int i=0; i<index; i++){
            temp = temp.next;
        }

        return temp;
    }
}

/**
 * 获取尾节点
 * @return
 */
public Node getLastNode(){
    Node temp = this.head;

    for(int i=0; i<this.size-1; i++){
        temp = temp.next;
    }

    return temp;
}

/**
 * 获取头节点
 * @return
 */
public Node getFirstNode(){
    return this.head;
}

获取链表长度

链表类里面维护了一个size,在delete操作时会减1,add操作时会加1,所以可以直接返回这个size

示例代码

/**
 * 获取链表长度
 * @return
 */
public int getSize(){
    return this.size;
}

整体Linked类示例代码

/**
 * 单向链表构建
 * @param <T>
 */
public class Linked<T> {

    /**
     * 链表节点的构建
     */
    public class Node{
        private Node next;
        private T t;

        public Node(T t, Node next){
            this.t = t;
            this.next = next;
        }

        public Node(T t){
            this(t, null);
        }

        public Node(){

        }

        /**
         * 获取当前节点的值
         * @return
         */
        public T get(){
            return t;
        }

        /**
         * 修改当前节点的值
         */
        public void set(T t){
            this.t = t;
        }
    }

    private Node head;
    private int size;

    /**
     * 默认链表构造函数
     */
    public Linked(){
        this.head = null;
        this.size = 0;
    }

    /**
     * 增加一个头节点
     * @param t
     */
    public void addFirst(T t){
        Node node = new Node(t);
        node.next = this.head;
        this.head = node;
        this.size++;
    }

    /**
     * 增加一个尾节点
     * @param t
     */
    public void addLast(T t){
        add(t, this.size);
    }

    /**
     * 根据index增加一个节点
     * @param t
     * @param index
     */
    public void add(T t, int index){
        if(index < 0 || index > this.size){
            throw new IllegalArgumentException("index is error");
        }

        if(index == 0){
            addFirst(t);
            return;
        }

        Node prevNode = this.head;
        for(int i=0; i<index-1; i++){
            prevNode = prevNode.next;
        }

        Node newNode = new Node(t);
        newNode.next = prevNode.next;
        prevNode.next = newNode;

        this.size++;
    }

    /**
     * 给定一个节点,按时间复杂度O(1)删除本节点
     * @param node
     */
    public void deleteByNode(Node node){

        if(node.next == null){
            Node temp = this.head;
            while(temp.next != node){
                temp = temp.next;
            }
            temp.next = null;
        }else if(node == head){
            this.head = head.next;
        }else{
            node.t = node.next.t;
            node.next = node.next.next;
        }

        this.size--;
    }

    /**
     * 删除头节点
     */
    public void deleteFirstNode(){
        if(this.head == null){
            System.out.println("没有元素可以删除");
        }
        this.head = head.next;
        this.size--;
    }

    /**
     * 删除尾节点
     */
    public void deleteLastNode(){
        if(this.head == null){
            System.out.println("没有元素可以删除");
        }
        Node temp = this.head;
        Node newTemp = temp;
        while(temp.next != null){
            newTemp = temp;
            temp = temp.next;
        }

        newTemp.next = null;
        this.size--;
    }

    /**
     * 根据index删除节点
     * @param index
     */
    public void delete(int index){
        if(index < 0 || index > this.size){
            throw new IllegalArgumentException("index超限或小于0");
        }

        if(index == 0){
            this.deleteFirstNode();
            return;
        }else if(index == this.size-1){
            this.deleteLastNode();
            return;
        }else{
            Node prev = this.head;
            Node temp = this.head;

            for(int i=0; i<index; i++){
                prev = temp;
                temp = temp.next;
            }

            prev.next = temp.next;
            this.size--;
        }
    }

    /**
     * 根据index修改节点的值
     * @param t
     * @param index
     */
    public void update(T t, int index){
        if(index < 0 || index > this.size){
            throw new IllegalArgumentException("index超限或小于0");
        }

        if(index == 0){
            this.updateFirstNode(t);
            return;
        }else if(index == this.size-1){
            this.updateLastNode(t);
            return;
        }else{
            Node temp = this.head;

            for(int i=0; i<index; i++){
                temp = temp.next;
            }

            //temp.set(t); 这个也可以
            temp.t = t;
        }
    }

    /**
     * 修改头结点的值
     * @param t
     */
    public void updateFirstNode(T t){

        //this.head.set(t); 这个也可以
        this.head.t = t;
    }

    /**
     * 修改尾节点的值
     * @param t
     */
    public void updateLastNode(T t){
        Node temp = this.head;

        for(int i=0; i<this.size-1; i++){
            temp = temp.next;
        }

        //temp.set(t); 这个也可以
        temp.t = t;
    }

    /**
     * 根据index获取节点
     * @param index
     * @return
     */
    public Node getNode(int index){
        if(index < 0 || index > this.size){
            throw new IllegalArgumentException("index超限或小于0");
        }

        if(index == 0){
            return this.getFirstNode();
        }else if(index == this.size-1){
            return this.getLastNode();
        }else{
            Node temp = this.head;

            for(int i=0; i<index; i++){
                temp = temp.next;
            }

            return temp;
        }
    }

    /**
     * 获取尾节点
     * @return
     */
    public Node getLastNode(){
        Node temp = this.head;

        for(int i=0; i<this.size-1; i++){
            temp = temp.next;
        }

        return temp;
    }

    /**
     * 获取头节点
     * @return
     */
    public Node getFirstNode(){
        return this.head;
    }

    /**
     * 获取链表长度
     * @return
     */
    public int getSize(){
        return this.size;
    }

}