【经典算法】Leetcode.83删除排序链表中的重复元素(Java/C/Python3/Go实现含注释说明,Easy)

  • 标签:链表

题目描述

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

原题:LeetCode 83

思路及实现

方式一:双指针

思路

使用快慢双指针遍历链表,快指针用于遍历链表,慢指针用于指向不重复元素的最后一个位置。当快指针指向的元素与慢指针指向的元素不同时,将慢指针向后移动一位,并将快指针指向的元素赋值给慢指针指向的位置。这样可以保证慢指针之前的元素都是不重复的。

代码实现

Java版本
// 定义链表节点
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        ListNode slow = head;
        ListNode fast = head.next;
        
        while (fast != null) {
            if (slow.val != fast.val) {
                slow.next = fast;
                slow = slow.next;
            }
            fast = fast.next;
        }
        
        // 最后一个不重复元素后面应该为null
        slow.next = null;
        
        return head;
    }
}

说明:
代码中定义了一个链表节点类ListNode,并在Solution类中实现了deleteDuplicates方法。首先判断链表是否为空或只有一个节点,若是则直接返回。然后初始化快慢指针,快指针指向头节点的下一个节点。在循环中,当快慢指针指向的元素不同时,将慢指针的next指向快指针,并将慢指针向后移动一位。最后,将最后一个不重复元素的next置为null,返回头节点。

C语言版本
// 定义链表节点
struct ListNode {
    int val;
    struct ListNode *next;
};

struct ListNode* deleteDuplicates(struct ListNode* head) {
    if (head == NULL || head->next == NULL) {
        return head;
    }
    
    struct ListNode *slow = head;
    struct ListNode *fast = head->next;
    
    while (fast != NULL) {
        if (slow->val != fast->val) {
            slow->next = fast;
            slow = slow->next;
        }
        fast = fast->next;
    }
    
    slow->next = NULL;
    
    return head;
}

说明:
C语言版本的实现与Java版本类似,只是语法上有所差异。

Python3版本
# 定义链表节点
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        slow = head
        fast = head.next
        
        while fast:
            if slow.val != fast.val:
                slow.next = fast
                slow = slow.next
            fast = fast.next
        
        slow.next = None
        
        return head

说明:
Python版本的实现中,使用了类定义链表节点,并实现了deleteDuplicates方法。与Java和C语言版本的逻辑相同。

Golang版本
package main

import "fmt"

// 定义链表节点
type ListNode struct {
    Val  int
    Next *ListNode
}

func deleteDuplicates(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    
    slow := head
    fast := head.Next
    
    for fast != nil {
        if slow.Val != fast.Val {
            slow.Next = fast
            slow = slow.Next
        }
        fast = fast.Next
    }
    
    slow.Next = nil
    
    return head
}

func main() {
    // 示例链表: 1->1->2
    head:= &ListNode{Val: 1}
    head.Next = &ListNode{Val: 1}
    head.Next.Next = &ListNode{Val: 2}

    result := deleteDuplicates(head)

    // 打印结果链表
    for result != nil {
        fmt.Print(result.Val, " ")
        result = result.Next
    }
}

说明:
Golang版本的实现中,首先定义了一个ListNode结构体来表示链表节点。然后在deleteDuplicates函数中实现了删除重复元素的功能。最后,在main函数中创建了一个示例链表,并调用deleteDuplicates函数进行处理,最后遍历结果链表并打印。

方式二(递归)

在处理链表删除重复元素的问题时,通过递归的方式能够简洁地表达算法逻辑。以下是方式二的详细解释和示例代码。

思路

对于递归方法,我们需要定义一个递归函数,该函数接受链表的头节点作为参数,并返回处理后的链表的头节点。递归函数的主要任务是判断当前节点是否与其下一个节点重复,并据此决定是继续递归还是删除下一个节点。

  1. 基本情况:如果链表为空(head == null)或只有一个节点(head.next == null),则没有重复元素可删除,直接返回头节点。

  2. 递归情况

    • 如果当前节点head的值与其下一个节点head.next的值相同,说明有重复元素,我们需要删除下一个节点。递归调用deleteDuplicates函数处理head.next,并返回处理后的头节点,此时原head节点将被忽略(因为重复)。
    • 如果当前节点head的值与其下一个节点head.next的值不同,说明没有重复元素,我们保留head节点,并递归处理head.next。将处理后的head.next赋值给head.next,并返回head

代码实现

以下是使用递归方法删除链表重复元素的示例代码,分别用Java、Python和Golang实现。

Java版本
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

public class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        
        if (head.val == head.next.val) {
            return deleteDuplicates(head.next);
        } else {
            head.next = deleteDuplicates(head.next);
            return head;
        }
    }
}
C语言

下面是使用C语言实现删除排序链表中重复元素的递归解法:

#include <stdio.h>
#include <stdlib.h>

// 定义链表节点结构体
typedef struct ListNode {
    int val;
    struct ListNode *next;
} ListNode;

// 创建新节点
ListNode* createNode(int val) {
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    newNode->val = val;
    newNode->next = NULL;
    return newNode;
}

// 递归删除重复元素
ListNode* deleteDuplicates(ListNode* head) {
    // 基本情况:空链表或只有一个节点,没有重复元素
    if (head == NULL || head->next == NULL) {
        return head;
    }

    // 如果头节点和下一个节点值相同,递归删除下一个节点
    if (head->val == head->next->val) {
        return deleteDuplicates(head->next);
    }
    // 如果头节点和下一个节点值不同,递归处理下一个节点,并更新头节点的next指针
    else {
        head->next = deleteDuplicates(head->next);
        return head;
    }
}

// 打印链表
void printList(ListNode* head) {
    ListNode* current = head;
    while (current != NULL) {
        printf("%d ", current->val);
        current = current->next;
    }
    printf("\n");
}

// 释放链表内存
void freeList(ListNode* head) {
    ListNode* current = head;
    while (current != NULL) {
        ListNode* temp = current;
        current = current->next;
        free(temp);
    }
}
}

说明
在上面的代码中,createNode 函数用于创建新链表节点,deleteDuplicates 函数是递归删除重复元素的实现,printList 函数用于打印链表,freeList 函数用于释放链表占用的内存。

Python3版本
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        
        if head.val == head.next.val:
            return self.deleteDuplicates(head.next)
        else:
            head.next = self.deleteDuplicates(head.next)
            return head
Golang版本
type ListNode struct {
    Val  int
    Next *ListNode
}

func deleteDuplicates(head *ListNode) *ListNode {
    if head == nil || head.Next == nil {
        return head
    }
    
    if head.Val == head.Next.Val {
        return deleteDuplicates(head.Next)
    } else {
        head.Next = deleteDuplicates(head.Next)
        return head
    }
}

复杂度分析

对于递归解法,复杂度分析需要考虑递归调用的次数和递归过程中使用的额外空间。

  • 时间复杂度:O(n),其中n是链表的长度。每个节点最多被访问一次,因此时间复杂度是线性的。
  • 空间复杂度:O(n),其中n是链表的长度。在最坏情况下,当链表中所有节点都重复时,递归的深度将达到n,因此需要额外的栈空间来存储递归调用信息。

总结

以下是针对删除排序链表中重复元素问题的不同方式的对比总结:

方式优点缺点时间复杂度空间复杂度
方式一(双指针迭代)不使用递归,内存占用稳定代码相对复杂,需要处理边界情况O(n)O(1)
方式二(递归)代码简洁,逻辑清晰递归深度可能导致栈溢出,内存占用不稳定O(n)O(n)

相似题目

以下是一些与删除排序链表中重复元素问题相似的题目:

相似题目难度链接
删除排序数组中的重复项简单力扣:26. 删除排序数组中的重复项
删除排序数组中的重复项 II中等力扣:80. 删除排序数组中的重复项 II
删除链表中的节点容易力扣:237. 删除链表中的节点
移除链表元素简单力扣:203. 移除链表元素
合并两个有序链表简单力扣:21. 合并两个有序链表
链表中倒数第k个节点中等力扣:19. 链表中倒数第k个节点

这些题目涉及到了链表、数组的基本操作,包括删除重复项、合并、查找特定节点等,对于理解链表和数组的基本数据结构以及操作非常有帮助。通过解决这些相似题目,可以加深对链表和数组操作的理解,并提升编程技能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构-简单队列

1.简介 队列为一个有序列表&#xff0c;可以用数组或链表来实现。 先进先出原则。先存入队列的数据先取出&#xff0c;后存进队列的数据后取出。 这里对比一下&#xff0c;栈是后来者居上 下面使用数组来模拟队列&#xff0c;用数组的结构来存储队列的数据&#xff1a; Que…

Stable Diffusion教程:额外功能/后期处理/高清化

"额外功能"对应的英文单词是Extras&#xff0c;算是直译。但是部分版本中的翻译是“后期处理”或者“高清化”&#xff0c;这都是意译&#xff0c;因为它的主要功能是放大图片、去噪、修脸等对图片的后期处理。注意这里边对图片的处理不是 Stable Diffusion 本身的能…

微软开源了 MS-DOS 4.00

DOS的历史源远流长&#xff0c;很多现在的年轻人不知道DOS了。其实早期的windows可以看做是基于DOS的窗口界面的模拟器&#xff0c;系统的本质其实是DOS。后来DOS的漏洞还是太多了&#xff0c;微软重新写了windows的底层内核。DOS只是一个辅助终端的形式予以保留了。 微软是在…

FreeRTOS学习——FreeRTOS队列(上)

本篇文章记录我学习FreeRTOS队列的相关知识&#xff0c;主要包括队列简介、队列的结构体、队列创建等知识。 队列是为了任务与任务、任务与中断之间的通信而准备的&#xff0c;可以在任务与任务、任务与中断之间传递消息&#xff0c;队列中可以存储有限的、大小固定的数据项目。…

大白菜启动U盘想格式化但格式化不了

部分区域被修改分区表保护起来了。直接格式化的话&#xff0c;里面的文件夹都还在。根本格式化不了。特别是可用容量并未还原出来。 进入计算机管理》磁盘管理&#xff0c;看到U盘盘符。别搞错了。删除掉里面的已经分的区域和未分区区域&#xff0c;让它还原成一个整体。退出。…

分类预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多特征分类预测

分类预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多特征分类预测 目录 分类预测 | Matlab实现POA-BP鹈鹕算法优化BP神经网络多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现POA-BP鹈鹕算法优化BP神经网络多特征分类预测&#xff08;Matlab实…

javaweb学习week6

javaweb学习 九.登录认证 5.登录后下发令牌 生成令牌&#xff1a;引入JWT令牌操作工具类&#xff0c;登录完成后&#xff0c;调用工具类生成JWT令牌&#xff0c;并返回 代码实例&#xff1a; 6.Filter入门 概念&#xff1a;Filter过滤器&#xff0c;是Javaweb三大组件之一…

在STM32上实现无线传感器网络节点

引言 无线传感器网络&#xff08;WSN&#xff09;是物联网&#xff08;IoT&#xff09;技术的关键组成部分&#xff0c;广泛应用于环境监测、智能建筑、精密农业等领域。 本教程将介绍如何在STM32微控制器上设计和实现一个无线传感器网络节点&#xff0c;包括硬件选择、网络协…

企业计算机服务器中了helper勒索病毒怎么办?Helper勒索病毒解密处理流程

网络技术的不断发展与成熟&#xff0c;为企业的生产运营提供了极大便利&#xff0c;让企业的发展速度大大提升&#xff0c;但网络毕竟是虚拟服务系统&#xff0c;虽然可以为企业提供便利&#xff0c;但也会给企业数据安全带来严重威胁。近日&#xff0c;云天数据恢复中心接到山…

visionPro链接相机

搜索Cognex GigE Vision Configura… 修改子网掩码为255.255.255.0 配置驱动程序 更新驱动&#xff08;如果能够选择9014Bytes&#xff0c;跳过此步骤&#xff09; 更新更改 相机ip配置 打开visionPro 选择照相机 查看实时画面 运行保存图像

【论文】关于网页上能打开的文章下载PDF“显示无效或损坏的 PDF 文件”的解决办法

1. 遇到的问题 今天我在 dl.acm.org/doi 下载论文时发现下载后的pdf打开出现“显示无效或损坏的 PDF 文件” 可是在原网址是可以打开并显示的 2. 解决方案 这里我用到了和之前【论文】去除PDF论文行号的完美解决方案 的相似的解决办法 就是下载的时候不直接下载&#xf…

【java9】java9新特性之接口的私有方法

在Java 9中&#xff0c;接口可以包含私有方法&#xff08;包括静态私有方法和实例私有方法&#xff09;。这允许接口的设计者创建一些辅助方法&#xff0c;这些方法只能被接口中的其他方法所使用&#xff0c;而不能被实现该接口的类直接访问。 Java7 Java7及之前 &#xff0c…

文件缓冲区

为什么要有文件缓冲区的存在&#xff1f; 假设甲在云南&#xff0c;甲的朋友乙在北京&#xff0c;甲想给乙送个东西就需要跑到北京去&#xff1a;这时候有菜鸟驿站了&#xff0c;甲就不用跑了&#xff0c;直接把包裹交给菜鸟驿站就可以了。缓冲区就类似于菜鸟驿站&#xff0c;…

【vscode环境配置系列】vscode远程debug配置

VSCODE debug环境配置 插件安装配置文件debug 插件安装 安装C/C, C/C Runner 配置文件 在项目下建立.vscode文件夹&#xff0c;然后分别建立c_cpp_properties.json&#xff0c; launch.json&#xff0c;tasks.json&#xff0c;内容如下&#xff1a; c_cpp_properties.json:…

Dockerfile实战(SSH、Systemctl、Nginx、Tomcat)

目录 一、构建SSH镜像 1.1 dockerfile文件内容 1.2 生成镜像 1.3 启动容器并修改root密码 二、构建Systemctl镜像 2.1 编辑dockerfile文件 ​编辑2.2 生成镜像 2.3 启动容器&#xff0c;并挂载宿主机目录挂载到容器中&#xff0c;然后进行初始化 2.4 进入容器验证 三、…

进程的概念(2)

进程优先级 1.什么的优先级 概念&#xff1a;指定进程获取某种资源&#xff08;CPU&#xff09;的先后顺序 本质&#xff1a;优先级的本质是优先级数字的大小&#xff0c;Linux中优先级数字越小&#xff0c;优先级越高 task_struct 进程控制快-> struct -> 内部字段 -&g…

《从Paxos到Zookeeper》——第四、七章:基本概念及原理

目录 第四章 Zookeeper与Paxos 4.1 Zk是什么 4.1.1 Zk特性 4.1.2 Zk基本概念 4.1.2.1 集群角色(Follower, Leader, Observer) 4.1.2.2 数据模型 4.1.2.3 ZNode(数据节点) 4.1.2.4 Session(会话) 4.1.2.5 ACL&#xff08;Access Control Lists&#xff09; 4.1.2.6 Watcher(事件…

测试开发 | 相比 Selenium,Web 自动化测试框架 Playwright 有哪些强大的优势?

Playwright 是由微软的研发团队所开发的一款 Web 自动化测试框架&#xff0c;这个框架具有多平台、跨语言的特点。除了基本的自动化测试能力之外&#xff0c;同时它还具备非常强大的录制功能、追踪功能。以下是 Playwright 与 Selenium 的对比。 ​ 由此可见&#xff0c;Play…

HTML5(2)

目录 一.列表、表格、表单 1.列表标签 2.表格 4.无语义的布局标签 5.字符实体 6.综合案例--1 7.综合案例--表单 一.列表、表格、表单 1.列表标签 1.1 无序列表 1.2 有序列表 1.3 定义列表 定义列表一般用于网页底部的帮助中心 2.表格 2.1 2.2 表格结构标签 shiftaltf 格…

chrome 安装devtools

chrome 安装devtools 下载安装 链接&#xff1a;https://github.com/vuejs/devtools 选择对应版本&#xff1a; 安装yarn 下载 npm install -g yarn --registryhttps://registry.npmmirror.com进入下载的目录安装依赖 yarn install --registryhttps://registry.npmmirror.…