优选算法——链表

 💁‍♂️个人主页:进击的荆棘

👇作者其它专栏:

《数据结构与算法》《算法》《C++起始之路》

相关题解

1.两数相加

算法思路(模拟):

两个链表都是逆序存储数字的,即两个链表的个位数、十位数等都已经对应,可以直接相加。在相加过程中,我们要注意是否产生进位,产生进位时需要将进位和链表数字一同相加。若产生进位的位置在链表底部,即答案位数比原链表位数长一位,还需要再new一个结点存储最高位。

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode* cur1=l1,*cur2=l2; ListNode* newhead=new ListNode(0);//虚拟头结点 ListNode* head=newhead;//尾指针 int t=0;//记录进位 //t中可能还保存一个进位 while(cur1||cur2||t){ if(cur1){ t+=cur1->val; cur1=cur1->next; } if(cur2){ t+=cur2->val; cur2=cur2->next; } ListNode* tmp=new ListNode(t%10); head->next=tmp; head=head->next; t/=10; } head=newhead->next; delete newhead; return head; } };

2.两两交换链表中的节点

算法思路(模拟):

由画图就可以明白。

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* swapPairs(ListNode* head) { if(!head||!head->next) return head; ListNode* newhead=new ListNode(0,head); ListNode* prev=newhead,*cur=newhead->next,*next=cur->next,*nnext=cur->next->next; while(cur&&next){ //交换结点 prev->next=next; cur->next=nnext; next->next=cur; //为修改下一对结点做准备 prev=cur; cur=nnext; if(cur) next=cur->next; if(next) nnext=next->next; } head=newhead->next; delete newhead; return head; } };

3.重排链表

算法思路:

1.找中间节点,分成两个链表

2.将第二条链表逆序

3.合并两条链表

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: void reorderList(ListNode* head) { if(!head||!head->next||!head->next->next) return ; ListNode* slow=head,*fast=head; //找到中间结点,将链表断开 while(fast&&fast->next){ slow=slow->next; fast=fast->next->next; } ListNode* head2=new ListNode(0); ListNode* cur=slow->next; slow->next=nullptr; //反转第二条链表,头插 while(cur){ //保存下一结点,防止找不到 ListNode* next=cur->next; cur->next=head2->next; head2->next=cur; cur=next; } //合并两条链表 ListNode* ret=new ListNode(0); ListNode* prev=ret; ListNode* cur1=head,*cur2=head2->next; while(cur1){ prev->next=cur1; prev=prev->next; cur1=cur1->next; if(cur2){ prev->next=cur2; cur2=cur2->next; prev=prev->next; } } delete head2; delete ret; } };

4.合并 K 个升序链表

算法思路一(堆):

合并两个有序列表是比较简单且做过的,就是用双指针依次比较链表1、链表2为排序的最小元素,选择更小的那一个加入有序的答案链表中。

合并k个升序链表时,我们依旧可以选择k个链表中,头节点最小的那一个。可以用堆这个数据结构,我们可以将所有的头节点放进一个小根堆中,这样就能快速找到每次k个链表中,最小的元素是哪个。

算法思路二(递归):

逐一比较时,答案链表越来越长,每个跟它合并的小链表的元素都需要比较很多次才可以成功排序。比如,我们有8个链表,每个链表长100。

逐一合并时,我们合并链表的长度分别为(0,100),(100,100),(200,100)(300,100),(400,100),(500,100),(600,100),(700,100)。所有链表的总长度共计3600。

若尽可能让长度相同发链表进行两两合并呢?这是合并链表的长度分别是(100,100)x4,(200,200)x2,(400,400),共计2400。比上一种的计算量整整少了1/3。

算法流程:

1.特例,若题目给出空链表,无需合并,直接返回;

2.返回递归结果。

递归函数设计:

1.递归出口:若当前要合并的链表编号范围左右值相等,无需合并,直接返回当前链表;

2.用二分思想,等额划分左右两段需要合并的链表,使这两段合并后的长度尽可能相等;

3.对左右两段分别递归,合并[l,r]范围内的链表;

4.再调用mergeTwoLists函数进行合并(就是合并两个有序链表)

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ //利用优先级队列的思想,建堆 struct cmp{ bool operator()(ListNode* l1,ListNode* l2){ return l1->val>l2->val; } }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { //创建优先级队列,建小堆 priority_queue<ListNode*,vector<ListNode*>,cmp> heap; //将所有链表的头结点入队列 for(auto l:lists){ if(l) heap.push(l); } ListNode* head=new ListNode(0); ListNode* prev=head; while(!heap.empty()){ //一直取堆顶 ListNode* t=heap.top(); heap.pop(); prev=prev->next=t; //若链表中还有结点,向后移动 if(t->next) {t=t->next;heap.push(t);} } prev=head->next; delete head; return prev; } }; //法三:分治——递归,时间复杂度O(n*k*logk) class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { return merge(lists,0,lists.size()-1); } ListNode* merge(vector<ListNode*>& lists,int left,int right){ if(left>right) return nullptr; //只有一条链表的情况 if(left==right) return lists[left]; //将数组分为两块 int mid=(left+right)>>1; ListNode* l1=merge(lists,left,mid); ListNode* l2=merge(lists,mid+1,right); //合并两个有序数组 return mergeTosort(l1,l2); } ListNode* mergeTosort(ListNode* l1,ListNode* l2){ if(!l1) return l2; if(!l2) return l1; ListNode* cur1=l1,*cur2=l2; ListNode head; ListNode* prev=&head; head.next=nullptr; while(cur1&&cur2){ if(cur1->val <= cur2->val){ prev=prev->next=cur1; cur1=cur1->next; } else{ prev=prev->next=cur2; cur2=cur2->next; } } if(cur1) prev->next=cur1; if(cur2) prev->next=cur2; return head.next; } };

5.K 个一组翻转链表

我们可以把链表按k个为一组进行分组,组内进行反转,并且记录反转后的头尾节点,使其可以和前、后连接起来。

可以先求出一共需要逆序多少组(假设逆序n组),然后重复n次长度为k的链表的逆序即可。

/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* reverseKGroup(ListNode* head, int k) { int n=0; ListNode* cur=head; while(cur){ n++; cur=cur->next; } //求出需要翻转多少组 n/=k; ListNode* newhead=new ListNode(0); ListNode* prev=newhead; cur=head; while(n--){ ListNode* tmp=cur; for(int i=0;i<k;i++){ ListNode* next=cur->next; cur->next=prev->next; prev->next=cur; cur=next; } prev=tmp; } prev->next=cur; prev=newhead->next; delete newhead; return prev; } };

Read more

Python 数据挖掘从入门到精通:回归 / 分类 / 聚类 / 关联分析完整教程

Python 数据挖掘从入门到精通:回归 / 分类 / 聚类 / 关联分析完整教程

技术点目录 * 一:Python编程【夯实基础】 * 二:特征工程 * 三:回归拟合模型 * 四:分类识别模型 * 五:聚类分析算法 * 六:关联分析算法 * 七:总结 * 了解更多 ———————————————————————————— 在数据驱动的科研与工程领域,Python 凭借其开源生态与高效编程特性,已成为数据挖掘与机器学习的首选工具。传统数据分析方法在面对高维、异构数据时,往往面临特征冗余、模型泛化能力弱等瓶颈,而机器学习算法的深度应用,正为复杂数据的价值挖掘提供全新解决方案。 当前,Python 机器学习技术已形成从数据预处理到模型落地的完整体系。通过 Numpy、Pandas 实现数据高效处理,借助 Scikit-learn、XGBoost、LightGBM 等库构建回归、分类、聚类等模型,结合遗传算法等群优化策略提升特征选择精度,可精准解决科研与工程中的预测、识别、关联分析等核心问题。技术前沿更聚焦于模型优化、创新点提炼与高水平成果转化,实现从算法应用到科学发现的跨越。 本文立足 Python

Windows系统下python新一代三方库管理工具uv及VSCode配置

Windows系统下python新一代三方库管理工具uv及VSCode配置

python新一代三方库管理工具uv uv是什么? uv是用RUST语言写的一个python三方库和项目管理工具,详见官网(uv)。 uv的安装 官网上提供了两种安装方式,第一种需要在PS终端里运行一下命令进行安装: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" 另一种的话,如果已经安装过python的话,可以直接使用pip安装,这也是本人比较推荐的方式: pip install uv 设置镜像源 虽然都说uv很快,但很多人安装了uv后,感觉uv也不是很快,这个就有点儿冤枉uv了。主要还是镜像的问题。可以参见我的另一个文章(关于anaconda的一些初级小配置)。 具体要设置的话,需要你手动在电脑的文件路径栏里输入 %APPDATA%,并在该目录下创建uv文件夹并进入。然后在uv文件夹里创建 uv.toml 文件并打开。内容为: [[index]] url = "http:

Lunar Python快速上手教程:轻松搞定农历日期转换难题

Lunar Python快速上手教程:轻松搞定农历日期转换难题 【免费下载链接】lunar-python 项目地址: https://gitcode.com/gh_mirrors/lu/lunar-python 还在为农历日期处理而头疼吗?很多开发者在处理中国传统节日、节气计算时都会遇到各种问题。公历转农历、节气查询、传统节日计算……这些看似简单的需求,在实际开发中却常常让人束手无策。 今天,我将带你深入了解Lunar Python这个强大的工具库,让你在5分钟内掌握农历日期处理的精髓! 为什么选择Lunar Python? 零依赖的轻量级解决方案 Lunar Python最大的优势在于完全基于Python标准库开发,无需安装任何第三方依赖。这意味着你可以在任何Python环境中安心使用,不用担心版本冲突或依赖问题。 多历法系统的完美集成 想象一下,一个工具就能同时处理公历、农历、佛历和道历四种历法系统。这在实际开发中意味着什么?意味着你可以: * 轻松构建支持多历法的日历应用 * 准确计算中国传统节日和节气 * 为文化类应用提供完整的日期支持 核心功能快速体

【Java篇】算术如诗,逻辑似梦:Java 编程中的运算符探寻

【Java篇】算术如诗,逻辑似梦:Java 编程中的运算符探寻

文章目录 * Java 运算符:在计算与逻辑之中追寻编程的哲理 * 1.前言 * 2. 算术运算符 * 2.1 基本四则运算符:加减乘除(+ - * / %) * 2.2 除法与取余 * 2.3 增量运算符(++ --) * 2.4 自增/自减运算符 * 3. 关系运算符 * 3.1 关系运算符 * 4. 逻辑运算符(重点) * 4.1 逻辑与 && * 4.2 逻辑或 || * 4.3 逻辑非 ! * 4.4 短路求值 * 5. 位运算符 * 5.1