Java数据结构之第五章、LinkedList与链表

简介: 由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。

 一、ArrayList的缺陷

public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        // ...
// 默认容量是10
        private static final int DEFAULT_CAPACITY = 10;
        //...
// 数组:用来存储元素
        transient Object[] elementData; // non-private to simplify nested class access
        // 有效元素个数
        private int size;
        public ArrayList(int initialCapacity) {
            if (initialCapacity > 0) {
                this.elementData = new Object[initialCapacity];
            } else if (initialCapacity == 0) {
                this.elementData = EMPTY_ELEMENTDATA;
            } else {
                throw new IllegalArgumentException("Illegal Capacity: " +
                        initialCapacity);
            }
        } // ...
    }

image.gif

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。


二、链表

2.1链表的概念及结构

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

image.gif编辑

实际中链表的结构非常多样,通过节点组成。以下情况组合起来就有8种链表结构:

1.单向 带头 循环链表   2.单向 带头 非循环链表   3.单向 不带头 循环链表 4.单向 不带头 非循环链表

5.双向 带头 循环链表   6.双向 带头 非循环链表   7.双向 不带头 循环链表   8.双向 不带头 非循环链表

2.1.1单向和双向

image.gif编辑

2.1.2带头或者不带头

image.gif编辑

2.1.3循环或者非循环

image.gif编辑

2.1.4重点

1.无头单向非循环链表

结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

image.gif编辑

2. 无头双向链表

在Java的集合框架库中LinkedList底层实现就是无头双向循环链表

2.2链表的实现

// 1、无头单向非循环链表实现
public class IndexOutOfBounds extends RuntimeException{
    public IndexOutOfBounds(String message){
        System.out.println(message);
    }
}
   public class MySingleList {
    //节点的创建
    class ListNode{
        //值域
        public int val;
        //引用变量
        public ListNode next;
        public ListNode(int val){
            this.val=val;
        }
    }
    //永远指向头节点
    public ListNode head;
    public void createList(){
        ListNode node1=new ListNode(12);
        ListNode node2=new ListNode(23);
        ListNode node3=new ListNode(34);
        ListNode node4=new ListNode(45);
        ListNode node5=new ListNode(56);
        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        node4.next=node5;
        head.next=node1;
    }
    public void show(){
        ListNode cur=head;
        while(cur!=null){
            System.out.println(cur.val);
            cur=cur.next;
        }
        System.out.println();
    }
    //头插法
    public void addFirst(int data){
        ListNode cur=new ListNode(data);
        cur.next=head;
        head=cur;
    }
    //尾插法
    public void addLast(int data){
        ListNode node=new ListNode(data);
        if(head==null){
            head=node;
            return ;
        }
        ListNode cur=head;
        while(cur!=null){
            cur=cur.next;
        }
        //cur 指向的节点是尾巴节点
        cur.next=node;
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        int len=size();
        if(index<0||index>len){
            throw new IndexOutOfBounds("任意位置插入数据的时候,index的位置不合法:"+index);
        }
        if(index==0){
            addFirst(data);
            return ;
        }
        if(index==len){
            addLast(data);
            return ;
        }
        ListNode node=new ListNode(data);
        ListNode cur=findIndex(index);
        node.next=cur.next;
        cur.next=node;
    }
    private ListNode findIndex(int index){
        ListNode cur=head;
        while(index-1!=0){
            cur=cur.next;
            index--;
        }
        return cur;
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur=head;
        while(cur!=null){
            //如果value值为空,需要使用equals方法比较
            if(cur.val==key){
                return true;
            }
            cur=cur.next;
        }
        return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key){
        if(head==null){
            return ;
        }
        if(head.val==key){
            head=head.next;
            return ;
        }
        ListNode prev=searchPrev(key);
        if(prev==null){
            System.out.println("没有这个数据!");
            return ;
        }
        ListNode del=prev.next;
        prev.next=del.next;
    }
    private ListNode searchPrev(int key){
        ListNode prev=head;
        while(prev.next!=null){
            if(prev.next.val==key){
                return prev;
            }
            else{
                prev=prev.next;
            }
        }
        return null;
    }
    //删除所有值为key的节点
    public void removeAllKey(int key){
        if(head==null){
            return ;
        }
        //第一种删除头节点方法
        /*
        * while(head.val==key){
        *   head=head.next;
        * }
        * */
        ListNode cur=head.next;
        ListNode prev=head;
        while(cur!=null){
            if(cur.val==key){
                prev.next=cur.next;
                cur=cur.next;
            }
            else{
                prev=cur;
                cur=cur.next;
            }
        }
        //第二种删除头节点方法
        if(head.val==key){
            head=head.next;
        }
    }
    //得到单链表的长度
    public int size(){
        int count=0;
        ListNode cur=head;
        while(cur!=null){
            count++;
            cur=cur.next;
        }
        return count;
    }
    public void clear() {
        //this.head=null;
        while(head!=null){
            ListNode HeadNext=head.next;
            head.next=null;
            head=HeadNext;
        }
    }
}

image.gif


目录
相关文章
|
2月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
50 1
|
7天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
29 5
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
48 6
|
1月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
2月前
|
存储 算法 Java
Java 中常用的数据结构
【10月更文挑战第20天】这些数据结构在 Java 编程中都有着广泛的应用,掌握它们的特点和用法对于提高编程能力和解决实际问题非常重要。
33 6
|
2月前
|
安全 Java 程序员
Java集合之战:ArrayList vs LinkedList,谁才是你的最佳选择?
本文介绍了 Java 中常用的两个集合类 ArrayList 和 LinkedList,分析了它们的底层实现、特点及适用场景。ArrayList 基于数组,适合频繁查询;LinkedList 基于链表,适合频繁增删。文章还讨论了如何实现线程安全,推荐使用 CopyOnWriteArrayList 来提升性能。希望帮助读者选择合适的数据结构,写出更高效的代码。
79 3
|
2月前
|
存储 Java 开发者
Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效
【10月更文挑战第19天】在软件开发中,随着项目复杂度的增加,数据结构的组织和管理变得至关重要。Java中的Map接口提供了一种优雅的方式来管理数据结构,使代码更加清晰、高效。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,帮助开发者告别混乱,提升代码质量。
32 1
|
1月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
174 9
|
1月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
31 1
|
21天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
43 5