【每日一题】2015考研数据结构 - 求不重复的链表元素

【每日一题】2015考研数据结构 - 求不重复的链表元素

在单链表中存储了 m 个整数,每个节点由两部分组成:[data][link],其中 data 是整数,且满足 |data| < nn 为正整数)。
现要求设计一个高效的算法来处理链表中 data 绝对值相等的节点,只保留首次出现的节点,删除其余绝对值相等的节点。

例如,对于以下链表:

1 -> 2 -> -2 -> 3 -> null 

经过处理后,得到的链表为:

1 -> 2 -> 3 -> null 

解题思路

本题的关键在于高效去重,即在链表中保留首次出现的数值对应的节点,而删除其他绝对值相等的节点。可以借助 哈希表(unordered_set 来记录已经出现过的节点绝对值。这样,我们可以在遍历链表的同时,实时判断是否存在绝对值重复的节点。

如果你不知道什么是set与实践复杂度的话可参考以下文章C++ 新手指南:如何使用 set 和 unordered_set【数据结构】时间复杂度和空间复杂度是什么?

具体思路如下:

  1. 从链表头开始遍历,使用一个哈希表 sets 来记录出现过的节点绝对值。
  2. 如果当前节点的绝对值没有出现在 sets 中,则将该节点的绝对值插入 sets,并继续遍历。
  3. 如果当前节点的绝对值已经出现在 sets 中,说明该节点为重复节点,删除该节点并更新链表结构。
  4. 最后得到一个不含重复绝对值节点的链表。

代码实现

数据结构定义

首先定义链表节点的数据结构:

structNode{int value;// 节点的数值 Node* next;// 指向下一个节点的指针};

算法实现

按照上述思路,以下是完整的 C++ 代码实现:

#include"bits/stdc++.h"usingnamespace std;// 链表节点结构structNode{int value; Node* next;};// 去除链表中绝对值相同的节点,仅保留首次出现的节点voidsearch(Node* node){ unordered_set<int> sets;// 用于存储出现过的节点绝对值 Node* prev = node;// 记录上一个节点while(node !=nullptr){// 判断当前节点的绝对值是否在哈希集合中if(sets.find(abs(node->value))== sets.end()){ sets.insert(abs(node->value));// 插入当前节点的绝对值 prev = node;// 更新前驱节点 node = node->next;// 移动到下一个节点}else{// 如果绝对值已存在,删除当前节点 prev->next = node->next; Node* n = node; node = node->next;free(n);// 释放删除的节点}}}

测试代码

测试代码如下,构建了一个示例链表并调用 search 函数对其进行去重操作:

intmain(){ Node* head =new Node{1,new Node{2,new Node{-2,new Node{3,nullptr}}}}; Node* cur = head; cout <<"原链表: ";while(cur !=nullptr){ cout << cur->value <<" "; cur = cur->next;} cout << endl;search(head); cur = head; cout <<"去重后的链表: ";while(cur !=nullptr){ cout << cur->value <<" "; cur = cur->next;} cout << endl;return0;}

代码讲解

  1. 数据结构定义:定义 Node 结构体,包含一个 value 和一个 next 指针,分别表示节点的数值和下一个节点的地址。
  2. 去重逻辑
    • 利用哈希集合 sets 来存储已访问的绝对值。在遍历过程中,检查 sets 中是否存在当前节点的绝对值。
    • 如果不存在,则将绝对值加入集合并继续遍历。
    • 如果存在,则说明当前节点重复,通过调整前驱节点 prev 的指针,跳过该节点并释放其内存。
  3. 时间复杂度:由于哈希表的插入、查找操作平均复杂度为 O(1),因此整体算法的时间复杂度为 O(m),其中 m 是链表的节点个数。
  4. 空间复杂度:由于使用了一个哈希集合来记录绝对值,最坏情况下需要 O(m) 的空间。

复杂度分析

  1. 时间复杂度:遍历链表的复杂度为 O(m),每次检查和插入哈希表的时间复杂度为 O(1),因此总的时间复杂度为 O(m)
  2. 空间复杂度:使用了一个 unordered_set 记录已访问的绝对值,因此空间复杂度为 O(m)

与其他方法的比较

另一种可能的思路是,使用两重循环遍历链表并删除重复节点。但这种方法的时间复杂度为 O(m^2),效率较低。而本算法通过哈希表实现了 O(m) 的时间复杂度,更适合大规模链表的数据处理。

总结

本文介绍了如何在链表中去除绝对值相等的节点,仅保留首次出现的节点,并通过哈希表优化了时间复杂度。在处理去重问题时,哈希表是非常实用的数据结构,可以显著提高算法效率。

通过本题,可以进一步加深对链表操作和哈希表使用的理解,为后续更复杂的数据结构题目打下基础。

Read more

【C++】哈希扩展——位图和布隆过滤器的介绍与实现

【C++】哈希扩展——位图和布隆过滤器的介绍与实现

各位读者大佬好,我是落羽!一个坚持不断学习进步的学生。 如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步! 也欢迎关注我的blog主页:落羽的落羽 文章目录 * 一、位图 * 1. 概念与实现 * 2. std::bitset * 二、布隆过滤器 * 1. 概念 * 2. 布隆过滤器误判率数学推导 * 3. 实现 一、位图 1. 概念与实现 在许多公司的面试题中会考到这样的场景:给40亿个不重复无符号整数,如何快速判断一个数是否在这40亿数中。 如果使用常规思路,每次查询暴力遍历O(N)太慢,排序+二分查找O(NlogN)+O(logN),内存不足以放下这些数据。 数据是否在给定的整型数据中,结果是在或不在,正好是两种状态,那么可以用一个二进制比特位来代表数据是否存在的信息,比特位为1代表存在,比特位为0代表不在。那么,我们可以设计一个用比特位表示数据是否存在的数据结构——位图!

By Ne0inhk
排序(数据结构)

排序(数据结构)

一. 排序概念及运用 排序在数据结构中是非常重要的一部分,所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 在生活中也有很多的应用,比如当我们搜索一款产品时候,我们可以选择按销量多少的顺序来给我们推荐产品,也可以按照价格高低来给我们推荐产品,所以排序在生活中也是很常见的。 1.1插入排序 (1)直接插入排序 上面就是一些常见的排序算法,首先我们来认识一下插入排序,插入排序又分为直接插入排序和希尔排序,直接插入排序是比较好理解的,比如我们日常生活中的扑克牌游戏,当我们拿到牌的时候我们会习惯性的直接将牌按我们想要的顺序排列,如下:   那么希尔排序又是怎么回事呢? 我还是用一张清晰的思路图来向大家展示: void InitSort(int* arr, int n) { for (int i = 0; i < n-1; i++) { int end = i; int tmp = arr[end + 1]; while (end >

By Ne0inhk
2026 前端 / 后端 / 算法岗 AI 技能清单,直接对标大厂

2026 前端 / 后端 / 算法岗 AI 技能清单,直接对标大厂

2026 大厂前端岗 AI 技能清单 核心基础技能 * 大模型前端适配能力:掌握大模型上下文管理,实现对话历史的高效存储与加载,适配流式输出的前端渲染逻辑。 * AI 组件开发:熟练开发基于大模型的智能组件,如代码补全、智能问答、内容生成类组件,支持参数化配置与多模型切换。 * 向量数据库集成:掌握 Pinecone、Weaviate 等向量数据库的前端调用方法,实现语义搜索、相似内容推荐等功能。 进阶实践技能 * 大模型微调适配:理解大模型微调原理,能够基于前端业务场景,将微调后的模型部署至前端环境,实现模型轻量化调用。 * 多模态交互开发:支持文本、图像、音频等多模态输入的前端处理,对接多模态大模型 API 实现智能交互。 * AI 性能优化:实现大模型请求的批量处理、缓存复用与增量更新,降低前端请求延迟与资源消耗。 实战代码示例 以下为基于 OpenAI API 实现的流式对话前端组件,使用 React 18 开发:

By Ne0inhk
【数据结构-初阶】详解线性表(3)---双链表

【数据结构-初阶】详解线性表(3)---双链表

🎈主页传送门:良木生香 🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》 🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离 目录 1、双链表的概念 2、双链表的基本实现 2.1、双向链表节点的创建 2.2、双向链表的初始化 2.3、双向链表长度的计算 2.4、双向链表的插入操作: 2.4.1、头部插入: 2.4.2、尾部插入: 2.4.3、查找pos位置的元素: 2.4.4、pos位置插入: 2.4.4.1、pos位置之前插入:

By Ne0inhk