链表(超详解)

1.链表的原理

在这里插入图片描述
结点(node): 为了组织链表而引入的一个结构,除了保存我们的元素之外,还会保存指向下一个结点的引用。

1.1代码表示


class Node{
    int var; //表示存储的值
    Node next; //下一个元素节点的引用
}

1.2链表节点的定义与遍历


//节点类的定义
class Node{
    int var;
    Node next;
    public Node(int var) {
        this.var = var;
    }
    @Override
    public String toString() {
        return "Node{" +
                "var=" + var +
                '}';
    }
}
public class NodeTest {
    //创建一个链表
    public static Node CreatNodeList(){
        Node a=new Node(1);
        Node b=new Node(2);
        Node c=new Node(3);
        a.next=b;
        b.next=c;
        c.next=null;
        return a;
    }

    public static void main(String[] args) {
        Node head = NodeTest.CreatNodeList();
        //遍历链表
        while (head!=null){
            System.out.println(head);
            head=head.next; //更新引用的位置
        }
    }
}

运行结果:
在这里插入图片描述
过程图:
在这里插入图片描述

2.java中的链表(LinkedList)

java中的链表不是使用头节点来表示整个链表;而是定义了一个链表类。


public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;

    /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }

3.实现自己的(LinkedList)

这里模拟Java实现的一个泛型版本的LinkedList。


class Node<T>{
    T var;
    Node<T> next;
    Node<T> pre;
    public Node(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return "Node{" +
                "var=" + var +
                '}';
    }
}

class MyExciption extends RuntimeException{
    public MyExciption(String msg) {
        super(msg);
    }
}


public class MyLinkedList<T> {
    private Node<T> head;
    private Node<T> Tail;
    private int lenght;

    public MyLinkedList() {
        head=null;
        Tail=null;
        lenght=0;
    }
    //获取链表长度的方法
    public int getLenght(){
        return this.lenght;
    }
    //头插
    public void addHead(T var){
        Node<T> newNode=new Node<>(var);
        //如果是空链表的情况
        if(head==null){
            head=newNode;
            Tail=newNode;
            lenght++;
            return;
        }
        //不是空链表的情况
        newNode.next=head;
        head.pre=newNode;
        head=newNode; //更新头节点引用的位置
        lenght++;
        return;
    }
    public void addLast(T var){
        Node<T> newNode=new Node<>(var);
        //如果是空链表
        if(head==null){
            head=newNode;
            Tail=newNode;
            lenght++;
            return;
        }
        //不是空链表的情况
        Tail.next=newNode;
        newNode.pre=Tail;
        Tail=newNode; //更新尾节点引用的位置
        lenght++;
        return;
        }
        //在任意位置插入
    public  void add(T var,int index){
        //先判断位置是否非法
        if(index<0 || index >lenght){
            throw new MyExciption("插入位置非法");
        }
        if(index==0){  //相当于头插
            addHead(var);
            return;
        }
        if(index==lenght){ //相当于尾插
            addLast(var);
            return;
        }
        // 1  2  3

        Node<T> newNode=new Node<>(var);
        Node<T> curNode=getIndex(index);
        Node<T> preNode=curNode.pre;
        preNode.next=newNode;
        newNode.pre=preNode;

        curNode.pre=newNode;
        newNode.next=curNode;
        lenght++;

        return;


    }
    //获取指定位置的节点
    public <T> Node<T> getIndex(int index){
        if(index<0 || index >=lenght){
            throw new MyExciption("位置非法");
        }
        Node<T> cur= (Node<T>) head;
        for(int i=0;i<index;i++){
            cur=cur.next;

        }
        return cur; //把节点返回
    }
    //头删
    public  void removeFirst(){
        if(head==null){ //空链表
            return;
        }
        if(head.next==null){
            head=null;
            Tail=null;
            lenght=0;
            return;
        }
        // 1  2  3
        Node<T> newcur=head.next;
        newcur.pre=null;
        head=newcur;
        lenght--;
        return;

    }
    //尾部删除
    public void removeLast(){
        if(head==null){ //如果是空链表
            return;
        }
        if(head.next==null){  //只有一个节点的情况
            head=null;
            Tail=null;
            lenght=0;
            return;
        }
        // 1  2  3
        Node<T> CurNode=Tail.pre;
        CurNode.next=null;
        Tail=CurNode;
        lenght--;
        return;

    }
    //按位置删除
    public void removeByIndex(int index){
        if(index<0 || index >=lenght){
            throw new MyExciption("位置非法!!!");
        }
        if(index==0){   //这就相当于删除头节点
            removeFirst();
            return;
        }
        if(index==lenght-1){ //这就相当于删除尾节点
            removeLast();
            return;
        }
        //中间其他位置  1  2  3  4  5
        Node<T> todelete=getIndex(index);
        Node<T> preNode=todelete.pre;
        Node<T> nextNode=todelete.next;
        preNode.next=nextNode;
        nextNode.pre=preNode;
        lenght--;
        return;

    }
    //按值删除节点 !!!
    public void removeByValue(T val){
        int index=indexOf(val);
        if(index==-1){
            throw new MyExciption("没有该值");
        }
        removeByIndex(index); //调用按位置删除的方法
    }

    //查找
    public T get(int index){
        if(index<0 ||index >=lenght){
            throw new MyExciption("位置非法!");
        }
        return (T) getIndex(index).var;
    }
    //修改链表节点的值
    public void set(int index,T val){
        if(index<0 || index>=lenght){
            throw new MyExciption("位置非法!");
        }
        Node<T> node=getIndex(index);
        node.var=val;
    }
    //根据值找下标的位置
    public int indexOf(T val) {
       Node<T> cur=head;
        for(int i=0;i<lenght;i++){
            if (cur.var==val){
                return i;
            }
            cur=cur.next;
        }
        return -1;
    }


    public static void main(String[] args) {
        //创建一个实例对象
        MyLinkedList<Integer> list=new MyLinkedList<>();
        System.out.println("初始情况下链表的长度:"+list.lenght);
        //测试添加元素
       list.addHead(1);
       list.addLast(6);
       list.add(3,2);


//        list.removeFirst();
//        list.removeLast();
//        list.removeFirst();

        list.removeByIndex(2);
        System.out.println(list.getIndex(1).var);
        System.out.println(list.getIndex(0).var);
        System.out.println(list.lenght);
        System.out.println(list.get(1));
        System.out.println(list.getIndex(1));
        list.set(1,8888);//修改元素的值
        System.out.println(list.getIndex(1));
        list.removeByIndex(1);
        System.out.println(list.getIndex(0).var);
        System.out.println(list.lenght);
        System.out.println("=============");
        MyLinkedList<String> list1=new MyLinkedList<>();
        list1.add("C++",0);
        list1.addHead("java");
        list1.addHead("python");
       for(int i=0;i<list1.lenght;i++){
           System.out.println(list1.get(i));
       }

    }

}

测试运行:
在这里插入图片描述

4.顺序表VS链表

4.1顺序表

空间连续支持随机访问;但是从中间或者头部插入元素,时间复杂度为O(N),而且扩容的开销很大。

4.2链表

空间不一定是连续的,没有扩容问题,头删,尾删的时间复杂度为O(1),但是中间插入的时间复杂度也是O(N)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值