一位C++小白的力扣刷题_成长记录_welcome to visit ^_^
数组_第5题:最长回文子串
题目描述:
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
举例
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
解题思路:采用类似于哈希表的 标记法。在一个二维的 “ vector 容器 ” 里面做标记。 这里其实有一个动态规划的思想。在力扣的 官方解题思路里有,结合了数学公式,很详细。
学习心得:做了这道题后,再一次让我感受到了 “数学思维”的重要性。 看官方解题思路,让我豁然开朗,
虽然,看了答案后,在之后自己独立敲地过程中还是遇到了很多问题。但最后还是解决了,深入分析,真正
弄懂了,小有成就。 嘿,等我第二遍来刷你时,我可不会手下留情的!
实现:(C++)
class Solution {
public:
string longestPalindrome(string s) {
int n=s.size();
vector<vector<int>> dp( n,vector<int>(n) ); //"()"里的n不能省去呀....
string res;
if ( n==1 ) return s; //特殊情况,特殊处理
int i,t;
for( t=0;t<n;t++ ) //字符串s的有效长度为多少,就循环多少次
for( i=0;i+t<n&&i+1<n;i++ )
{
if( t==0 ) //使“vector”二维容器里的对角线 上的数组值为1。即最短字符串
dp[i][i+t]=1;
else if( t==1 ) //如果 s[i]==s[i+t] ,注意 ,这里的t=1,即字符串里相邻的两个元素值相同时,对应“vector”二维容器里的元素 值赋为1
dp[i][i+t] = ( s[i]==s[i+t] );
else
{
dp[i][i+t] = ( s[i]==s[i+t]&&dp[i+1][i+t-1] ); //首尾字符相同,字符串里面的 字符串是回文字符串 。前面两个条件满足,才能给“vector”二维容器里 相应的元素赋值为1
}
if( dp[i][i+t]&&t+1>res.size() ) //只有遇到“vector”二维容器里 标记为1的元素,说明才有 回文字符串。 注: t+1 其实是 (i+t)-i+1
res=s.substr( i,t+1 );
}
return res;
}
};
运行结果:
代码执行结果:
我的输入
"babad"
我的答案
"bab"
预期答案
"bab"
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
数组_第6题: 递增的三元子序列
题目描述:
给定一个未排序的数组,判断这个数组中是否存在长度为 3 的递增子序列。
数学表达式如下:
如果存在这样的 i, j, k, 且满足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否则返回 false 。
说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1) 。
举例:
示例 1:
输入: [1,2,3,4,5] 输出: true
示例 2:
输入: [5,4,3,2,1] 输出: false
解题思路:先找前两者 min 和 mid( middle 的意思),然后找第三者(即 max),这里需要注意的是,当遇到一个数,值在 min 和 mid 之间时,要把这个数值 赋给 mid。当遇到
一个数,值小于 min 时,要把这个值 赋给min 。虽然可能遇到 这种情况 [ 1,2,0,3 ] ,但你细品品,对于结果的 正确性,也是没问题的呦。
学习心得:做这道题,主要在 min 和 mid 的初始化上,摔了很多坑。 mid不能由 数组中的某个元素赋值。
应当 给他赋 INT_MAX。 具体原因,看看代码,想一想就懂了。这道题不难,刚开始我还满怀信心,以为
我能凭一己之力,轻轻松松地将其解决。可是提交五六次都失败,害。原来呀,许多细节没做好。 渐渐醒悟,
敲代码,是个细针手活呀~
实现:(C++)
class Solution {
public:
bool increasingTriplet(vector<int>& nums) {
if( nums.size()<=2 ) return false;
int i,min=nums[0],mid=INT_MAX; // mid 得赋int类型的最大值:INT_MAX。 这样可以保证,只有执行过第一个 “else if” ,才有可能执行 第二个 “else if”,不然,
//可能还没执行第一个,直接执行第二个“else if”。
for( i=1;i<nums.size();i++ )
{
if( nums[i]<min )
min=nums[i];
else if( nums[i]<mid&&nums[i]>min )
mid=nums[i];
else if( nums[i]>mid )
return true;
}
return false;
}
};
运行结果:
代码执行结果:
我的输入
[1,2,3,4,5]
我的答案
true
预期答案
true
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
链表_第1题:两数相加
题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
举例:
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
解题思路:我的解题思路是,先使两个链表对位对齐,没有的 用 “0” 来补上(申请动态空间来补)。然后进行逐位相加,并把进制问题认真处理。
学习心得:练习了这道题后,我仿佛看到了一些更深远的东西。 int型、double型 始终只能分别表示32位、64位。
但这链表可长着了! 这让我明白了为什么 π (圆周率)可以写到小数点后 一亿位。 所得斯勒!I got it!做了这
道题,也让我再次熟悉了一下链表操作,以及 申请动态空间的操作。
实现:(C++)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *head=new ListNode(-1); //哑结点
ListNode *fp=head; //用来移动的指针
ListNode *p=l1; //l1是指向链表头的头指针,一般不动它,另外建立一个指针来使用
ListNode *q=l2;
int flag=0,sum=0; //flag 用来判断是否进位,其值要么是1,要么是0
int len_1=0,len_2=0;
if( l1==NULL ) return l2;
if( l2==NULL ) return l1;
while ( p->next!=NULL ) { len_1++; p=p->next; } //注意,这里有个细节,while() 循环的括号里是“ p->next!=NULL ”,而不是 “ p!=NULL ”。如果不这样,在后面 就不能在 l1的链表末尾
//新生成一个结点。因为如果不这样,p就指向了 “空”,生成的结点 并没有和 l1的链表末尾连接起来。
while ( q->next!=NULL ) { len_2++; q=q->next; }
if( len_1>len_2 )
while( len_1>len_2 )
{ q->next=new ListNode (0); q=q->next; len_2++; }
else if( len_1<len_2)
while( len_1<len_2 )
{ p->next=new ListNode (0); p=p->next; len_1++; }
while( l1!=NULL&&l2!=NULL )
{
sum+=l1->val+l2->val; //sum用来记录相加结果
l1=l1->next;
l2=l2->next;
fp->next=new ListNode( (sum+flag)%10 ); //sum要加上 上两位数相加后的 进位标记flag的值
fp=fp->next;
if( (sum+flag)>=10 )
flag=1;
else
flag=0;
sum=0;
}
if( flag==1 ) //如果最后,flag值还为1,就再申请一个 结点
fp->next=new ListNode( 1 );
return head->next; //返回 哑结点的下一位结点的地址
}
};
运行结果:
代码执行结果:
我的输入
[2,4,3] [5,6,4]
我的答案
[7,0,8]
预期答案
[7,0,8]
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
链表_第2题: 奇偶链表
题目描述:
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。
请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。
举例:
示例 1:
输入: 1->2->3->4->5->NULL 输出: 1->3->5->2->4->NULL
示例 2:
输入: 2->1->3->5->6->4->7->NULL 输出: 2->3->6->7->1->5->4->NULL
说明:
- 应当保持奇数节点和偶数节点的相对顺序。
- 链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。
解题思路:我的思路就是,在构造一个链表,两次遍历,分别储存 目标链表的 奇位数的 、偶位数的 结点。
学习心得:原地算法,害,我的小脑瓜想不出来呀。 现在,我能独立完成一道中等难度的题就有点小满足了。
虽然心里还是力求更高,但少年,别慌,咱们慢慢磨刀。第二遍时,优化,优化。
实现:(C++)
/**
* 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* oddEvenList(ListNode* head) {
ListNode* res=new ListNode(-1); //哑结点
ListNode* fp=res;
ListNode* h=head;
int i=1;
while( h!=NULL )
{
if( i%2==1 ) //先储存奇位数的 结点
{
fp->next=new ListNode( h->val);
fp=fp->next;
h=h->next;
}
else
h=h->next;
i++;
}
i=1;
h=head; //准备好第二次遍历
while( h!=NULL)
{
if( i%2==0) //在储存偶位数的 结点
{
fp->next=new ListNode( h->val);
fp=fp->next;
h=h->next;
}
else
h=h->next;
i++;
}
return res->next;
}
};
运行结果:
代码执行结果:
我的输入
[1,2,3,4,5]
我的答案
[1,3,5,2,4]
预期答案
[1,3,5,2,4]