链表List加LinkedList

#学习记录

##数据结构

##链表

链表的引入:

对于顺序表而言,其存在许多缺陷,比如删除某个数据,他的时间复杂度可能会很高达到O(N),比如删除第一个数据,扩容也可能造成空间的浪费。这时候就体现出多个数据结构的好处,可以在特定业务下调用合适的数据结构,为了弥补顺序表的缺陷,引入链表

链表的概念:

对于下图中一个个物理意义上非连接的节点共同就组成了一个链表(大致)

链表的分类:

1带头与不带头

2循环与非循环

3单向与双向

那么链表共有8种类型(排列组合 2*2*2=8),只需要特别掌握划红线的即可,其他的都可以类推。

什么是带头,什么是头呢?

什么是循环?

什么是双向?

像这种就是单向:

像这种就是双向:

单向不带头非循环链表的实现

public class MySingleList implements IList {//无头单向非循环链表

    static class ListNode{//节点类--
        int val;
        ListNode next;

        public ListNode(int val) {//由于一个节点的后一个节点是变动的,不确定的,所以在这里next先不写进构造方法
            this.val = val;//但是要存储的值是一定的
        }
    }
    public ListNode head;//需要一个标志头】head默认为null



    @Override
    public void addFirst(int data) {//头插

        ListNode listNode=new ListNode(data);
        if (head==null){
            this.head=listNode;

        }else {
        listNode.next=head;
        head=listNode;}

    }
    




    @Override
    public void addLast(int data) {
        ListNode node=new ListNode(data);
        if (head==null){
            this.head=node;

        }
        ListNode cur=head;

        while (cur.next !=null){//while的循环条件特别巧妙 也是关键
            cur=cur.next;
        }
        cur.next=node;

    }

    @Override
    public void addIndex(int index, int data) {
        ListNode node=new ListNode(data);
        if (head==null||index<0||index>size()){
            return;
        }
        //注意到首尾位置处要特别注意要调用头插和尾差来实现特定位置插入
        if (index==size()){
            addLast(data);
            return;
        }
        if (index==0){
            addFirst(data);
            return;
        }
        ListNode cur=head;
        int count=index-1;
        while (count!=0){
            cur=cur.next;
            count--;
        }
        node.next=cur.next;
        cur.next=node;
    }

    @Override
    public boolean contains(int key) {
        ListNode cur=head;
        while (cur != null){
        if(cur.val==key){
            return true;

        }
            cur=cur.next;//移动到下一个节点
        }
    return false;

    }

    @Override
    public void remove(int key) {
        if (head==null){
            return;
        }
        if (head.val==key){
            head=head.next;
            return;
        }

        ListNode cur=findNodeOfKey(key);
        ListNode del=cur.next;
        cur.next=del.next;
    }
    //我们可以写一个方法来找要删除的内容所在的位置的前一个位置
    public  ListNode findNodeOfKey(int key){
        ListNode cur=head;
        while (cur!=null){
            if (cur.next.val==key){
                return cur;
            }
            cur=cur.next;
        }

         return null;

    }

    @Override
    public void removeAllKey(int key) {
        if (head==null){
            return;
        }
        ListNode cur=head.next;
        ListNode pre=head;

        while (cur!=null){
            if (cur.val==key){
                pre.next=cur.next;
                //此时不用移动pre
            }else{
                pre=pre.next;
                //如果该节点不是要删除的节点,那么就需要移动pre
            }
            cur=cur.next;

        }
        //回头来看首位置是否也存在要删的对象,因为在上面代码的方法无法删除欲删的首节点
        if (head.val==key){
            head=head.next;
            return;
        }

    }

    @Override
    public int size() {//节点个数
        ListNode cur=head;
        int count=0;
        while (cur!=null){
            cur=cur.next;
            count++;
        }



        return count;
    }

    @Override
    public void clear() {
        ListNode cur=head;
        while (cur!=null){
         ListNode code=cur.next;//做一份缓存
         cur.next=null;
         cur=code;}
        //head=nll;


    }

    @Override
    public void display() {

        ListNode cur=head;
        while (cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }

    }

    @Override
    public void creatHoop(ListNode head) {
        //这个是生成回文链表
    }

    @Override
    public boolean hasCycle(ListNode head) {
        //判断是否有环,这个两个在此不实现
        return false;
    }

    public  void CreatList(){//在该方法里面会帮助生成许多的节点
        //实例节点出来
        ListNode listNode0=new ListNode(11);
        ListNode listNode1=new ListNode(12);
        ListNode listNode2=new ListNode(13);
        ListNode listNode3=new ListNode(14);
        ListNode listNode4=new ListNode(15);
        //将每个实例出来的节点连接起来
        listNode0.next=listNode1;//next存放下一个节点的地址就可以将每个单独节点连接起来
        listNode1.next=listNode2;
        listNode2.next=listNode3;
        listNode3.next=listNode4;
        this.head=listNode0;
    }
   
   

}

认识LinkedList

我们前文讲述的单非循环不带头列表其实不是ieadl提供的链表,其只是链表当中的一种情况,真正的常用链表LinkedList是不带头非循环双向链表,双向链表能够很好的体现出它的优点,不需要像单向链表那样去找尾巴,可以很好的降低时间复杂度,而且也具备单链表的功能,。更能体现出对于不同的业务,采取高效的数据结构这一特征。

注意:

1 LinkedList实现了List接口,重写了List当中许多的方法;

2 LinkedList底层使用了双向链表;

3 LinkedList没有实现RandomAccess接口,因此不支持随机访问;

4 LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)

5 LinkedList比较适合任意位置插入的场景

模拟实现LinkedLIst

我们有了单链表模拟实现的基础,那么来实现LinkedList也就容易了.LinkedList也实现了List接口。

public class MyLinkedList implements IList {

    class ListNode{
    public int val;
    public ListNode pre;
    public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }
    //定义头指针和指针两个引用变量;
    public ListNode head;
    public ListNode last;


    @Override
    //头插
    public void addFirst(int data) {
        ListNode node=new ListNode(data);
       if(head==null){
           last=head=node;
           return;
       }
       head.pre=node;
       node.next=head;
       head=node;

    }

    @Override
            //尾差
    public void addLast(int data) {
        ListNode node=new ListNode(data);
        if (head==null){
            head=last=node;
        }
        last.next=node;
        node.pre=last;
        last=node;
    }

    @Override
    //指定位置插入
    public void addIndex(int index, int data) {
        int len=size();
        if (head==null||index<0||index>len){
            return;
        }
        //注意到首尾位置处要特别注意要调用头插和尾差来实现特定位置插入
        if (index==len){
            addLast(data);
            return;
        }
        if (index==0){
            addFirst(data);
            return;
        }
        ListNode cur=findIndex(index);
        ListNode node=new ListNode(data);
        cur.pre.next=node;
        node.pre=cur.pre;
        cur.pre=node;
        node.next=cur;
    }
    //查找对应下标的节点的方法,这个不是重写的,这个方法是在该类中使用的,为该类的模拟实现过程提供便利;
    private  ListNode findIndex(int index){
        ListNode cur=head;
        while (index!=0){
            cur=cur.next;
            index--;
        }
        return cur;
    }

    @Override
    //判断是否含有某个数据key
    public boolean contains(int key) {
        ListNode cur=head;
        while (cur!=null){
            if (cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }

    @Override
    public void remove(int key) {
        ListNode cur = head;

        while (cur != null) {
            //开始删除
            if (cur.val == key) {
                if (cur == head) {
                    head = head.next;
                    head.pre = null;
                } else {
                    cur.pre.next = cur.next;
                    if (cur.next == null) {
                        last = last.pre;
                        return;
                   }
                    cur.next.pre=cur.pre;
                    return;
                }

            }
            cur = cur.next;
        }
    }

    @Override
    public void removeAllKey(int key) {
        if (head==null){
            return;
        }
        ListNode cur=head;
        while (cur!=null){

            //开始删除
            if (cur.val==key){
                if (cur!=head&&cur!=last){
                    cur.pre.next=cur.next;
                    cur.next.pre=cur.pre;
                }else if (cur==head){
                    head=head.next;
                    head.pre=null;
                }else {
                    last=last.pre;
                    last.next=null;
                }
            }
            cur=cur.next;
        }


    }

    @Override
    public int size() {
        int count=0;
        ListNode cur=head;
        while (cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }

    @Override
    public void clear() {
        ListNode cur=head;
        while (cur!=null){
            //这里要注意备份一份下节点,因为我们已经将本节点的next赋值为null了,我们无法通过cur.next来移动到下一个节点了
            ListNode curN=cur.next;
            cur.next=null;
            cur.pre=null;
            cur=curN;
        }
        head=last=null;

    }

    @Override
    public void display() {
        ListNode cur=head;
        while (cur!=null){
            System.out.print(cur.val+" ");
            cur=cur.next;
        }
        System.out.println();
    }
//生成一个回文链表
    public MyLinkedList creatCycycle(){
        MyLinkedList myLinkedList1=new MyLinkedList();
        myLinkedList1.addFirst(1);
        myLinkedList1.addFirst(2);
        myLinkedList1.addFirst(3);
        myLinkedList1.addFirst(4);
        myLinkedList1.addFirst(5);
        myLinkedList1.addFirst(6);
        int count=2;
        ListNode cur=myLinkedList1.head;
        while (count!=0){
            cur=cur.next;
            count--;
        }
        myLinkedList1.last.next=cur;
        return myLinkedList1;
    }

}

LinkedList的构造方法

有两种

一种一不带参数的,另一种是带参数,构造链表的时候实参部分可以写一个集合容器比如list

然后就会将list这个集合容器内的数据全部插入到此时我们新构造的这个链表内

LinkedList链表当中的方法

和顺序表单链表一样,有许多的方便的方法可以使用

ArrayList和LinkedList的区别

注意:

1   ArrayList底层是数组,数组是连起来的,它在存储空间上是连续的,物理意义上也连续,但是LinkedList的底层是一个个节点,我们每new一个节点的时候分配给这个节点对象的地址是不一样的,也不一定是连续的,是随机的,只有当地址是连续的,物理意义上才是连续的,每个节点之间是通过引用来联系起来的,通过next当中的地址访问到另外的节点,所以逻辑意义上是连续的,但是每个节点地址不一样,是分隔的,物理意义上不连续。

2  顺序表底层是数组,数组我们可以通过下标随意的去访问该容器内的数据,但是对于链表而言就不是,你可能会说链表当中不是有get(int x)方法吗?能够返回链表中指定位置的数据吗?但是我们看源码就知道了,其实要移动到指定的位置是需要while循环来实现的,有while循环那么时间复杂度就O(N)了

3合理的使用顺序表和链表,不同的情况使用不同的数据结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值