文章目录
25.k个一组翻转链表
题目要求
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
- 示例1:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
- 示例2:
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
思路分析
拿到这道题我们首先来分析题目要求,题目要求我们每k个节点为一组,翻转这一组的链表,然后再将翻转之后的链表链接起来,于是我们这道题可以分为下面的几个操作步骤:
- 步骤一:找到k个一组的链表
- 步骤二:断开链接
- 步骤三:翻转k个一组的链表
- 步骤四:链接链表
上面的四步操作我们完成的是一组链表的翻转,那么对于n个元素组成的链表,我们需要将上面的操作循环往复执行,直到后面一组的元素个数小于k个元素为止
那么知道了如何操作之后,我们来看代码应该如何书写,由于翻转链表的操作需要经常进行,所以我们需要一个翻转链表的函数;其次,我们在每一次翻转一组链表的时候,需要把该组链表与原链表断开,然后从头向后翻转链表,所以我们需要知道该组链表的起始节点;然后,由于我们需要重新将该组链表进行链接,所以我们需要知道该组链表的下一个节点;最后我们来看结束条件,通过上面的分析我们可以知道,我们一定是需要遍历这个链表的,那么为了遍历这个链表,我们就需要一个节点的指针帮我们遍历,当我们发现这个节点为空时,就需要结束遍历的操作,其次我们遍历节点的目的是为了找到一组翻转的链表,所以我们需要在k步以内进行遍历操作
上面我们讲述了如何编写主逻辑的代码,下面我们来看我们应该如何翻转链表,也就是如何编写翻转链表的函数。
首先,我们为了翻转这个链表,我们需要循环遍历这个链表;其次,我们为了翻转每一个节点,我们都需要知道该节点的前驱节点和后继节点,所以我们需要定义一个前驱节点prev和后继节点next;那么这个循环的结束条件是什么?我们可以看到,如果我们遍历这个链表,当前节点如果指向空,那么我们就结束循环
有了上面的分析后,我们来编写代码
代码编写
cpp
/**
* 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*reverse(ListNode*head)
{
ListNode*prev=nullptr;
ListNode*cur=head;
while(cur)
{
ListNode*next=cur->next;
cur->next=prev;
prev=cur;
cur=next;
}
return prev;
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode phead(-1);
phead.next=head;
ListNode*prev=&phead;
ListNode*cur=&phead;
while(true)
{
for(int i=0;i<k&&cur!=nullptr;++i)
{
cur=cur->next;
}
if(!cur)break;
ListNode*next=cur->next;
ListNode*start=prev->next;
//断开链接
cur->next=nullptr;
//翻转链表
prev->next=reverse(start);
//链接链表
start->next=next;
//更新节点
prev=start;
cur=start;
}
return phead.next;
}
};
43.字符串相乘
题目要求
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。
- 示例1:
输入: num1 = "2", num2 = "3"
输出: "6"
- 示例2:
输入: num1 = "123", num2 = "456"
输出: "56088"
思路分析
我们首先来分析题目要求,题目让我们进行字符串相乘,那么字符串显然是不能直接运算的,所以我们首先要把字符串转化成数字,然后再进行运算,最后再把运算结果转化成字符串。所以我们根据这个思路,来逐步进行分析
首先我们来看把字符串转化成数字的操作,我们需要遍历这个字符串,然后将每一个位置的字符转化成数字,然后再进行相加;其次我们来看数字之间的运算,这个运算的过程十分的简单,直接相乘即可;最后我们还需要把数字转化成字符串。
那么根据上面的分析,我们来看一下可行性如何?遍历一个字符串的时间复杂度为O(N),遍历两个字符串的时间复杂度就是O(2N);运算的过程是常数级别,但是如果是两个极大的数字,极易造成栈溢出;最后将数字转化成字符串,我们任然需要进行遍历操作,时间复杂度为O(N),加起来的话时间复杂度到了O(3N),那么我们还有没有其他的做法呢?
我们首先来自己运算一下两个数相乘,我们需要将num1每一位的数字与num2相乘,然后再将相乘的结果相加,那么这一个过程当中涉及到进位的问题,所以我们需要解决进位的问题;其次,我们思考一下,一个n位的数字与一个m位的数字相乘,结果最多为多少位?答案是m+n位,所以我们能不能定义一个数组,这个数组的每一个元素都对应了结果的每一位数字,通过这个数组我们可以把问题简化成将每一位的运算结果相加后插入到数组的每一个位置中去,所以我们需要遍历num1字符串和num2字符串,最后将字符串的每一个元素转化成字符,插入到新的字符串中去
有了这个思路,我们来编写代码
代码编写
cpp
class Solution {
public:
string multiply(string num1, string num2) {
if(num1=="0"||num2=="0"){return "0";}
int n=num1.size();
int m=num2.size();
vector<int> result(m+n,0);
for(int i=n-1;i>=0;--i)
{
for(int j=m-1;j>=0;--j)
{
//相乘
int mul=(num1[i]-'0')*(num2[j]-'0');
//进位相加
int sum = mul + result[i+j+1];
//将每一位的结果插入到数组
result[i+j+1]=sum%10;
//进位
result[i+j]+=sum/10;
}
}
string str;
int i=0;
//解决头部为0的问题
while(i<result.size()&&result[i]==0){++i;}
//尾插元素,转化成字符
while(i<result.size())
{
str.push_back(result[i]+'0');
++i;
}
return str;
}
};