LinkedList与链表

目录

一、链表的概念结构

二、单向无头非循环链表的实现

三、双向无头非循环链表的实现 

四、LinedList的遍历

1、for循环遍历

2、for each遍历

3、使用迭代器正向遍历

4、使用迭代器反向遍历

五、ArrayList和LinedList的区别

1、存储空间

2、随机访问

3、从头插入元素

4、应用场景


一、链表的概念结构

链表:是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。

如上图,每一个节点都会存储下一个节点的地址,那么就可以通过当前节点访问下一个节点,这些节点在逻辑上是连续的,但是在物理上不一定连续,因为每次申请的空间可能连续,可能不连续。 

实际中,链表的结构非常多样:

1、单向或双向:

  2、带头或不带头:

 3、循环或非循环:

这些情况组合起来就有8种链表结构:

我们常用的是单向不带头非循环链表和双向不带头链表。

二、单向无头非循环链表的实现

public class SingleList {
    private int usedSize;

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

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;//表示存储当前链表的头节点的引用

    public void disPlay() {
        ListNode ret = this.head;
        while (ret != null) {
            System.out.print(ret.val + " ");
            ret = ret.next;
        }
    }
    //查看链表中是否包含key
    public boolean contains(int key) {
        ListNode ret = this.head;
        while (ret != null) {
            if (ret.val == key) {
                return true;
            }
            ret = ret.next;
        }
        return false;
    }

    public int size() {
        /*Node ret = this.head;
        int count = 0;
        while (ret != null){
            count++;
            ret = ret.next;
        }
        return count;*/
        return this.usedSize;
    }

    //头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        node.next = this.head;
        this.head = node;
        this.usedSize++;
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        ListNode ret = this.head;
        if (head == null) {
            head = node;
        } else {
            while (ret.next != null) {
                ret = ret.next;
            }
            ret.next = node;
        }
        usedSize++;
    }

    private void checkIndex(int index) {
        if (index < 0 || index > usedSize) {
            throw new IndexNotLegalException("index位置不合法");
        }
    }

    //任意位置插入
    public void addIndex(int index, int data) {
        checkIndex(index);
        if (index == 0) {
            addFirst(data);
        }
        if (index == usedSize) {
            addLast(data);
        }
        ListNode node = new ListNode(data);
        ListNode ret = head;
        for (int i = 0; i < index - 1; i++) {
            ret = ret.next;
        }
        node.next = ret.next;
        ret.next = node;
        usedSize++;
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if (head == null) {
            return;
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        ListNode cur = searchPrevOfKey(key);
        if (cur == null) {
            return;
        }
        ListNode del = cur.next;
        cur.next = del.next;
    }

    /**
     * 找到关键字key的前驱
     *
     * @param key
     * @return
     */
    private ListNode searchPrevOfKey(int key) {
        if (head == null) {
            return null;
        }
        ListNode cur = head;
        while (cur.next != null) {
            if (cur.next.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    //删除所有值为key的节点
    public void removeAllKey(int key) {
        if (head == null) {
            return;
        }
        ListNode prev = head;
        ListNode cur = prev.next;
        while (cur != null) {
            if (cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
                usedSize--;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        if (head.val == key) {
            head = head.next;
            usedSize--;
        }
    }
    //清空链表
    public void clear() {
        //this.head = null;
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        this.head = null;
    }
}

三、双向无头非循环链表的实现 

public class MyLinkedList {
    //节点
    public class ListNode{
        public int val;
        public ListNode next;
        public ListNode prev;//记录当前节点的前驱
        public ListNode(int data){
            this.val = data;
        }
    }
    public ListNode head;
    public ListNode last;
    //头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        if(this.head == null){
            this.head = node;
            this.last = node;
        }else {
            this.head.prev = node;
            node.next = head;
            head = node;
        }
    }
    //尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if(this.head == null){
            this.head = node;
            this.last = node;
        }else {
            this.last.next = node;
            node.prev = this.last;
            this.last = node;
        }
    }
    private void checkIndex(int index){
        if(index<0 || index>size()){
            throw new RuntimeException("index坐标不合法");
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        checkIndex(index);
        if(index == 0 || this.head == null){
            addFirst(data);
            return;
        }
        if(index == size()){
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        for (int i = 1; i < index; i++) {
            cur = cur.next;
        }
        node.next = cur.next;
        node.prev = cur;
        cur.next.prev = node;
        cur.next = node;
    }
    //查找是否包含关键字key是否在双链表当中
    public boolean contains(int key){
        ListNode cur = this.head;
        while (cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur = this.head;
        while (cur != null){
            if(cur.val == key){
                if(cur == this.head){
                    //删除头节点
                    this.head = this.head.next;
                    if(this.head != null){
                        //只有一个节点的情况
                        this.head.prev = null;
                    }
                }else {
                    //删除中间节点
                    cur.prev.next = cur.next;
                    if(cur != this.last){
                        cur.next.prev = cur.prev;
                    }else {
                        //删除尾结点
                        this.last = cur.prev;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }
    //删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur = this.head;
        while (cur != null){
            if(cur.val == key){
                if(cur == this.head){
                    //删除头节点
                    this.head = this.head.next;
                    if(this.head != null){
                        //只有一个节点的情况
                        this.head.prev = null;
                    }
                }else {
                    //删除中间节点
                    cur.prev.next = cur.next;
                    if(cur != this.last){
                        cur.next.prev = cur.prev;
                    }else {
                        //删除尾结点
                        this.last = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
    }
    //得到双链表的长度
    public int size(){
        ListNode cur = head;
        int count = 0;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }
    public void display(){
        ListNode cur = head;
        while (cur != null){
            System.out.println(cur.val);
            cur = cur.next;
        }
    }
    public void clear(){
        /*this.head = null;
        this.last = null;*/
        ListNode cur = this.head;
        while (cur != null){
            if(cur.next != null) {
                cur = cur.next;
                cur.prev.next = null;
                if(cur.prev != null) {
                    //处理头节点
                    cur.prev.prev = null;
                }
            }else {
                //处理尾巴节点
                this.head = null;
                this.last = null;
                cur = null;
            }
        }
    }
}

四、LinedList的遍历

1、for循环遍历

2、for each遍历

3、使用迭代器正向遍历

4、使用迭代器反向遍历

五、ArrayList和LinedList的区别

1、存储空间

ArrayList在逻辑上是连续的,并且在物理内存上也是连续存储的;

LinkedList在逻辑上是连续的,但是在物理内存上不一定是连续的。

2、随机访问

ArrayList支持随机访问,并且时间复杂度为O(1);

LinkedList不支持随机访问,访问链表内元素需要遍历整个链表,时间复杂度为O(N)。

3、从头插入元素

ArrayList使用头插法效率较低,需要搬移元素,如果空间不够,还需要扩容,时间复杂度为O(N);

LinkedList只需修改引用的指向,没有容量的概念,不需要扩容,时间复杂度为O(1)。

4、应用场景

当元素需要频繁访问或高效存储时,ArrayList更适用;

当元素需要频繁删除或从任意位置插入时,LinkedList更适用;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃点橘子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值