java实现单链表(最清晰)

本文主要内容:
1、单链表理解的最痛点
2、单链表编写的最痛点
3、单链表实现代码

1、单链表理解的最痛点

所谓的临时节点,又有人称为可以移动的节点。其实这些名字都不形象也不利于让别人理解。
我们以在链表中添加元素为例子来理解,如下代码:(并不是真正链表,用来实验)
方案一:

class Solution {
    public static void main(String[] args) {
        Node node= new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        node.next = node2;
        node2.next = node3;
        node3.next = node4;

        //在node的尾部增加一个值为10的节点
        //方案一:
        while (node.next != null) {
            node = node.next;
        }
        node.next = new Node(10);
    }
}
class Node<T> {
    Node next;
    T t;

    Node(T t) {
        this.t = t;
    }
}     

方案一是没有办法实现在node的尾部增加一个值为10的节点这个要求的,方案一代码运行结果:
这里写图片描述
我们看到node不但没有实现新增,最终node变成了只有2个节点了。

这就是没有用临时节点temp的原因(我不喜欢临时节点这个名字,暂且这么叫它),在方案一中,一开始node有4个节点:node,node2,node3,node4,每一个节点都是一个Node的对象。我们知道对象是引用,即 Node node= new Node(1); node是一个变量名,引用类型的变量名是存储数据值的内存位置的名字,在这里变量名和内存位置进行映射,内存位置和数据值映射。当while循环里进行node = node.next;这个代码时,第一次循环是把node2变量名映射的内存位置赋值给node变量名映射的内存位置,通俗一点就是把node2赋给node,以此类推,当跳出while循环的时候,只有一个node4,因为node4.next =null

方案二:

class Solution {
    public static void main(String[] args) {
        Node node= new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        node.next = node2;
        node2.next = node3;
        node3.next = node4;

        Node temp = node;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = new Node(10);
    }
}
class Node<T> {
    Node next;
    T t;

    Node(T t) {
        this.t = t;
    }
}

方案二解决了上述的问题,用一个临时节点Node temp = node;,在未进入while循环时,temp和node变量名都会映射到node的内存位置,每一个节点都有自己的引用地址(和内存位置是一个概念,这里用引用地址更通俗),即node,node2,node3,node4,temp都有自己的引用地址,这些地址指向了数据值存储地址的首地址。我们用while在循环temp时,即temp的引用地址从和node的引用地址相同,到和node2的引用地址相同,最终循环到和node4的引用地址相同,且修改了node4next。这个过程不会影响到node,因为node指向的地址的值并没有被改变,改变的是node4的。
这里写图片描述

2、单链表编写的最痛点

我们运行一下代码一和代码二,请对比一下结果,代码的逻辑似乎没有问题,结果却不一样。请思考一下原因。

代码一:

class Solution {
    public static void main(String[] args) {
        Node node= new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        node.next = node2;
        node2.next = node3;
        node3.next = node4;

        Node temp = node;
        //不同之处
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = new Node(10);
        System.out.println(node.next.next.next.next.val);
    }
}
class Node<T> {
    Node next;
    T t;

    Node(T t) {
        this.t = t;
    }
}

代码二:

class Solution {
    public static void main(String[] args) {
        Node node= new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        node.next = node2;
        node2.next = node3;
        node3.next = node4;

        Node temp = node;
        //不同之处
        while (temp != null) {
            temp = temp.next;
        }
        temp = new Node(10);
        System.out.println(node.next.next.next.next.val);
    }
}
class Node<T> {
    Node next;
    T t;

    Node(T t) {
        this.t = t;
    }
}

3、单链表的实现代码

/**
 * 〈一句话功能简述〉<br> 
 * 〈功能详细描述〉
 *
 * @author wangzha
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@SuppressWarnings("ALL")
public class LinkedList<T> {
    //头结点,头结点是为了方便在下面的方法中遍历链表用的
    public Node head = new Node(null);
    public int size;

    public LinkedList() {

    }

    /**
     * 获取元素值
     *
     * @param i
     * @return
     */
    public T get(int i) {
        if (i < 0 || i > size - 1) {
            throw new ArrayIndexOutOfBoundsException("获取的位置不合法");
        } else {
            //把第一个节点给临时节点temp,让temp遍历
            Node temp = head;
            //counter用来计数,找到i在链表里的节点位置,头结点不算链表的真实节点,所以从-1开始计数
            int counter = -1;
            while (temp != null) {
                if (counter == i) {
                    return (T) temp.t;
                }
                temp = temp.next;
                counter++;
            }
        }
        return null;
    }

    /**
     * 添加元素
     *
     * @param t
     */
    public void add(T t) {
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = new Node(t);
        size++;
    }

    /**
     * 指定位置添加元素
     *
     * @param i
     * @param t
     */
    public void add(int i, T t) {
        if (i < 0 || i > size) {
            throw new ArrayIndexOutOfBoundsException("插入的位置不合法");
        } else {
            Node temp = head;
            int counter = -1;
            while (temp != null) {
                if ((i - 1) == counter) {
                    //将i前面的节点指向node,node的指向i节点
                    Node node = new Node(t);
                    Node back = temp.next;
                    temp.next = node;
                    node.next = back;
                    size++;
                }
                temp = temp.next;
                counter++;
            }
        }
    }


    /**
     * @param i
     */
    public void delete(int i) {
        if (i < 0 || i > size) {
            throw new ArrayIndexOutOfBoundsException("删除的位置不合法");
        } else {
            Node temp = head;
            int counter = -1;
            while (temp != null) {
                //将i前面的节点指向i后面的节点
                if ((i - 1) == counter) {
                    temp.next = temp.next.next;
                    size--;
                }
                counter++;
                temp = temp.next;
            }
        }
    }

    private class Node<T> {
        Node next;
        T t;

        Node(T t) {
            this.t = t;
        }
    }
}
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值