【数据结构(三)】链表与LinkedList

❣博主主页: 33的博客

▶️文章专栏分类:数据结构◀️

🚚我的代码仓库: 33的代码仓库🚚

🫵🫵🫵关注我带你学更多数据结构知识

目录

1. 前言

在上一篇文章中,我们已经认识了顺序表,通过源码我们知道ArrayList底层是使用数组来存储元素,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

2.链表

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


注意: 链式结构是在逻辑上连续,但在物理上不一定连续。

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
1.单向或者双向

2.带头或者不带头

3.循环或者非循环

虽然有这么多的链表的结构,但是我们重点掌握两种:
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

3.链表的实现

3.1 IList接口

java 复制代码
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();
}

3.2MyLinkList实现

java 复制代码
public class MyLinkList implements IList{
    //内部类
    class Node{
        public int val;
        public Node next;//下一个结点
        public Node head;//头结点
        public Node(int val){
            this.val=val;
        }
    }  
    //自己创造的一个链表
    public void create(){
        Node node1=new Node(1);
        Node node2=new Node(2);
        Node node3=new Node(3);
        node1.next=node2;
        node2.next=node3;
       head= node1;
    }
    //头插法
    @Override
    public void addFirst(int data) {
        Node node=new Node(data);
        node.next=head;
        head=node;
    }
    //尾插法
    @Override
    public void addLast(int data) {
    Node node=new Node(data);
    Node end=head;
    while (end.next!=null){
        end=end.next;
    }
    end.next=node;
    }
    //在某一个位置插入一个元素
    @Override
    public void addIndex(int index, int data) {
    if(index<0||index>size()){
        System.out.println("位置不合法");
        return;
    }
    if(index==0){
        addFirst(data);
        return;
    }
    if(index==size()){
        addLast(data);
        return;
    }
    Node node=new Node(data);
    Node pre=getNode(index);
    node.next=pre.next;
    pre.next=node;
    }
    public Node getNode(int index){
        Node node=head;
        while (index-1!=0){
            node=node.next;
            index--;
        }
        return node;
    }
	//是否包含某一个元素
    @Override
    public boolean contains(int key) {
        Node node=head;
        while (node!=null){
            if(node.val==key){
                return true;
            }
            node=node.next;
        }
        return false;
    }
	//删除第一个为key的元素
    @Override
    public void remove(int key) {
    if(head.val==key){
        head=head.next;
        return;
    }
    int index=getindex(key);
    Node node=head;

    while (index-1!=0){
        node=node.next;
        index--;
    }
    node.next=node.next.next;
    }
    //根据key返回它的前一个坐标
    public int getindex(int key){
        int count=0;
        Node node=head;
        while (node!=null){
            if(node.val==key){
                return count;
            }
            node=node.next;
            count++;
        }
        return -1;
    }
	//删除所有为key的元素
    @Override
    public void removeAllKey(int key) {
    if (head==null){
        return;
    }
    Node node=head;
    Node cur=head.next;
    while (cur!=null){
        if(cur.val==key){
            node.next=cur.next;
            cur=cur.next;
        }else {
            node=node.next;
            cur=cur.next;
        }
    }
    if(head.val==key){
        head=head.next;
    }
    }
	//求链表大小
    @Override
    public int size() {
        Node node=head;
        int count=0;
        while (node!=null){
           count++;
            node=node.next;
        }
        return count;
    }
	//清空链表
    @Override
    public void clear() {
    Node node=head;
    while (node!=null){
        Node next=node.next;
        node.next=null;
        node=next;
    }
    head=null;
    }
	//打印链表
    @Override
    public void display() {
        Node node=head;
        while (node!=null){
            System.out.print(node.val+" ");
            node=node.next;
        }

    }
}

3.3 Test

java 复制代码
public class Test {
    public static void main(String[] args) {
        MyLinkList myLinkList=new MyLinkList();
        myLinkList.create();
        myLinkList.display();
        System.out.println();
        //头插法
        System.out.println("头插法");
        myLinkList.addFirst(12);
        myLinkList.addFirst(13);
        myLinkList.addFirst(14);
        myLinkList.display();
        System.out.println();
        //尾插法
        System.out.println("尾插法");
        myLinkList.addLast(21);
        myLinkList.addLast(22);
        myLinkList.addLast(23);
        myLinkList.display();
        System.out.println();
        //中间位置插入
        System.out.println("在1位置插入100");
        myLinkList.addIndex(1,100);
        myLinkList.addIndex(1,100);
        myLinkList.addIndex(1,100);
        myLinkList.display();
        System.out.println();
        //查找元素
        Boolean a=myLinkList.contains(100);
        System.out.println(a);
        //删除第一个100元素
        myLinkList.remove(100);
        myLinkList.display();
        System.out.println();
        //删除所有100的元素
        myLinkList.removeAllKey(100);
        myLinkList.display();
        System.out.println();
        //求size
        int Size=myLinkList.size();
        System.out.println(Size);
        //清空链表
        myLinkList.clear();
        myLinkList.display();
    }
}

输出结果如下:

4.LinkedList

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

LinkedList源码:

【说明】
通过上图所示,我们可以知道LinkedList实现了List接口,LinkedList的底层使用了双向链表。

4.1 LinkedList的使用

构造:

方法 解释
LinkedList() 无参构造
public LinkedList(Collection<? extends E> c) 使用其他集合容器中元素构造List
java 复制代码
 public static void main(String[] args) {
 // 构造一个空的LinkedList
 List<Integer> list1 = new LinkedList<>();
 List<String> list2 = new java.util.ArrayList<>();
 list2.add("JavaSE");
 list2.add("JavaWeb");
 list2.add("JavaEE");
 // 使用ArrayList构造LinkedList
 List<String> list3 = new LinkedList<>(list2);
 }

其他常用方法:

方法 解释
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< E > subList(int fromIndex, int toIndex) 截取部分 list

4.2 LinkedList的遍历

java 复制代码
LinkedList<Integer> list = new LinkedList<>();
    list.add(1);   // add(elem): 表示尾插
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    System.out.println(list.size());
    // foreach遍历
    for (int e:list) {
        System.out.print(e + " ");
    }
    System.out.println();
    // 使用迭代器遍历---正向遍历
    ListIterator<Integer> it = list.listIterator();
    while(it.hasNext()){
        System.out.print(it.next()+ " ");
    }
    System.out.println();

4.3ArrayList和LinkedList的区别

本篇文章主要介绍了链表的基础知识,简单介绍了什么是链表以及如何实现一个链表,以及LinkedList的操作方法,在下一篇文章中博主将带领同学们一起学习链表的相关习题。

下期预告:链表经典练习题

相关推荐
xlsw_4 分钟前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹1 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭1 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫2 小时前
泛型(2)
java
超爱吃士力架2 小时前
邀请逻辑
java·linux·后端
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石2 小时前
12/21java基础
java
李小白662 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp2 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
菜鸡中的奋斗鸡→挣扎鸡2 小时前
滑动窗口 + 算法复习
数据结构·算法