代码随想录打卡 Day 2

news/2025/1/3 17:09:28/文章来源:https://www.cnblogs.com/kotoriinsky/p/18644833

代码随想录打卡 Day 2

1.链表的定义与操作

链表作为基本的数据结构类型,其主要形式有三种:

  • 单链表

  • 双链表

  • 循环链表

由于刷代码题平时在OJ上默认定义已经给出,可以直接使用。而在面试/机试时,一般需要需要自己手写链表。因此,正确写出链表的定义是非常有必要的。一个单链表的定义如下所示:

struct ListNode{int val;ListNode *next;ListNode(int x):val(x),next(nullptr){} //节点的构造函数
};

其中,注意到,第三行给出了一个构造函数,通过这个构造函数,我们可以对一个新节点的val变量直接赋值。

链表的基本操作包括了:

  • 删除节点
  • 添加节点
  • 查询链表内元素(包括按照val值和索引值)

相比较数组,链表的长度可以是不固定的,并且可以动态增删,适合数据量不固定,频繁增删,较少查询的场景。

2.链表常见的6个操作

leetcode题号:707.设计链表

【题目描述】

设计一个链表类,实现六个接口:

  1. 获取链表第index个节点的值(按索引查询)
  2. 在链表的最前面插入一个节点(头插法)
  3. 在链表的最后面插入一个节点(尾插法)
  4. 在链表的第index个节点插入一个节点(按索引插入)
  5. 删除链表的第index个节点(按索引删除)
  6. 打印当前链表

【题目分析】

该题是练习链表操作的一道非常好的题目,六个结构覆盖了链表的常见操作,其代码如下:

class MyLinkedList {
public:struct Node {int val;Node* next;Node(int x) : val(x), next(NULL) {}};private:Node* _dummyhead; //设置一个伪头指针,方便后续操作int _size;        // _size表示链表元素个数MyLinkedList() { //构造函数_dummyhead = new Node(0);_size = 0;}int get(int index) {   //按索引查询元素if(index > _size -1 || index < 0)return -1;Node* cur=_dummyhead->next;while (index--){cur=cur->next;}return cur->val;}void addAtHead(int val){  //头插法Node* newnode = new Node(val);newnode->next = _dummyhead->next;_dummyhead->next = newnode;_size++;}void addAtTail(int val) {  //尾插法Node* newnode =new Node(val);Node* cur=_dummyhead;while (cur->next != NULL){cur=cur->next;}cur->next=newnode;_size++;}void addAtIndex(int index, int val) {  //按索引插入if(index>_size) return;if(index<0) index=0;Node* newnode = new Node(val);Node* cur=_dummyhead;while(index--){cur=cur->next;}newnode->next=cur->next;cur->next=newnode;_size++;}void deleteAtIndex(int index) {  //按索引删除if(index>=_size || index<0) return;Node* cur=_dummyhead;while(index--){cur=cur->next;}Node* tmp=cur->next;cur->next=cur->next->next;delete tmp;tmp=nullptr;_size--;}void printList(){	//打印链表Node* cur=_dummyhead;while (cur->next != NULL){cout<<cur->next->val<<" ";cur=cur->next;}cout<<endl;}
};

3.利用伪头节点(dummy_Head)来简化操作

在单链表中,删除头节点与删除其他节点的操作不一样,除了共同的释放内存和指针移动之外,还需要移动头指针。通过设置一个伪头节点,最后将dummy_Head->next设置为新的头节点即可。

leetcode203题移除链表元素为例,设置伪头节点可以方便处理待处理元素为头节点的情况。

本题的代码如下:

ListNode* removeElements(ListNode* head, int val) {ListNode* dummy = new ListNode(-1);  //伪头节点的设置dummy->next = head;ListNode* cur = dummy;while(cur->next!=NULL){if(cur->next->val == val){ListNode* tmp = cur->next;cur->next = cur->next->next;delete tmp;}else{cur = cur->next;}}head = dummy->next; //设置新的头节点delete dummy;  //删除伪头节点return head;   
}

4.链表的反转

leetcode题号:206.反转列表

【题目描述】

反转一个单链表,要求不能申请额外的内存空间

【题目分析】

反转链表是链表操作中的一个重要思路,这里使用双指针法和递归法来实现。对于双指针发,只需要改变next指针的指向,不需要重新定义一个新的链表。

双指针法

双指针法的思想是定义一个cur指针和prev指针,分别进行遍历,并且使用nxt指针保存下一个指针,便于遍历。

双指针法的代码如下所示:

ListNode* reverseList(ListNode *head) {if (head == NULL || head->next == NULL) {  //对链表只有至多一个元素的边界条件处理return head;}ListNode *p = head->next;ListNode *prev = head;ListNode *nxt= NULL; //nct指针保存p所指的下一个结点while(p!=NULL){ //遍历nxt = p->next;p->next = prev;prev = p;p = nxt;}return  prev;
}
递归法

递归法的思路实质上与双指针法的思路一致,但是递归法的代码较为抽象,比较难写。代码如下所示:

ListNode* reverse(ListNode * pre, ListNode * cur) {if (cur == NULL) return pre;ListNode * nxt = cur->next;cur->next = pre;//如下递归的算法,实质上完成了://pre=cur;//cur=temp;return reverse(cur, nxt);
}ListNode* reverseList(ListNode *head) {//与双指针法的初始化逻辑一致return reverse(NULL, head);
}

5.双指针法在链表中的应用

1)删除倒数第N个节点

【基本思路】

如果要删除倒数第N个节点,可以先让快指针fast移动N步,然后让fastslow同时移动,删除slow所在的节点即可。

删除链表倒数第N个节点又应该使用伪头节点,让slow节点指向待删除节点前一个节点。因此,可以让fast先动一步,然后让fast移动N步,最后让fastslow同时移动一步。

基于以上思路,本题的代码如下所示:

ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode(0, head);dummyHead->next = head; //伪头节点设置ListNode* fast = dummyHead;ListNode* slow = dummyHead;while(n-- && fast->next != nullptr) { //fast移动N步fast = fast->next;}fast = fast->next; //需要让slow指向要删除节点的前一个节点while(fast!=NULL){ //fast与slow同时移动一步fast = fast->next;slow = slow->next;}slow->next = slow->next->next;return dummyHead->next;
}

对快慢指针进行移动方法调整,就可以获得单链表的中间节点。

2)判断链表是否有环

【基本思路】

通过快慢指针法,分别定义fastslow指针,从头节点出发,fast移动两步,slow移动一步,若fastslow相遇,则说明有环,否则则无环。

但是,如果要寻找到环的入口,则是一个比较困难的问题。在这里,我们假设从头节点到环的入口的节点数为$x$,环形入口节点到fastslow节点相遇节点数为$y$。从相遇节点再到环形入口节点书为$z$.

那么,相遇时,slow走过了$(x+y)$个节点,fast走过了$2(x+y)=x+y+n*(y+z)$个节点,其中$n$为fast指针与slow相遇时已经走过的圈数。

通过化简,得到

$$x=(n-1)(y+z)+z$$,

$$x+y=n(y+z)$$

此时,可以在相遇节点处定义一个指针index1,在头节点处定义index2,让两个指针同时移动一步,两者相遇的位置即为环形入口的起点。

根据以上的思路,可以整理出本题的解

ListNode *detectCycle(ListNode *head) {ListNode *slow = head, *fast = head;while (fast && fast->next) {slow = slow->next;fast = fast->next->next;if (slow == fast) { //找到环后的处理slow = head;while (slow != fast) {slow = slow->next;fast = fast->next;}return slow;}}return NULL;
}

3)判断两个链表的共同后缀

【基本思路】

两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。题目数据保证整个链式结构中不存在环。

本题可以通过先遍历A,B链表的长度,再使用双指针法进行操作。需要注意的是,交错链表并非数值相等,而是指针相等。

基于以上思考,可以给出如下的算法:

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {if (headA == NULL || headB == NULL) {return NULL;}ListNode *pA = headA, *pB = headB;int lenA = 0, lenB = 0;while(pA!=NULL){lenA++;pA = pA->next;}while(pB!=NULL){lenB++;pB = pB->next;}pA = headA;pB = headB;if (lenB > lenA) {swap(lenA, lenB);swap(pA, pB);}//保证headA是长的,便于算法的求解int gap = lenA - lenB;//计算两者长度插值,为接下来使用快慢指针法做准备while (gap--){pA = pA->next;}while (pA != NULL) {if(pA == pB) return pA;pA = pA->next;pB = pB->next;}return NULL;}

6. 总结

对于以单链表为基本数据结构的算法题,主要的技巧就是伪头节点,双指针,以及反转链表。

  • 伪头节点:其主要作用时简化链表的边界条件,简化操作,并保持链表结构的一致性。
  • 双指针法:通过快慢指针的方法,可以快速查找中间节点,倒数K个节点等特殊节点,以及探测链表中的环。
  • 反转链表:通过设置prev,curr,next三个指针,可以将链表进行反转操作,便于判断回文等操作的实现。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]

manacher 与贪心的好题。Paper-cutting:思维很好,但代码很构式的 manacher 题。 蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念。 判断能否折叠 我们先考虑一部分能折叠需要满足什么条件。显然,这一部分需要是一个长度为偶数的回文串。 那么横向和纵向会…

深度学习基础理论————分布式训练(模型并行/数据并行/流水线并行/张量并行)

主要介绍Pytorch分布式训练代码以及原理以及一些简易的Demo代码 模型并行 是指将一个模型的不同部分(如层或子模块)分配到不同的设备上运行。它通常用于非常大的模型,这些模型无法完整地放入单个设备的内存中。在模型并行中,数据会顺序通过各个层,即一层处理完所有数据之后…

overleaf-Latex教程

1.领取免费服务器,推荐免费服务器(SanFengYun)见下图。2.安装宝塔面板,配置内网为127.0.0.1,访问外网地址。 3.可以在宝塔面板一键部署网站,输入自己的域名即可。 4.关键:安装docker,安装yum,设置github可以访问。 5.更换docker镜像,自带镜像无法访问 6.按照overleaf…

Sola的2024年度总结

前言 2024 这一年对我来说确实意义非凡,很想写点东西来记录一下这一年我的经历,算是第一次写年度总结了。 简短的记录一下我这一年。 现在?未来? 回忆起大一下最后一节体育课,体育老师让每个人想一个词来描述这个上半年,我给出的答案是 : 迷茫 。 现在来看,这个答案贯穿…

洛谷 P11487 「Cfz Round 5」Gnirts 10——题解

洛谷P11487「Cfz Round 5」Gnirts 10传送锚点摸鱼环节 「Cfz Round 5」Gnirts 10 题目背景 English statement. You must submit your code at the Chinese version of the statement.In Memory of \(\text{F}\rule{66.8px}{6.8px}\). 题目描述 题面还是简单一点好。给定 \(n, …

基于高德地图API在Python中实现地图功能的方法

本文介绍在高德开放平台中,申请、获取地图API的Key的方法;同时通过简单的Python代码,调取API信息,对所得Key的可用性加以验证~本文介绍在高德开放平台中,申请、获取地图API的Key的方法;同时通过简单的Python代码,调取API信息,对所得Key的可用性加以验证。首先,我们进入…

活动对象----active object

一.preface 近期学习QPC框架,其核心之一就是 actvie-object,活动对象的出现是为了解决并发(阻塞、数据竞争)问题。笔者这里做一篇笔记,方便日后回顾。 二.What is "active object"活动对象的组成框架代码如下点击查看代码 typedef struct Active Active; typedef …

新的一年,我决定拆解一个蓝牙接收器

哈哈,容我介绍一下,如果大家对电子感兴趣,可以看一下下面的图片,会经常更新优秀的原创文章。再次感谢每一个努力的电子爱好者。今天我们来拆解一个蓝牙接收器,首先我们需要有一个直观的印象。下图就是我们这次需要拆解的对象。我再想这么小的接收器,电路是怎么放进去得呢…

jfianl 如何定时某个时间点执行一个任务

如果我们需要在某个点执行一个任务,可以用使用以下方法,首先在操作之间先明白思路 参考技术来源:https://jfinal.com/doc/9-2 第一步,先安装包,因为 这是第三方包: <dependency> <groupId>it.sauronsoftware.cron4j</groupId> <artifactId>cr…

Window平台下Visual Studio版本和Qt构建kit 以及OpenCV的对应关系

1、VS版本、MSVC版本、工具集的对应关系 参考https://www.cnblogs.com/lidabo/p/183977552、Qt中的构建kit和MSVC的对应关系 qt中使用对应版本的kit必须安装对应版本的VS才能使用3、OpenCV的VC17文件夹和VS版本的对应关系 OpenCV中的VC17文件夹就是指用的VS2022编译的库,visua…

题解:AT_abc386_d [ABC386D] Diagonal Separation

分析题面,发现题目求的是是否存在一个白点被 \((1, 1)\) 和任意一个黑点围成的矩形内。 先将所有黑点按 \(x\) 坐标排序。 枚举所有的白点。 找到所有横坐标不比该白点横坐标小的所有黑点的纵坐标的最大值所属点。如果该点的纵坐标小于该白点的纵坐标:(蓝点代表题目中的白点…

【Miscellaneous】一道高质量的杂项题,涉及暴破、Cloakify-python2、零宽、emoji-AES等知识点

引言 下半年很忙,好久不做题,趁2025元旦放假整理一道高质量的题目,怀念一下繁忙的2024年。 题目 考虑到某公司的不分享精神或许会有版权之类的争端,文件链接以后就不放了。 名称:happymd5 提示:有好多奇奇怪怪的MD5值,这是用来干什么的呢。 Writeup(WP)题目附件cipher…