数据结构 链表

数据结构 链表

1. ArrayList的缺陷

在上篇文章中我们已经熟悉了ArrayList的使用,并且进行了简单的模拟实现。通过源码知道,ArrayList底层使用数组来存储元素,但由于其底层是一段连续的空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(N),效率较低。因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:Java集合中有引入了LinkedList,即链表结构

2. 链表

2.1 链表的概念及结构

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

在这里插入图片描述

链表的结构非常多,以下情况组合起来就有8种链表结构:

  1. 单向或双向:

    在这里插入图片描述

  2. 带头或不带头:

    在这里插入图片描述

  3. 循环或非循环:

    在这里插入图片描述

链表结构较多,本文重点介绍以下两种:

  • 无头单向非循环链表: 结构简单,一般不会单独用来存数据,实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等
  • 无头双向链表:在Java的集合框架库中LinkedList底层实现的就是无头双向循环链表

2.2 单链表的实现

  • IList接口

    package demo2;
    
    public interface IList {
        //头插法
        void addFirst(int data);
        //尾插法
        void addLast(int data);
        //任意位置插入,第一个数据节点为0号下标
        void addIndex(int index,int data);
        //查找是否包含关键字key是否在单链表当中
        boolean contains(int key);
        //删除第一次出现关键字为key的节点
        void remove(int key);
        //删除所有值为key的节点
        void removeAllKey(int key);
        //得到单链表的长度
        int size();
        void clear();
        void display();
    }
    
  • MyLinkList实现IList接口

    关键步骤: 设置内部类定义链表结构:

    public class MyLinkList implements IList{
    
        static class ListNode {
    
           public int val;
           public ListNode next;
    
            public ListNode(int val) {
                this.val = val;
            }
        }
    }
    
    package demo2;
    
    public class MyLinkList implements IList{
    
        static class ListNode {
    
           public int val;
           public ListNode next;
    
            public ListNode(int val) {
                this.val = val;
            }
        }
        public ListNode head;
    	
        /*初始化链表*/
        public void creatList() {
            ListNode node1 = new ListNode(1);
            ListNode node2 = new ListNode(12);
            ListNode node3 = new ListNode(123);
            ListNode node4 = new ListNode(1234);
    
            node1.next = node2;
            node2.next = node3;
            node3.next = node4;
            node4.next = null;
    
            this.head = node1;
        }
    
    	/*头插法*/
        @Override
        public void addFirst(int data) {
            ListNode cur = new ListNode(data);
            cur.next = this.head;
            this.head = cur;
        }
    	
        /*尾插法*/
        @Override
        public void addLast(int data) {
    
            ListNode node = new ListNode(data);
            if (this.head == null) {
                this.head = node;
                return;
            }
    
            ListNode cur = this.head;
    
            while(cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    	
        /*指定位置插入元素*/
        @Override
        public void addIndex(int index, int data) {
            if (index < 0 || index > size()) {
                throw new ArrayIndexOutOfBoundsException("下标超出范围");
            }
            if (index == 0) {
                addFirst(data);
            }
            else if (index == size()) {
                addLast(data);
            }
            else {
                ListNode cur = this.head;
                ListNode node = new ListNode(data);
                int count = 0;
                while(count < index - 1) {
                    cur = cur.next;
                    count++;
                }
                node.next = cur.next;
                cur.next = node;
            }
        }
    	
        /*判断链表是否包含该元素*/
        @Override
        public boolean contains(int key) {
            ListNode cur = this.head;
    
            while(cur != null) {
                if (cur.val == key) {
                    return true;
                }
                cur = cur.next;
            }
            return false;
        }
    
        /*清空指定元素*/
        @Override
        public void remove(int key) {
            if (this.head == null) {
                System.out.println("无元素可以删除");
                return;
            }
            if (this.head.val == key) {
                this.head = head.next;
            }
            ListNode cur = this.head;
            while(cur.next != null) {
                if (cur.next.val == key) {
                    ListNode del = cur.next;
                    cur.next = del.next;
                    return;
                }
                cur = cur.next;
            }
        }
    
        /*清空所有指定元素*/
        @Override
        public void removeAllKey(int key) {
            if (this.head == null) {
                System.out.println("链表为空");
                return;
            }
            ListNode prev = this.head;
            ListNode cur = this.head.next;
            while(cur != null) {
                if (cur.val == key) {
                    prev.next = cur.next;
                    cur = cur.next;
                }
                else {
                    prev = cur;
                    cur = cur.next;
                }
            }
        }
    
        @Override
        public int size() {
            ListNode cur = this.head;
            int count = 0;
            while(cur != null) {
                count++;
                cur = cur.next;
            }
            return count;
        }
    
    
        /*清空链表*/
        @Override
        public void clear() {
            ListNode cur = this.head;
    
            while(cur != null) {
                ListNode curNext = cur.next;
                cur.next = null;
                cur = curNext;
    
            }
            head = null;
        }
    
        @Override
        public void display() {
           ListNode cur = this.head;
           while(cur != null) {
               System.out.print(cur.val + " ");
               cur = cur.next;
           }
            System.out.println();
    
        }
    }   
    
  • Main主程序

    package demo2;
    
    public class Main {
       
        public static void main(String[] args) {
            MyLinkList myLinkList = new MyLinkList();
            myLinkList.creatList();
            myLinkList.display();
    		
            //头插法
            MyLinkList myLinkList1 = new MyLinkList();
            myLinkList1.addFirst(25);
            myLinkList1.addFirst(72);
            myLinkList1.display();
    		
            //尾插法
            MyLinkList myLinkList2 = new MyLinkList();
            myLinkList2.addLast(11);
            myLinkList2.addLast(150);
            myLinkList2.addLast(99);
            myLinkList2.display();
    
            System.out.println(myLinkList2.contains(99));
            System.out.println(myLinkList2.contains(101));
    		
            //指定位置插入
            myLinkList2.addIndex(1,77);
            myLinkList2.display();
    		
            //移除指定元素
            myLinkList2.remove(99);
            myLinkList2.display();
    		
            //清空链表
            myLinkList2.clear();
            myLinkList2.display();
            System.out.println("====");
    
            //清空所有指定元素
            MyLinkList myLinkList3 = new MyLinkList();
            myLinkList3.addLast(13);
            myLinkList3.addLast(14);
            myLinkList3.addLast(15);
            myLinkList3.addLast(14);
            myLinkList3.addLast(17);
            myLinkList3.display();
            myLinkList3.removeAllKey(14);
            myLinkList3.display();
    
        }
    }
    
    //执行结果
    1 12 123 1234 
    ==========
    72 25 
    ==========
    11 150 99 
    true
    false
    ==========
    11 77 150 99 
    ==========
    11 77 150 
    ==========
    
    ==========
    13 14 15 14 17 
    13 15 17 
    ==========
    

    在这里插入图片描述

2.3 双链表的实现

  • IList接口

    package demo3;
    
    public interface IList {
    
            //头插法
            void addFirst(int data);
            //尾插法
            void addLast(int data);
            //任意位置插入,第一个数据节点为0号下标
            void addIndex(int index,int data);
            //查找是否包含关键字key是否在单链表当中
            boolean contains(int key);
            //删除第一次出现关键字为key的节点
            void remove(int key);
            //删除所有值为key的节点
            void removeAllKey(int key);
            //得到单链表的长度
            int size();
            void clear();
            void display();
    
    }
    
  • MyLinkedList类实现IList接口

    关键步骤:设置内部类定义链表结构:

    public class MyLinkedList implements IList{
        static class ListNode {
            public int val;
            public ListNode prev;
            public ListNode next;
    
            public ListNode(int data) {
                this.val = data;
            }
        }
        public ListNode head;
        public ListNode last;
    }
    

    这里比单链表多了一个prev指针(指向当前节点的上一个节点)和last尾指针(指向最后一个节点)

    package demo3;
    
    import demo2.MyLinkList;
    
    import java.util.List;
    
    public class MyLinkedList implements IList{
        static class ListNode {
            public int val;
            public ListNode prev;
            public ListNode next;
    
            public ListNode(int data) {
                this.val = data;
            }
        }
        public ListNode head;
        public ListNode last;
    
        /*头插法*/
        @Override
        public void addFirst(int data) {
            ListNode node = new ListNode(data);
            if (this.head == null) {
                head = node;
                last = node;
            }
            else {
                this.head.prev = node;
                node.next = this.head;
                head = node;
            }
        }
    
        /*尾插法*/
        @Override
        public void addLast(int data) {
            ListNode node = new ListNode(data);
            if (this.head == null) {
                head = node;
                last = node;
            }
            else {
                last.next = node;
                node.prev = last;
                last = node;
            }
        }
    
        /*指定位置插入元素*/
        @Override
        public void addIndex(int index, int data) {
            if (index < 0 || index > size()) {
                throw new ArrayIndexOutOfBoundsException("下标超出范围");
            }
            if (index == 0) {
                addFirst(data);
            }
            else if (index == size()) {
                addLast(data);
            }
            else {
                ListNode cur = this.head;
                ListNode node = new ListNode(data);
    
                for (int i = 0;i < index - 1;i++) {
                    cur = cur.next;
                }
    
                node.prev = cur.prev;
                node.next = cur;
                cur.prev.next= node;
                cur.prev = node;
            }
        }
    
        /*判断元素是否在链表中*/
        @Override
        public boolean contains(int key) {
            ListNode cur = this.head;
            while(cur != null) {
                if (cur.val == key) {
                    return true;
                }
                cur = cur.next;
            }
            return false;
        }
    
        /*移除指定元素*/
        @Override
        public void remove(int key) {
    
            ListNode cur = this.head;
            while(cur != null) {
                if (cur.val == key) {
                    if (cur == head) {
                        head = head.next;
                        if (head == null) {
                            last = null;
                        }
                        else {
                            head.prev = null;
                        }
                    }
                    else {
                        cur.prev.next = cur.next;
                        if (cur.next == null) {
                            last = last.prev;
                        }
                        else {
                            cur.next.prev = cur.prev;
                        }
                    }
                    return;
                }
                else {
                    cur = cur.next;
                }
            }
        }
    
        /*移除所有指定元素*/
        @Override
        public void removeAllKey(int key) {
    
            ListNode cur = this.head;
            while(cur != null) {
                if (cur.val == key) {
                    if (cur == head) {
                        head = head.next;
                        if (head == null) {
                            last = null;
                        }
                        else {
                            head.prev = null;
                        }
                    }
                    else {
                        cur.prev.next = cur.next;
                        if (cur.next == null) {
                            last = last.prev;
                        }
                        else {
                            cur.next.prev = cur.prev;
                        }
                    }
                }
                cur = cur.next;
            }
        }
    
        /*返回链表长度*/
        @Override
        public int size() {
            ListNode cur = this.head;
            int len = 0;
            while(cur != null) {
                len++;
                cur = cur.next;
            }
            return len;
        }
    
        /*清空链表*/
        @Override
        public void clear() {
            ListNode cur = this.head;
            while(cur != null) {
                ListNode curNext = cur.next;
                cur.prev = null;
                cur.next = null;
                cur = curNext;
    
            }
            this.head = null;
            this.last = null;
        }
    
        @Override
        public void display() {
            ListNode cur = this.head;
            while(cur != null) {
                System.out.print(cur.val + " ");
                cur = cur.next;
            }
            System.out.println();
        }
    }
    
  • Main主程序

    package demo3;
    
    public class Main {
        public static void main(String[] args) {
            //头插法
            MyLinkedList myLinkedList1 = new MyLinkedList();
            myLinkedList1.addFirst(1);
            myLinkedList1.addFirst(2);
            myLinkedList1.addFirst(3);
            myLinkedList1.addFirst(4);
            myLinkedList1.display();
    
            //尾插法
            MyLinkedList myLinkedList2 = new MyLinkedList();
            myLinkedList2.addLast(11);
            myLinkedList2.addLast(12);
            myLinkedList2.addLast(13);
            myLinkedList2.addLast(14);
            myLinkedList2.display();
    
            //指定位置插入(首节点下标为0)
            myLinkedList2.addIndex(3,32);
    //        myLinkedList2.addIndex(4,17);
    //        myLinkedList2.addIndex(5,22);
            myLinkedList2.display();
    
            //判断元素是否在链表中
            System.out.println(myLinkedList2.contains(11));
            System.out.println(myLinkedList2.contains(25));
    
            //删除指定元素值
            myLinkedList2.remove(14);
            myLinkedList2.display();
    
            //清空链表
            myLinkedList1.clear();
            myLinkedList1.display();
    
            //删除链表中所有指定元素
            myLinkedList2.addLast(11);
            myLinkedList2.display();
            myLinkedList2.removeAllKey(11);
            myLinkedList2.display();
        }
    }
    
    //执行结果
    4 3 2 1 
    11 12 13 14 
    11 12 32 13 14 
    true
    false
    11 12 32 13 
    
    11 12 32 13 11 
    12 32 13 
    

    在这里插入图片描述

3. LinkedList的使用

3.1 什么是LinkedList

LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

在这里插入图片描述

在集合框架中,LinkedList也实现了List接口,如下图所示:

在这里插入图片描述

注:

  1. LinkedList实现了List接口
  2. LinkedList的底层使用了双链表
  3. Linked List没有实现RandomAccessjiekou,因此LinkedList不支持随机访问
  4. LinkedList的任意位置插入和删除元素效率比较高,时间复杂度为O(1)
  5. LinkedList比较适合任意位置插入的场景

3.2 LinkedList的使用

3.2.1 LinkedList的构造
方法解释
LinkedList()无参构造
public LinkedList(Collection<? extends E> c)使用其他集合容器中元素构造List

代码示例:

 public static void main(String[] args) {
        List<Integer> list1 = new LinkedList<>();
        list1.add(1);
        list1.add(12);
        list1.add(123);

        List<String> list2 = new java.util.ArrayList<>();
        list2.add("张三");
        list2.add("李四");
        list2.add("王五");

        //使用ArrayList构造LinkListed
        List<String> list3 = new LinkedList<>(list2);
        System.out.println(list1);
        System.out.println(list2);
        System.out.println(list3);
    }

//执行结果
[1, 12, 123]
[张三, 李四, 王五]
[张三, 李四, 王五]

在这里插入图片描述

3.2.2 LinkedList的其他常用方法介绍
方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list

代码示例:

package demo4;

import java.util.LinkedList;
import java.util.List;

public class Main1 {
    public static void main(String[] args) {

        LinkedList<Integer> list = new LinkedList<>();

        //添加元素,表示尾插
        list.add(1);
        list.add(12);
        list.add(123);
        list.add(1234);
        list.add(12345);

        System.out.println(list);
        System.out.println("============");

        //返回链表长度
        System.out.println(list.size());
        System.out.println("============");

        //从起始位置插入
        list.add(0,0);
        System.out.println(list);
        System.out.println("============");

        //删除指定位置的元素
        list.remove(2);
        System.out.println(list);
        //删除第一个元素
        list.removeFirst();
        //删除最后一个元素
        list.removeLast();
        System.out.println(list);
        System.out.println("============");

        //检查元素是否存在,如果是返回true,否则返回false
        if (!list.contains(100)) {
            System.out.println("元素不存在");
        }
        System.out.println("============");

        //从前往后找到第一个指定元素值的位置
        System.out.println(list.indexOf(1));
        //从后往前找到第一个指定元素值的位置
        System.out.println(list.lastIndexOf(1));
        System.out.println("============");

        //获取指定位置元素
        int elem = list.get(2);
        System.out.println(elem);
        System.out.println("============");

        //将指定位置元素改成指定元素
        System.out.println(list);
        list.set(2,11111);
        System.out.println(list);
        System.out.println("============");

        //subList(from,to): 用list中[from,to)之间的元素构造一个新的LinkedList返回
        List<Integer> newList = list.subList(0,2);
        System.out.println(list);
        System.out.println(newList);
        System.out.println("============");

        //将list中的元素清空
        list.clear();
        System.out.println(list);
        System.out.println(list.size());

    }
}

//执行结果
[1, 12, 123, 1234, 12345]
============
5
============
[0, 1, 12, 123, 1234, 12345]
============
[0, 1, 123, 1234, 12345]
[1, 123, 1234]
============
元素不存在
============
0
0
============
1234
============
[1, 123, 1234]
[1, 123, 11111]
============
[1, 123, 11111]
[1, 123]
============
[]
0

在这里插入图片描述

3.2.3 LinkedList的遍历

代码示例:

package demo5;

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list);

        //foreach遍历链表
        for (int x:list) {
            System.out.print(x + " ");
        }
        System.out.println();

        //使用迭代器遍历-正向遍历
        ListIterator<Integer> it = list.listIterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();

        //使用迭代器遍历-反向遍历
        ListIterator<Integer> rit = list.listIterator(list.size());
        while(rit.hasPrevious()) {
            System.out.print(rit.previous() + " ");
        }
        System.out.println();
    }
}

//执行结果
[1, 2, 3, 4, 5, 6, 7]
1 2 3 4 5 6 7 
1 2 3 4 5 6 7 
7 6 5 4 3 2 1 

在这里插入图片描述

4. ArrayList和LinkedList的区别

不同点ArrayListLinkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
插入空间不够时需要扩容没有容量的概念
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值