【代码随想录37期】 第一周总结

2024-05-13 15:52

本文主要是介绍【代码随想录37期】 第一周总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

周末再写一遍

【代码随想录37期】Day01 二分查找 + 移除元素
【代码随想录37期】Day02 有序数组的平方、长度最小的子数组、螺旋矩阵Ⅱ
【代码随想录37期】Day03 移除链表元素、设计链表、反转链表
【代码随想录37期】Day04 两两交换链表中的节点、删除链表的倒数第N个节点、链表相交、环形链表II

补充

长度最小的子数组

v2.0:使用滑动窗口
滑动窗口的思想其实很简单,传统暴力使用两个for循环分别控制窗口的边界
滑动窗口重点在于滑~ 所以其中一个边界是靠条件来操控的
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int sum = 0, len = 0,left = 0;vector<int> result;result.push_back(0);for(int right = 0; right<nums.size();right++){sum+=nums[right];len++;while(sum>=target&&left<nums.size()){result.push_back(len);sum-=nums[left++];len--;}}sort(result.begin(), result.end());return result.size()>1?result[1]:result[0];}
};v3.0:
前面使用vector有点大材小用,这里将result声明为INT32_MAX可以直接迭代
class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int sum = 0, len = 0,left = 0,result = INT32_MAX;for(int right = 0; right<nums.size();right++){sum+=nums[right];len++;while(sum>=target&&left<nums.size()){result = len<result?len:result;sum-=nums[left++];len--;}}return result==INT32_MAX?0:result;}
};
v4.0:
int minSubArrayLen(int target, vector<int>& nums) {int left = 0, right = 0, len = 0, sum = 0, min = INT32_MAX;for (; right < nums.size(); right++){sum += nums[right];len++;while (sum >= target){if (len < min)min = len;sum -= nums[left];len--;left++;}}return min==INT32_MAX? 0 : min;
}

关键点

为什么要写INTMAX_32?

题目条件:1 <= nums.length <= 10^ 5,写一个比10^5大的数就行
INT_MAX 和 INT_MIN 是 C++ 的两个宏,代表了整型变量能够存储的最大正整数和最小负整数,分别为 2147483647 和 -2147483648,这两个宏在头文件 <limits.h> 中定义。
2147483647约等于10的9次方
9223372036854775807(long long)约等于10的18次方
记忆方法:2的10次幂(1024)约等于10的3次幂,那2的32次约等于10的9次幂,2的64次约等于10的18次

为什么最后一种写法while不用加left < nums.size()?

已知进入for循环时right<nums.size(),要满足sum >= target,left与right之间至少要有一个数,也就是nums[right],此时left<right<nums.size();

设计链表


class MyLinkedList {
public:struct LinkNode{int val;LinkNode* next;LinkNode() :val(0), next(nullptr) {};LinkNode(int n) :val(n), next(nullptr) {};};MyLinkedList() {dummyHead = new LinkNode();size = 0;}int get(int index) {if (index > size - 1)return -1;LinkNode* curNode = dummyHead->next;while (index--){curNode = curNode->next;}return curNode->val;}void addAtHead(int val) {LinkNode* newNode = new LinkNode(val);newNode->next = dummyHead->next;dummyHead->next = newNode;size++;}void addAtTail(int val) {LinkNode* newNode = new LinkNode(val);LinkNode* curNode = new LinkNode();curNode = dummyHead;while (curNode->next){curNode = curNode->next;}curNode->next = newNode;size++;}void addAtIndex(int index, int val) {if (index > size)return;LinkNode* newNode = new LinkNode(val);LinkNode* curNode = new LinkNode();curNode = dummyHead;while (index--){curNode = curNode->next;}newNode->next = curNode->next;curNode->next = newNode;size++;}void deleteAtIndex(int index) {if (index > size-1)return;LinkNode* curNode = new LinkNode();curNode = dummyHead;while (index--){curNode = curNode->next;}LinkNode* toDelete = curNode->next;curNode->next = curNode->next->next;delete toDelete;size--;}private:LinkNode* dummyHead;int size;
};

##关键点

为什么有的时候curNode = dummyHead?,有时候curNode = dummyHead->next?

总之都会有一个while循环,无论是while(curNode)还是while(curNode->Next)
宗旨有2:

  1. 当链表为空时也可以访问curNode;
  2. curNode遍历完curNode指向最后一个节点而不是nullptr;
    一般考虑这两个情况即可,也就是链表的边界情况

反转链表

1. 用指针定义的结构体或类的成员用->访问,cur->next等价于(*cur).next
2. 链表双指针重要的是指针朝向的更新,循环条件看看是不是快指针,但是返回的指针注意不要是指向null的
3. 指针的结构体:
struct LinkedList{int val;LinkedList* next;
};class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* tmp;ListNode* cur = head;ListNode* pre = nullptr;while(cur){tmp = cur->next;cur->next = pre;pre = cur;cur = tmp;}return pre;}
};v2.0:使用虚拟头节点来做这道题,可见虚拟头节点并不是必要的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* reverseList(ListNode* head) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *slow = nullptr;ListNode *fast = head;ListNode *tmp;while(fast){tmp = fast->next;fast->next = slow;slow = fast;fast = tmp;}// delete dummyHead;dummyHead = nullptr;return slow;}
};v3.0:写反转链表完全不需要虚拟头节点,把nullptr"作为"头节点即可
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* left = nullptr;ListNode* right = head;while (right){ListNode* tmp = right->next;right->next = left;left = right;right = tmp;}return left;}
};

为什么反转链表不需要虚拟头节点?什么情况下需要呢?

反转链表中,我们如果有虚拟头节点,最后还要去掉,因为我们最后的head发生了变化,不能直接通过虚拟头节点访问了,所以在这里是冗余的,涉及到最终无法访问head节点、head节点发生变化导致无法从虚拟头节点访问时,就不要用虚拟头节点。

两两交换链表中的节点

/*** 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) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *cur = dummyHead; while(cur->next&&cur->next->next){ListNode* tmp = cur->next; // 记录临时节点ListNode* tmp1 = cur->next->next->next; // 记录临时节点cur->next = cur->next->next;    // 步骤一cur->next->next = tmp;          // 步骤二cur->next->next->next = tmp1;   // 步骤三cur = cur->next->next; // cur移动两位,准备下一轮交换}return  dummyHead->next;}
};
class Solution {
public:ListNode* swapPairs(ListNode* head) {ListNode* dummyHead = new ListNode();dummyHead->next = head;ListNode* pre = dummyHead;while (pre->next&&pre->next->next){ListNode* left = pre->next;ListNode* right = pre->next->next;pre->next = right;left->next = right->next;right->next = left;pre = left;}return dummyHead->next;
}
};

关键点

为什么要判断pre->next&&pre->next->next?

检查pre->next->next是因为循环体中会访问pre->next->next的next,不检查会越界
检查pre->next是因为要检查pre->next->next首先要访问pre->next的next,不检查也会越界

这道题有些绕

确实,我目前是这样理解的,主要有三个指针需要交换,除了两个节点还有两个节点的前一个节点,也就是pre,left和right
这道题的left和right要写成局部变量,不然不好处理while的条件

删除链表中的倒数第N个节点

注意使用虚拟头节点,可以避免对头节点的判断,防止各种复杂情况

/*** 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* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode(0);dummyHead->next = head;ListNode* slow = dummyHead;ListNode* fast = dummyHead;while(n--&&fast){fast = fast->next;}fast = fast->next;while(fast){fast = fast->next;slow = slow->next;}slow->next = slow->next->next;return dummyHead->next;}
};
/*** 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* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode(0);dummyHead->next = head;ListNode* slow = dummyHead;ListNode* fast = dummyHead;while(n--&&fast){fast = fast->next;}fast = fast->next;while(fast){fast = fast->next;slow = slow->next;}slow->next = slow->next->next;return dummyHead->next;}
};
ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* dummyHead = new ListNode();dummyHead->next = head;ListNode* fast = dummyHead;while (n--){fast = fast->next;}ListNode* slow = dummyHead;while (fast->next){slow = slow->next;fast = fast->next;}ListNode* todelete = slow->next;slow->next = slow->next->next;delete todelete;return dummyHead->next;
}

关键点

为什么不直接返回head?

因为head有可能被删掉

这种题该怎么写?

链表题主要需要想象节点之间的连接关系,一定要画图来做,我常用的一个例子就是:
虚拟节点(0)->head(1)->(2)->(3)->(4)->nullptr

环形链表Ⅱ

这道题值得多做几遍

v1.0 这道题很巧妙,是看了随想录的讲解才做出来的
1. 判断有无环:定义快慢指针,快指针一次走两个节点,慢指针走一个,快指针追上慢指针说明有环
2. 追上时需要纸笔列下式子,此时快慢指针走过的节点数量,然后要求的是从开始节点到进入环的节点的长度
3. 具体推导可以看随想录,总之就是需要列一下式子就可以找出关系了~
/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode *detectCycle(ListNode *head) {ListNode *dummyHead = new ListNode(0);dummyHead->next = head;ListNode *fast = head;ListNode *slow = head;ListNode *aux = head;while(fast&&fast->next){fast= fast->next->next;slow = slow->next;if(fast==slow){while(aux!=slow){slow=slow->next;aux=aux->next;}return aux;}}return nullptr;}
};

靠视觉记忆记住这张图吧

快指针走了:a + b + k(b+c)

慢指针走了:a + b

所以a +b + k(b+c) = 2(a+b)

得到a + b = k (b + c)

所以a - c = k(b+c)
补充一个哈希表做法保底:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:ListNode* detectCycle(ListNode* head) {unordered_set<ListNode*> visited;ListNode* cur = head;while (cur){if (visited.count(cur)){return cur;}visited.insert(cur);cur = cur->next;}return nullptr;
}
};

这篇关于【代码随想录37期】 第一周总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/986152

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时