【Java数据结构】通过Java理解和实现——顺序表和单链表(二)

简介: 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

🍉链表

🌵链表概念及结构

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

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

  • 单向、双向
  • 带头、不带头
  • 循环、非循环

我们重点学习以下两种链表

61e7d2624a1747ed818944373f0f89d2.png

981e17d8406641f1b46bd57eb7c535dd.png

🍌无头单向非循环链表接口实现(注释非常详细,我👴👴都能看懂)

先写两个类,一个是链表类(包含有一个可变的头结点和实现各种功能的接口,因为是无头链表,所以这个头结点是可变的),一个是节点类(成员属性有value和next)


3676f9567f53494c863e1d3b8f4900f0.png

d37ae62301634e54bc47cd5df1583c50.png

接下来的接口都是写在链表类里的,因为链表是一个对象,我们要实现的就是一个链表对象所有的功能

🍈打印链表

打印链表其实和打印顺序表类似,遍历链表就好了,不过要注意一个点,这里需要引入一个局部变量cur来代替头节点去遍历,因为头结点在没添加或者删除节点之前是固定的,不要让头结点变来变去

//打印链表
   public void display(){
       ListNode cur = this.head;//利用创建一个局部变量cur来替代head,这样头结点就不会改变了
       while (cur!=null) {//遍历的条件就是下一个节点的引用(地址)不为null
           System.out.print(cur.value+" ");
           cur = cur.next;//找到下一个节点
       }
       System.out.println();
   }

🍈头插法插入

顾名思义,头插法就是从头部插入节点,使新创建的节点成为新的头结点,这里需要额外考虑一个点,就是头结点是否存在(链表是否为空),不过以下代码能处理头结点为空的情况(注意要先将原头结点的地址先存入新节点,再将新节点引用赋给原头结点成为新头结点)

//头插法
   public void addFirst(int data){
        ListNode node = new ListNode(data);//创建新节点并初始化节点的data
        node.next = this.head;//将当前头结点地址存到新节点的next里
        this.head = node;//将新创建的节点变为头结点
        //这两行代码包含了头结点为null的情况
   }

🍈尾插法插入

尾插法不同于头插法,必须先判断链表是否为空(判断头结点是否为null),然后引入局部变量cur遍历链表,直到cur.next为空的时候,说明找到尾节点了,此时的cur就是尾巴结点

//尾插法
   public void addLast(int data){
   //找尾巴,cur.next为null了,说明这是尾节点了
       ListNode node = new ListNode(data);//创建新节点并初始化节点的data
       if (this.head == null){ //尾插的第一次必须判断头结点是否为空
           this.head = node;//如果是第一次插入,则新节点就是头结点
       }
       ListNode cur = this.head; //引入局部变量cur遍历链表
           while(cur.next != null){ //next等于null了就跳出while了
               cur = cur.next;//找到下一个节点
           }
       cur.next = node;
   }

🍈查找是否包含关键字key在单链表当中

传入关键字key,依旧引入局部变量cur遍历链表,哪个节点的value等于key了,说明链表里有这个关键字,返回true,否则返回false

//查找是否包含关键字key是否在单链表当中
   public boolean contains(int key){
       ListNode cur = this.head;//引入局部变量cur遍历
           while(cur != null){//循环条件为节点引用不为null
               if (key == cur.value)
                   return true;//如果找到了,返回true
               cur = cur.next;//找到下一个节点
           }
           return false;
   }

🍈得到单链表的长度

依旧采取引用局部变量cur来遍历链表,还要多设置一个局部变量size来计数,只要节点不为null,size就+1,最后返回size的值就是链表长度了

//得到单链表的长度
   public int size(){
       int size=0;//引入局部变量来计数
       ListNode cur = this.head;
          while(cur != null){//遍历并计数
              size++;//节点不为null,计数器+1
              cur = cur.next;//找到下一个节点
          }
          return size;//返回链表长度
   }

🍈任意位置插入,第一个数据节点为0号下标

首先得判断你要插入的这个位置是否合法,然后这里需要额外写一个查找插入位置前一个节点的方法findIndex()用于插入节点,插入原理

9782adb441354eec90f66d0d152fee4b.png

//根据传入的index查找前一个节点并返回地址
   public ListNode findIndex(int index){
       ListNode cur = this.head;//引入局部变量遍历至index前一节点
       while (index-1 != 0){//停止条件就是index-1等于0了
       //也就是说遍历到index位置上一个节点了
           cur = cur.next;//向后遍历
           index--;//每向后找一个节点index减1
       }
       return cur;//返回index上一个节点引用
   }
//任意位置插入,第一个数据节点为0号下标
   public void addIndex(int index,int data){//需要创建一个查找index位置前一节点的函数
       if (index > 0 && index < size()) {//判断插入位置是否合法
           ListNode node = new ListNode(data);//创建新节点并初始化节点的data
           node.next = findIndex(index).next;//通过上边写的查找方法将index位置前一节点的下一节点引用赋给新插入节点的next
           findIndex(index).next = node;//将新节点的引用存到查找到的节点的next达到链接效果
       }
       else if (index==0) {//如果插入位置是0,则直接使用头插法插入
           addFirst(data);
           return;
       }
       else if(index==size()) {//如果插入位置是链表长度值,则直接使用尾插法插入
           addLast(data);
           return;
       }
       else
           System.out.println("位置不合法!");
       return;
   }

🍈删除第一次出现关键字为key的节点

首先判断头结点是否为null(链表是否为空),然后有两种情况
①关键字在头结点:将头结点下一个节点设置为新头结点
②关键字不在头结点:将有关键字的节点的下一节点引用赋给有关键字节点的上一节点的next

//删除第一次出现关键字为key的节点
   public void remove(int key){
       if (this.head == null){//判断链表是否为空
           System.out.println("链表为空,不能删除");
           return;
       }
       ListNode cur = this.head;
       while(cur.next != null){//遍历链表
           if(cur.value == key) {//①关键字在头结点:将头结点下一个节点设置为新头结点
               head = cur.next;
               return;
           }
           else if(cur.next.value == key) {//②关键字不在头结点:将有关键字的节点的下一节点引用赋给有关键字节点的上一节点的next
               cur.next = cur.next.next;
               return;
           }
           cur = cur.next;
       }
       System.out.println("没有你要删除的节点");
   }

🍈删除所有值为key的节点

和上边删除第一次出现的key类似,只不过return换成了continue,再有就是需要设置一个局部变量size,删除过程进行完之后若删除前设置的size值没发生改变,则说明没有删除节点

//删除所有值为key的节点
   public void removeAllKey(int key){
       int size = size();
       if (this.head == null){
           System.out.println("链表为空,不能删除");
           return;
       }
       ListNode cur = this.head;
       while(cur.next != null){
           if(cur.value == key) {
               head = cur.next;
               continue;//删除之后不要return,继续遍历
           }
           else if(cur.next.value == key) {
               cur.next = cur.next.next;
               continue;//删除之后不要return,继续遍历
           }
           cur = cur.next;
       }
       if(size()==size) {//如果进行删除前设置的size等于进行删除后size()方法返回的值,说明没有进行删除操作
           System.out.println("没有你要删除的节点");
       }
   }

🍈清空链表

暴力清空,直接将头结点置空,这样整个链表就无法找到了

//清空链表
   public void clear(){
       this.head = null;
   }

顺序表和链表的区别与联系

顺序表

顺序表:一白遮百丑
白:空间连续、支持随机访问
丑:1.中间或前面部分的插入删除时间复杂度O(N) 2.增容的代价比较大。

链表

链表:一(胖黑)毁所有
胖黑:以节点为单位存储,不支持随机访问
所有:1.任意位置插入删除时间复杂度为O(1) 2.没有增容问题,插入一个开辟一个空间



相关文章
|
2月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
53 1
|
2月前
|
存储 Java
告别混乱!用Java Map优雅管理你的数据结构
【10月更文挑战第17天】在软件开发中,随着项目复杂度增加,数据结构的组织和管理至关重要。Java中的Map接口提供了一种优雅的解决方案,帮助我们高效、清晰地管理数据。本文通过在线购物平台的案例,展示了Map在商品管理、用户管理和订单管理中的具体应用,有效提升了代码质量和维护性。
97 2
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
77 2
|
20小时前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
13天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
33 5
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
51 6
|
1月前
|
存储 Java 索引
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
1月前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
82 3
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明