Java数据结构与算法学习之双向链表

Java数据结构与算法学习之双向链表

什么是双向链表?

双向链表是链表的一种,与单向链表不同的是,双向链表的每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,因此双向链表可以双向遍历。

双向链表的Java实现

Java中可以使用节点类来实现双向链表,节点类代码如下:

public class Node<T> {

    private T data;
    private Node<T> prev;
    private Node<T> next;

    public Node(T data, Node<T> prev, Node<T> next) {
        this.data = data;
        this.prev = prev;
        this.next = next;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Node<T> getPrev() {
        return prev;
    }

    public void setPrev(Node<T> prev) {
        this.prev = prev;
    }

    public Node<T> getNext() {
        return next;
    }

    public void setNext(Node<T> next) {
        this.next = next;
    }
}

具体实现过程的代码请参见Github链接:https://github.com/luokangyuan/data-structure-algorithm/tree/master/src/main/java/com/kangyuan/core/linklist

双向链表的基本操作

双向链表的基本操作包括:插入节点、删除节点、查找节点、修改节点等。以下是双向链表的基本操作的代码实现。

插入节点操作

双向链表的插入节点操作可以在任意位置插入一个新节点,包括表头和表尾。具体代码实现如下:

public void add(int index, T data) {
    if (index < 0 || index > size) {
        throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
    }
    Node<T> newNode = new Node<>(data, null, null);
    if (index == 0) { // 插入到链表头部
        newNode.setNext(head);
        head.setPrev(newNode);
        head = newNode;
    } else if (index == size) { // 插入到链表尾部
        newNode.setPrev(tail);
        tail.setNext(newNode);
        tail = newNode;
    } else { // 插入到链表中间
        Node<T> temp = getNode(index);
        newNode.setPrev(temp.getPrev());
        temp.getPrev().setNext(newNode);
        newNode.setNext(temp);
        temp.setPrev(newNode);
    }
    size++;
}

删除节点操作

双向链表的删除节点操作可以删除链表中任意位置的节点,包括表头和表尾。具体代码实现如下:

public T remove(int index) {
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
    }
    Node<T> removedNode = null;
    if (size == 1) { // 链表只有一个节点
        removedNode = head;
        head = null;
        tail = null;
    } else if (index == 0) { // 删除链表头部节点
        removedNode = head;
        head = head.getNext();
        head.setPrev(null);
    } else if (index == size - 1) { // 删除链表尾部节点
        removedNode = tail;
        tail = tail.getPrev();
        tail.setNext(null);
    } else { // 删除链表中间节点
        Node<T> temp = getNode(index);
        removedNode = temp;
        temp.getPrev().setNext(temp.getNext());
        temp.getNext().setPrev(temp.getPrev());
    }
    size--;
    return removedNode.getData();
}

查找节点操作

双向链表的查找操作即根据索引查找节点,实现代码如下:

public Node<T> getNode(int index) {
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
    }
    Node<T> temp = head;
    for (int i = 0; i < index; i++) {
        temp = temp.getNext();
    }
    return temp;
}

修改节点操作

双向链表要修改节点只需要先查找到该节点,然后修改该节点的数据即可,实现代码如下:

public void set(int index, T data) {
    if (index < 0 || index >= size) {
        throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
    }
    Node<T> node = getNode(index);
    node.setData(data);
}

双向链表示例

以下是双向链表的两个示例,一个是将学生成绩按从高到低的顺序插入到链表中,另一个是将链表中的重复节点删除。

将学生成绩按从高到低的顺序插入到链表中

public static void insertSort(LinkList<Integer> linkList, Integer[] scores) {
    for (Integer score : scores) {
        Node<Integer> node = linkList.tail;
        while (node != null && score > node.getData()) {
            node = node.getPrev();
        }
        if (node == null) {
            linkList.add(0, score);
        } else if (node == linkList.tail) {
            linkList.add(linkList.size(), score);
        } else {
            linkList.add(linkList.indexOf(node.getData()), score);
        }
    }
}

public static void main(String[] args) {
    LinkList<Integer> linkList = new LinkList<>();
    Integer[] scores = {85, 78, 92, 60, 76};
    insertSort(linkList, scores);
    System.out.println(linkList);
}

删除双向链表中的重复节点

public void removeRepeated() {
    Node<T> node = head;
    while (node != null) {
        Node<T> next = node.getNext();
        while (next != null) {
            if (node.getData().equals(next.getData())) {
                remove(indexOf(next.getData()));
            }
            next = next.getNext();
        }
        node = node.getNext();
    }
}

public static void main(String[] args) {
    LinkList<String> linkList = new LinkList<>();
    linkList.add("A");
    linkList.add("B");
    linkList.add("C");
    linkList.add("B");
    linkList.add("D");
    linkList.add("C");
    linkList.add("E");
    linkList.add("F");
    linkList.add("A");
    linkList.add("E");
    linkList.removeRepeated();
    System.out.println(linkList);
}

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构与算法学习之双向链表 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • cad背景怎么变黑

    首先,我们需要明确一下,cad背景变黑可能是由于CAD的视觉样式设置不正确或者是显卡驱动设置不正确。 以下是设置cad背景变黑的完整攻略。 步骤1:更改CAD视觉样式 示例1:使用2019版的CAD 打开CAD软件 在顶部菜单中,找到”视图”选项,点击 在”视觉样式”下拉菜单中,选择”2D线框”或者其他选项 如果需要更改背景颜色,可以在”VPROPS”命令中…

    其他 2023年4月16日
    00
  • vue3封装echarts组件最佳形式详解

    下面我会详细讲解“vue3封装echarts组件最佳形式详解”的完整攻略。 一、背景介绍 在使用Vue3框架进行开发的过程中,我们经常会使用到echarts组件来实现数据的可视化展示。但是,直接使用echarts官方提供的API进行开发,会使代码十分冗余,不利于复用和维护。因此,封装一个通用的echarts组件是十分必要的。 二、封装思路 对于封装一个通用的…

    other 2023年6月25日
    00
  • sqlserver的split

    以下是SQL Server中Split函数的完整攻略,包括Split函数的定义、用法、示例说明等内容。 1. Split函数的定义 Split函数是SQL Server中的一个字符串函数,用于将一个字符串按照指定的分隔符进行分割,并返回一个字符串数组。 2. Split函数的用法 Split函数的语法如下: STRING_SPLIT ( string , s…

    other 2023年5月10日
    00
  • 目标跟踪之卡尔曼滤波—理解Kalman滤波的使用预测

    目标跟踪之卡尔曼滤波—理解Kalman滤波的使用预测 卡尔曼滤波是一种用于估计系统状态的算法,它可以通过观测数据和系统模型来预测未来的状态。在目标跟踪中,卡尔曼滤波可以用于预测目标的位置和速度,从而实现目标跟踪。本文将介绍卡尔曼滤波的基本概念、使用方法和两个示例说明。 基本概念 1. 状态空间模型 卡尔曼滤波是一种基于状态空间模型的算法,它将系统的状态表…

    other 2023年5月5日
    00
  • 20191031:python取反运算详解

    20191031:Python取反运算详解 Python是一种强大的编程语言,为程序员提供了丰富的运算符,包括取反运算符。在本文中,我们将探讨Python中的取反运算符几种形式和用法。 取反运算符的基本概念 取反运算符通常表示为“!”。简单来说,取反运算符会将一个布尔值从True变为False,或者从False变为True。在Python中,为了避免与比较运…

    其他 2023年3月28日
    00
  • 【原理】从零编写ili9341驱动全过程(基于arduino)

    以下是关于“从零编写ili9341驱动全过程(基于Arduino)”的完整攻略,包括基本概念、解决方法、示例说明和注意事项。 基本概念 ILI9341是一种用于TFT液晶屏的驱动芯片,可以用于显示图像和文本等内容。在Arduino中,可以通过编写驱动程序来控制ILI9341芯片,实现图像和文本的显示。ILI9341驱动程序的编写需要了解硬件电路、SPI通信协…

    other 2023年5月7日
    00
  • Java使用泛型Class实现消除模板代码

    Java中使用泛型Class可以实现消除重复的模板代码,以下是具体实现的详细攻略: 1. 定义泛型类 首先,我们需要定义一个泛型类。这个类中的操作都是针对泛型类型的。可以使用<T>来表示泛型参数,T可以是任意类型。 public class MyGenericClass<T> { private T data; public MyGe…

    other 2023年6月27日
    00
  • 关于Spring的@Autowired依赖注入常见错误的总结

    关于Spring的@Autowired依赖注入常见错误的总结 问题背景 @Autowired是Spring框架中用于进行依赖注入的关键注解。使用@Autowired注解,可以将需要的依赖自动注入到相应的字段、构造函数或者setter方法中。然而,由于@Autowired注解的使用方法和一些特性,会导致一些常见的错误出现。本攻略将总结一些常见的@Autowir…

    other 2023年6月28日
    00
合作推广
合作推广
分享本页
返回顶部