《Java小子怒闯数据结构九重天》第五重天——链表

简介: 自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰,以下是第五重的内容。

前言


自古以来数据结构界就分为九重天,据说冲破这九重天之后就可以去进攻算法界最终修炼最后成佬,受万人敬仰。


但是这谈何容易,因为每一重天都有神兽把守,想要冲破每一重天都必须收服守护的神兽才行。


守护九重天的神兽分别是:数组、字符串、栈、队列、链表、树、散列表、堆、图。可见他们的战斗力也是逐层增强的。想只凭靠自身的能力拿下他们谈何容易。


不过大家不必惊慌,我这里有一本上古秘籍《Java小子怒闯数据结构九重天》,里面有每一重天神兽的攻略。只要修炼者仔细钻研里面的每一篇,对九重天了如指掌之后,冲破这九重天也是易如反掌的。


今天为大家带来第五重天的攻略!

image.png

1.🌀链表的基础知识

链表是一种线性表数据结构。它使用一组任意的存储单元(可以是连续的,也可以是不连续的),来存储一组具有相同类型的数据。

简单来说链表是实现线性表的链式存储结构的基础。


链表是一种通过指针串联在一起的线性结构,每一个节点是由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。


节点情况如下:


数据域 指针域

date next

上面的介绍都是单链表相关的,除了单链表我们还有双向链表,循环链表等。


双链表


双链表顾名思义就是比单链表高级了一点,一个双链表的结点在单链表的基础上又增加了一个指针,这个指针指向它的前一个结点


节点情况如下:


指针域 数据域 指针域

prior date next

循环链表


之前的链表都是条状的,循环链表是头尾相连了,就是将链表的尾部与头部连接起来。有因为条状链表有单链表与双链表之分,所以循环链表也有两种分别是:循环单链表与循环双链表


对于循环单链表我们一般设置尾指针这样操作效率会更高。


对于循环双链表我们头结点的prior结点指向最后一个节点,尾结点的next指针指向头结点,以形成循环双链表。


2.🌀Java实例化链表

想要实例化链表就得先创建一个链表的结构类,这个类代表的是每一个节点结构。


之前提到过每一个节点是由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。


所以链表节点结构定义为:


class ListNode {
    int val;       // 节点值
    ListNode next; // 后继节点引用
    ListNode(int x) { 
      val = x; 
    }
}



使用定义的这个链表结构我们可以自己构造一个链表如下:


// 实例化节点

ListNode n1 = new ListNode(4); // 节点 head
ListNode n2 = new ListNode(5);
ListNode n3 = new ListNode(1);


// 连接节点使之成为链表

n1.next = n2;
n2.next = n3;
n3.next = null;



我们构造出来的链表如下图:


image.png

3.🌀Java实现单链表

其中实现了Iterable接口,提供了遍历方式。


import java.util.Iterator;
/**
 * 链表的head是不可以动的
 * @param 
 */
public class LinkList implements Iterable{
    private Node head;//头结点,链表的head是不可以动的
    private int N;//结点个数
  //构造方法
    public LinkList() {
        this.head = new Node(null,null);
        N = 0;
    }
    //结点内部类
    private class Node{
        //存储数据
        T item;
        //下一个结点
        Node next;
        public Node(T item,Node next) {
            this.item = item;
            this.next = next;
        }
    }
    //清空链表
    public void clear(){
        head.item=null;
        head.next=null;// 头节点next为null就是空链表
        this.N=0;
    }
    //判断链表是否为空
    public boolean isEmpty(){
        return this.N==0;
    }
    //获取链表的长度
    public int length(){
        return this.N;
    }
    //读取链表第i位置的元素值并返回
    public T get(int i){
        //非法性检查
        if(i<0 || i > this.N){
            throw new RuntimeException("位置不合法");
        }
        // n也是指向头结点
        Node n = head;
        for(int index=0; index
            n = n.next;
        }
        return n.item;
    }
    //往链表中插入数据t
    public void insert(T t){
        // head不可以移动,不然就无法在找到链表
        // 定义一个临时的Node也指向head的指针就可以通过移动该指针就可以
        Node n = head;
        // 获取尾节点
        while(true){
            // 当刚好就一个节点时(头节点)
            if(n.next == null){
                break;
            }
            n = n.next;
        }
        //当为空表时,就可以插入
        Node node = new Node(t, null);
        n.next =node;
        this.N ++;
    }
    //在第i位置上插入数据t
    public void insert(T t,int i){
        // 非法性检查
        if(i < 0 || i > this.N){
            throw  new RuntimeException("插入位置非法");
        }
        Node pre = head;
        for(int index=0;index <= i-1; index++){
            pre = pre.next;
        }
        Node current = pre.next;
        //先链接后面结点
        Node newNode = new Node(t,null);
        pre.next = newNode;
        newNode.next = current;
        this.N++;
    }
    //移除并返回第i位置的元素值
    public  T remove(int i){
        // 非法性检查
        if(i < 0 || i >this.N){
            throw  new RuntimeException("删除位置有误");
        }
        Node n =head;
        for(int index=0;index <= i-1;index ++){
             n = n.next;
        }
        //要删除的节点
        Node curr = n.next;
        n.next = curr.next;
        this.N--;//结点个数减一
        return curr.item;
    }
    //查找元素t在链表中第一次出现的位置
    public int indexof(T t){
        Node n = head;
        for(int i = 0; n.next != null;i++){
            n =n.next;
            if(n.item.equals(t)){
                return i;
            }
        }
        return -1;
    }
    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node n =head;
            @Override
            public boolean hasNext() {
                return n.next !=null;
            }
            @Override
            public Object next() {
                //下移一个指针
                n = n.next;
                return n.item;
            }
        };
    }
}


补充一点:链表的赋值给新的链表后,两个链表是会相会影响的,说白了就是把地址赋值给它了,他们操作是同一块内存的同一个对象。


Node n = head;


把head赋值给n,现在对n操作也是会影响head的。


4.🌀链表进阶练习

Leetcode 206. 反转链表

微信图片_20220523230415.png

题解:


我们可以申请两个指针,第一个指针叫 pre,最初是指向 null 的。

第二个指针 cur 指向 head,然后不断遍历 cur。

每次迭代到 cur,都将 cur 的 next 指向 pre,然后 pre 和 cur 前进一位。

都迭代完了(cur 变成 null 了),pre 就是最后一个节点了。


代码如下:

class Solution {
  public ListNode reverseList(ListNode head) {
  //申请节点,pre和 cur,pre指向null
  ListNode pre = null;
  ListNode cur = head;
  ListNode tmp = null;
  while(cur!=null) {
    //记录当前节点的下一个节点
    tmp = cur.next;
    //然后将当前节点指向pre
    cur.next = pre;
    //pre和cur节点都前进一位
    pre = cur;
    cur = tmp;
  }
  return pre;
  }
}


结语

恭喜你修炼到这里,你已经基本有了收服神兽链表的能力。神兽链表是我们到进攻算法界最重要的能力之一。大家不可懈怠。



相关文章
|
6月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
179 1
|
4月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
217 3
|
11月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
188 4
|
12月前
|
存储 Java
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
129 1
|
6月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
560 1
|
8月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
237 30
|
8月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
372 25
|
9月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
385 5
|
10月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
10月前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
158 5

热门文章

最新文章