【做算法学数据结构】【链表】删除排序链表中的重复元素

news2024/11/23 3:44:41

链表

  • 链表
    • 单向链表
    • 双向链表
  • 题目
  • 代码

链表

当涉及到数据结构时,链表是一种常见且重要的数据结构。链表由一系列节点组成,每个节点包含数据和指向下一个节点的引用。相比于数组,链表的大小可以动态地增长或缩小,因为每个节点只需要存储自己的数据和指向下一个节点的引用。
在这里插入图片描述

单向链表

首先,我们需要定义一个表示链表节点的类。节点类应该包含一个数据字段和一个指向下一个节点的引用字段。可以使用泛型来使节点类更加通用。

class ListNode<T> {
    T data;
    ListNode<T> next;

    public ListNode(T data) {
        this.data = data;
        this.next = null;
    }
}

接下来,我们可以创建一个链表类,它包含对链表进行操作的方法,如插入节点、删除节点和遍历链表等。

class LinkedList<T> {
    private ListNode<T> head;

    public LinkedList() {
        this.head = null;
    }

    // 在链表头部插入节点
    public void insertAtHead(T data) {
        ListNode<T> newNode = new ListNode<>(data);
        newNode.next = head;
        head = newNode;
    }

    // 在链表尾部插入节点
    public void insertAtTail(T data) {
        ListNode<T> newNode = new ListNode<>(data);
        if (head == null) {
            head = newNode;
        } else {
            ListNode<T> current = head;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
        }
    }

    // 删除指定节点
    public void deleteNode(T data) {
        if (head == null) {
            return;
        }
        if (head.data.equals(data)) {
            head = head.next;
            return;
        }
        ListNode<T> current = head;
        while (current.next != null) {
            if (current.next.data.equals(data)) {
                current.next = current.next.next;
                return;
            }
            current = current.next;
        }
    }

    // 遍历链表并打印节点数据
    public void printList() {
        ListNode<T> current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }
}

现在,我们可以使用链表类来创建链表对象,并进行插入、删除和遍历等操作。

public class Main {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.insertAtHead(3);
        linkedList.insertAtHead(2);
        linkedList.insertAtHead(1);
        linkedList.insertAtTail(4);
        linkedList.printList(); // 输出: 1 2 3 4

        linkedList.deleteNode(2);
        linkedList.printList(); // 输出: 1 3 4
    }
}

这就是一个简单的链表教程,它展示了如何使用Java语言来实现链表的基本操作。通过理解链表的结构和使用方法,你可以在编程中灵活地应用链表来解决各种问题。

双向链表

双向链表(Doubly Linked List)是一种链表的变体,每个节点除了包含数据和指向下一个节点的引用外,还包含指向前一个节点的引用。这使得双向链表可以在正向和反向两个方向上遍历。

下面是一个用Java语言描述双向链表的简单教程:

首先,我们需要定义一个表示双向链表节点的类。节点类应该包含一个数据字段,一个指向前一个节点的引用字段和一个指向后一个节点的引用字段。同样,可以使用泛型来使节点类更加通用。

class DoublyListNode<T> {
    T data;
    DoublyListNode<T> prev;
    DoublyListNode<T> next;

    public DoublyListNode(T data) {
        this.data = data;
        this.prev = null;
        this.next = null;
    }
}

接下来,我们可以创建一个双向链表类,它包含对双向链表进行操作的方法,如插入节点、删除节点和遍历链表等。

class DoublyLinkedList<T> {
    private DoublyListNode<T> head;

    public DoublyLinkedList() {
        this.head = null;
    }

    // 在链表头部插入节点
    public void insertAtHead(T data) {
        DoublyListNode<T> newNode = new DoublyListNode<>(data);
        if (head == null) {
            head = newNode;
        } else {
            newNode.next = head;
            head.prev = newNode;
            head = newNode;
        }
    }

    // 在链表尾部插入节点
    public void insertAtTail(T data) {
        DoublyListNode<T> newNode = new DoublyListNode<>(data);
        if (head == null) {
            head = newNode;
        } else {
            DoublyListNode<T> current = head;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
            newNode.prev = current;
        }
    }

    // 删除指定节点
    public void deleteNode(T data) {
        if (head == null) {
            return;
        }
        if (head.data.equals(data)) {
            head = head.next;
            if (head != null) {
                head.prev = null;
            }
            return;
        }
        DoublyListNode<T> current = head;
        while (current != null) {
            if (current.data.equals(data)) {
                current.prev.next = current.next;
                if (current.next != null) {
                    current.next.prev = current.prev;
                }
                return;
            }
            current = current.next;
        }
    }

    // 遍历链表并打印节点数据
    public void printList() {
        DoublyListNode<T> current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }
}

现在,我们可以使用双向链表类来创建双向链表对象,并进行插入、删除和遍历等操作。

public class Main {
    public static void main(String[] args) {
        DoublyLinkedList<Integer> doublyLinkedList = new DoublyLinkedList<>();
        doublyLinkedList.insertAtHead(3);
        doublyLinkedList.insertAtHead(2);
        doublyLinkedList.insertAtHead(1);
        doublyLinkedList.insertAtTail(4);
        doublyLinkedList.printList(); // 输出: 1 2 3 4

        doublyLinkedList.deleteNode(2);
        doublyLinkedList.printList(); // 输出: 1 3 4
    }
}

这就是一个简单的双向链表,它展示了如何使用Java语言来实现双向链表的基本操作。通过理解双向链表的结构和使用方法,你可以在编程中灵活地应用双向链表来解决各种问题。

题目

在这里插入图片描述
ListNode

public class ListNode {

    int val;
    ListNode next;
    ListNode prev;

    public ListNode() {
    }

    ListNode(int x) {
        val = x;
        next = null;
    }

    ListNode(int x, ListNode next) {
        val = x;
        this.next = next;
    }

    ListNode(int x, ListNode next, ListNode prev) {
        val = x;
        this.next = next;
        this.prev = prev;
    }
}

代码

在这里插入图片描述

public ListNode deleteDuplicates(ListNode head) {
    if (head == null) {
        return null;
    }

    ListNode dummy = new ListNode(0, head);
    ListNode cur = dummy;
    while (cur.next != null && cur.next.next != null) {
        if (cur.next.val == cur.next.next.val) {
            cur.next = cur.next.next;
        } else {
            cur = cur.next;
        }
    }

    return dummy.next;
}

现在要把所有的重复元素全部删除要怎么做?

public ListNode deleteDuplicatesAll(ListNode head) {
    if (head == null) {
        return null;
    }

    ListNode dummy = new ListNode(0, head);
    ListNode cur = dummy;
    while (cur.next != null && cur.next.next != null) {
        if (cur.next.val == cur.next.next.val) {
            cur.next = cur.next.next;
            int x = cur.next.val;
            while (cur.next != null && cur.next.val == x) {
                cur.next = cur.next.next;
            }
        } else {
            cur = cur.next;
        }
    }

    return dummy.next;
}

这段代码实现了删除链表中所有重复的节点。下面是对代码的解析和题解:

  1. 首先,检查链表头节点是否为空,如果为空,则直接返回空链表。
  2. 创建一个虚拟节点 dummy,将其指向头节点 head。这样做是为了方便处理头节点可能被删除的情况。
  3. 使用指针 cur 来遍历链表,初始时指向虚拟节点 dummy
  4. 在循环中,检查当前节点 cur 的下一个节点 cur.next 和下下个节点 cur.next.next 是否存在。如果两个节点的值相等,说明存在重复节点。
  5. cur.next 指向下下个节点 cur.next.next,跳过重复节点。
  6. 使用变量 x 记录被删除的重复节点的值,然后继续循环,删除链表中所有与 x 相等的节点。
  7. 如果 cur.nextcur.next.next 的值不相等,说明当前节点没有重复,将 cur 移动到下一个节点。
  8. 循环结束后,返回虚拟节点 dummy 的下一个节点作为新的链表头节点。

这段代码的时间复杂度为 O ( n ) O(n) O(n),其中 n n n 是链表的长度,因为我们只遍历了一次链表。空间复杂度为 O ( 1 ) O(1) O(1),只使用了常数级别的额外空间。
可以有效地删除链表中所有重复的节点,保留不重复的节点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1606288.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

风力发电自动化控制系统中的智能化技术应用研究

风力发电自动化控制系统中的智能化技术应用研究 随碳中和目标的提出和执行&#xff0c;风能发电作为新能源行业的核心部分&#xff0c;步入了它的黄金发展期。由于风能资源具有间歇性、随机性等特点&#xff0c;这给风电的高效利用带来了巨大挑战。为了增强风力发电系统的工作效…

枚举类型介绍

1.如果一个变量只有几种可能的值&#xff0c;比如星期几 son,mon,tus,wed,fri,sat 列表中的名字&#xff0c;可以自己定义&#xff0c;无需像变量一样去申请 枚举变量&#xff1a;只限列表中的情况&#xff0c;值默认从0开始&#xff0c;枚举元素不能被赋值&#xff0c;可以指定…

OpenCV从入门到精通实战(七)——探索图像处理:自定义滤波与OpenCV卷积核

本文主要介绍如何使用Python和OpenCV库通过卷积操作来应用不同的图像滤波效果。主要分为几个步骤&#xff1a;图像的读取与处理、自定义卷积函数的实现、不同卷积核的应用&#xff0c;以及结果的展示。 卷积 在图像处理中&#xff0c;卷积是一种重要的操作&#xff0c;它通过…

JAVA网络编程、项目验证码实现

什么是网络编程? 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输。 应用场景&#xff1a;即时通信、网游对战、金融证券、国际贸易、邮件、等等 不管是什么场景&#xff0c;都是计算机跟计算机之间通过网络进行数据传输 Java中可以使用ja…

面试经典算法系列之二叉树17 -- 验证二叉树

面试经典算法32 - 验证二叉树 LeetCode.98 公众号&#xff1a;阿Q技术站 问题描述 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当…

浅写个登录(无js文件)

全部代码如下&#xff0c;无需编写wxss文件&#xff0c;渲染都在style里面&#xff1a; <view style"height: 250rpx;width: 100%;"> <!-- 背景图片 --><view style"position: absolute; background-color: antiquewhite; height: 250rpx;width…

OpenHarmony UI动画-recyclerview_animators

简介 带有添加删除动画效果以及整体动画效果的list组件库 下载安装 ohpm install ohos/recyclerview-animatorsOpenHarmony ohpm 环境配置等更多内容&#xff0c;请参考如何安装OpenHarmony ohpm 包 使用说明 引入组件库 import { RecyclerView } from "ohos/recycler…

RabbitMQ项目实战(一)

文章目录 RabbitMQ项目实战选择客户端基础实战 前情提要&#xff1a;我们了解了消息队列&#xff0c;RabbitMQ的入门&#xff0c;交换机&#xff0c;以及核心特性等知识&#xff0c;现在终于来到了激动人心的项目实战环节&#xff01;本小节主要介绍通过Spring Boot RabbitMQ S…

UI5 快速入门教程

环境准备 node >16.8 ,VSCode&#xff0c;官方网址 开始 创建一个根文件夹&#xff0c;根文件中创建一个package.json文件 {"name": "quickstart-tutorial","private": true,"version": "1.0.0","author":…

吴恩达<用于LLM应用程序开发的LangChain> L1-Model_prompt_parser

问题预览/关键词 课程地址如何获取openAI的API Key如何根据日期设置不同模型?如何调用OpenAI的API?如何使用OpenAI的API&#xff1f;langchain如何抽象OpenAI的API接口&#xff1f;langchain如何创建提示词模板并查看模板内容&#xff1f;langchain如何使用提示词模板生成提…

【opencv】dnn示例-vit_tracker.cpp 使用OpenCV库和ViTTrack模型实现的视频追踪程序

这段代码是一个使用OpenCV库和ViTTrack模型实现的视频追踪程序。程序通过摄像头或视频文件获取图像序列&#xff0c;并对选定的目标对象进行实时追踪。 代码主要分为以下几个部分&#xff1a; 导入必要的库&#xff1a;程序开始时先导入了iostream&#xff0c;cmath以及相关Ope…

每日两题 / 53. 最大子数组和 56. 合并区间(LeetCode热题100)

53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 经典dp题&#xff0c;dp[i]表示以nums[i]为结尾的所有子数组中&#xff0c;最大的和 将i从左到右遍历&#xff0c;考虑dp[i]如何维护&#xff1f; 以nums[i]结尾的子数组只有两种情况&#xff0c;子数组只有nums[i]…

[Qt网络编程]之UDP通讯的简单编程实现

hello&#xff01;欢迎大家来到我的Qt学习系列之网络编程之UDP通讯的简单编程实现。希望这篇文章能对你有所帮助&#xff01;&#xff01;&#xff01; 本篇文章的相关知识请看我的上篇文章: http://t.csdnimg.cn/UKyeM 目录 UDP通讯 基于主窗口的实现 基于线程的实现 UDP通讯…

小黄脸404自动跳转源码

源码介绍 小黄脸404自动跳转源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 效果截图 源码下载 小黄脸404自动跳转…

网络防火墙技术知多少?了解如何保护您的网络安全

在当前以网络为核心的世界中&#xff0c;网络安全成为了至关重要的议题。网络防火墙是一种常见的保护网络安全的技术&#xff0c;用于监控和控制网络流量&#xff0c;阻止未经授权的访问和恶意活动。今天德迅云安全就带您了解下防火墙的一些相关功能和类型。 防火墙的五个功能…

【EI会议征稿通知】2024年图像处理、机器学习与模式识别国际学术会议(IPMLP 2024)

2024年图像处理、机器学习与模式识别国际学术会议&#xff08;IPMLP 2024) 2024 International Conference on Image Processing, Machine Learning and Pattern Recognition 重要信息 大会官网&#xff1a;www.ipmlp.net&#xff08;点击参会/投稿/了解会议详情&#xff09;…

【操作系统】(Operator System)

目录 1.概念2.系统调用和库函数 1.概念 1.操作系统是什么呢&#xff1f;&#xff1f; 操作系统是一款进行软硬件资源管理的软件&#xff0c;我们打开电脑第一个加载的软件就是操作系统。 2.为什么要有操作系统呢&#xff1f; 操作系统通过将软硬件资源管理好(手段)&#xf…

axios的封装理解和基本使用

axios的配置 ruoyi的前端对axios进行了封装&#xff0c;让我们发get请求或者是post请求更加方便了。 ruoyi对axios的封装在下面文件中&#xff1a;打开文件&#xff0c;可以看到它有三个显眼的方法&#xff0c;分别是request拦截器、response拦截器和通用下载方法。ruoYi接口地…

ansible进阶-剧本调试方法

目录 1、调试方法 2、实例 2.1 Debug模块 2.2 tags标签 2.3 忽略错误 1、调试方法 具体方法应用场景debug标签执⾏剧本的时候输出剧本的执⾏流程,⼀般配合register⼀起使⽤. 输出facts变量自定义变量tags标签给⼀些模块加上标签,运⾏剧本的时候可以运⾏指定标签的…

基于非线性控制策略的电力电子电路——DC-DC电路的3种滑模控制器【MATLAB/simulink】

第一种&#xff0c;滞环滑模控制器Buck电路 在滑模控制系统中&#xff0c;采用滞环技术&#xff0c;直接将切换函数转换成开关控制信号&#xff0c;滞环技术被看做一种降低系统结构的切换频率的调制方法&#xff0c;业界也把基于滞环滑模技术实现的滑模控制称为直接滑模控制技…