突然想起有一个做题的网站(LeetCode)地址:https://leetcode-cn.com/,心血来潮,于是注册做了第一题“两数之和”
感觉非常有意思,因为它是给定你初始格式,让你来完成里面的函数,和之前完全由自己写又不一样,上来第一道题差点儿把自己
看懵了,以后会不时做几道题,并且补充相关知识点!
目录:
第一题 两数之和
第二题是链表就不说了(不过也得不时回顾,要不然就忘了!!!)
第三题 无重复字符的最长子串
第四题 先空着(不是太懂)
第五题 最长回文子串
第六题 Z字形变换
第七题 整数反转
第八题 字符串转换整数(atoi)
第九题 回文数
第十题 标为困难,先空着嘻嘻
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
class Solution {
public:
vector twoSum(vector& nums, int target) {
}
};
上面是一开始系统给的
居然有vector,感觉自己并不太熟,于是查了下vector的用法:
从菜鸟教程上摘了部分:
一、什么是vector?
向量是一个封装了动态大小数组的顺序容器,他能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
二、容器特性
1、顺序序列。顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置(!!!)访问对应的元素
2、动态数组。支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。操供了在序列末尾相对快速地添加/删除元素的操作。
3、能够感知内存分配器的(Allocator-aware)容器使用一个内存分配器对象来动态地处理它的存储需求。
三、基本用法及函数
引用:
#include < vector>
using namespace std;
Vector< vector< int> >v; 二维向量//这里最外的<>要有空格。否则在比较旧的编译器下无法通过
补充:网上一般都会有一维向量的去重,二维向量的去重是这样的类似,
vector< vector
vector
ans.erase(IE,ans.end());
函数:
补充vector的排序:
sort(vector.begin(),vector.end());
其他具体内容看https://www.runoob.com/w3cnote/cpp-vector-container-analysis.html
现在开始做题,于是我尝试写了一下,用了两层循环,哈哈,然后过了,发现居然还有这个功能:
这一看就有神奇方法,于是我想了想:
感觉是可以在O(n)的时间内做完,比如对于nums中的每一个元素进行标记,然后从0到target/2,判断该数和(target-该数)是否被标记过,这里要注意类似8(4+4)这种两数相同的情况,还要注意要有负数这种情况,然后我就写了一发,哈哈,栈溢出,然后我就不会写了
看了一下题解,amazing!哈希表!怎么把他忘了!
先学学哈希表:
见W3Cschoolhttps://www.w3cschool.cn/cpp/cpp-fu8l2ppt.html
第一种方法:两遍哈希
class Solution {
public:
vector twoSum(vector& nums, int target) {
vectorans;
map mapTest;
for(int i=0;i
Amazing!
题解中还提供了一种一遍循环的方法,就是在边构造hash,边查找
class Solution {
public:
vector twoSum(vector& nums, int target) {
vectorans;
map mapTest;
for(int i=0;i
这种方法有个比上一个方法较好的地方就是:
我给你一组【3,3】6这个例子,你按第一个方法去做时,不好理解,因为此时map【3】=1不等于0
但是第二种办法就很好,在加入第二个3之前,就可以先找到第一个3的value,然后才将其覆盖
Amazing!原来LeetCode这么有趣!
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
遇到字符串的题我就比较懵逼了,不过这题标明中等,咱就来看一下吧,这题要求的是最长的字串的长度,一想应该是这种解题思路:
给两个指针从字符串的头开始,一个先不动,一个每次动一个,当遇到一个字符在前面出现过(也就是重复)时,将那个之前不动的向后提(只能是向后提!),提到那个产生重复的前面。在整个过程中,不断更新(j-i+1)和答案之间的最大值即可。
在这里的难点应该就是如何知道是否重复,以及产生重复的元素的位置在哪儿,想了一下,Amazing!上面刚用了map,这里也可以用一下:
另外注意下C++中字符串的遍历!!!
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int ans=0;
mapmapTest;
int i=0,j=0;
for(;i=j){
j=mapTest[s[i]]+1;
}
mapTest[s[i]]=i;
ans=max(ans,i-j+1);
}else{
mapTest[s[i]]=i;
ans=max(ans,i-j+1);
}
}
return ans;
}
};
map挺好用的,一交发现有点儿慢:
不行,再想想:
找了一下博客里有这样说的,然后我改用了unordered_map,结果是一样的,再想想:
不行,想不到了,看了下题解,哈哈,也使用了map,看看有啥不一样?
好像是一样的,再看看评论区吧
天呢!这里有个用了两层循环来找重复的居然用时16ms。。。
不过好像基本思路基本都是一样的,继续!
好熟悉的一题:
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
解法一:
一开始忘了马拉车!!!所以一番思索,结果想了个O(n^2)的。。。
想的是按照回文串的性质,我以字符串中间元素作为回文串的中心,让他同时往两个方向(左右)移动,在移动过程中,同时判断以它为中心的回文串的长度,并记录最大值的左右届,然后最后根据左右届返回对应的字符串即可。这里需要注意的是由于原字符串的长度可奇可偶,所以若为偶数,则中心不太好找,所以我们将其做类似以下的扩展:
abcd ===> @a@b@c@d@ 就是插入一个不常用的字符串即可
结果如下:
代码如下:
class Solution {
public:
string longestPalindrome(string s) {
char *change=new char[2*s.length()+1];
for(int i=0;i<2*s.length()+1;i++){
if(i%2==1){
change[i]=s[(i-1)/2];
}else{
change[i]='@';
}
}
int ans=0;
int left=0;
int right=0;
int middle1=s.length();
int middle2=s.length();
int length=s.length();
while(middle1>0){
int l=middle1;
int r=middle1;
while(l>=0&&r<2*length+1){
if(change[l]!=change[r]){
break;
}
if((r-l)/2>ans){
ans=(r-l)/2;
left=l/2;
right=(r-2)/2;
}
l--;
r++;
}
middle1-=1;
}
while(middle2<2*length){
int l=middle2;
int r=middle2;
while(l>=0&&r<2*length+1){
if(change[l]!=change[r]){
break;
}
if((r-l)/2>ans){
ans=(r-l)/2;
left=l/2;
right=(r-2)/2;
}
l--;
r++;
}
middle2+=1;
}
string Answer="";
for(int i=left;i<=right;i++){
Answer+=s[i];
}
return Answer;
}
};
然后看了一下题解!!!马拉车!!!
解法二:马拉车
我们先学习一下马拉车,这个不太好理解:
又是字符串的题
这种题我想了想,觉得一般都是有规律的,然后我找了找,果然有:
我将转化为Z字形的字符串分成了几组(按 || 这种形式分的)
|| ||
||
比如上面第一个例子 L 是一组
E T
E
这种每组拥有的字符数量为2*numRows-2,能分s.length()/(2*numRows-2)这些组,剩余s.length()%(2*numRows-2)这些字符
然后按Z字形行遍历,找每一行添加进答案中的字符的下标的规律即可,比较难处理的就是剩余那部分字符的处理,不过你可以看成第(s.length()/(2*numRows-2)+1)的处理,然后注意边界就可以了
class Solution {
public:
string convert(string s, int numRows) {
string ans="";
if(numRows==1){
ans+=s;
}else{
int sum=2*numRows-2;
int groupNum=s.length()/sum;
int shengyuNum=s.length()%sum;
for(int i=0;i=numRows){
ans+=s[numRows-1+groupNum*sum];
}
}else{
for(int j=0;j=numRows-temp-1){
ans+=s[groupNum*sum+i];
ans+=s[(groupNum+1)*sum-i];
}else{
ans+=s[groupNum*sum+i];
}
}
}
}
}
return ans;
}
};
写的很麻烦!
总算遇到个被标为简单的题了
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−2^31, 2^31 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
这题一看,不就是整数反转吗,但是这题有个非常重要的就是他给定了最后答案的数据范围,若超过了,只能标为0,然后我就在每一遍答案*10的过程中进行检测(用了long感觉有点儿投机取巧)
class Solution {
public:
int reverse(int x) {
long ans=0;
long temp=x;
while(temp!=0){
long y=temp%10;
ans+=y;
if((ans*10<-2147483648&&temp<=-10)||(ans*10>=2147483648)&&temp>=10){
ans=0;
break;
}else{
if(temp>=10||temp<=-10){
ans*=10;
}
temp=temp/10;
}
}
return ans;
}
};
速度不快,看看人家题解怎么写的:
class Solution {
public:
int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > INT_MAX/10 || (rev == INT_MAX / 10 && pop > 7)) return 0;
if (rev < INT_MIN/10 || (rev == INT_MIN / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
};
提前检测,大体思路差不多,不过这种将我那种*10检测,转为了/10了,避免了*10导致int溢出,而换用long的操作了。。!!
这题是一道字符串处理题吧,有点儿复杂。。
请你来实现一个 atoi 函数,使其能将字符串转换成整数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,qing返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231) 。
这题我的思路就是先去掉原串中字符前面的空格,然后进行处理即可,里面题目中的溢出,我们就可以用第七题中的检测方法来做了!!!
class Solution {
public:
int myAtoi(string str) {
int ans=0;
string s="";
//先删掉所有字符前的空格吧
int i=0;
while(i57){
if(s[0]!='+'&&s[0]!='-'){
ans=0;
}else{
//这种情况一开始是+、-号
if(s[0]=='-'){
for(int g=1;g57){
break;
}
if(ans0){
ans=ans*-1;
}
ans=ans*10+-1*(s[g]-'0');
}
}else{
for(int g=1;g57){
break;
}
if(ans>INT_MAX/10||(ans==INT_MAX/10&&(s[g]-'0')>7)){
ans=INT_MAX;
break;
}
ans=ans*10+(s[g]-'0');
}
}
}
}else{
//这种情况从一开始就是数字,而且是正的
for(int h=0;h57){
break;
}
//检测溢出,用刚做过一题的方法
if(ans>INT_MAX/10||(ans==INT_MAX/10&&(s[h]-'0')>7)){
ans=INT_MAX;
break;
}
ans=ans*10+(s[h]-'0');
}
}
return ans;
}
};
有点儿慢,难道这题有什么好方法???我去看看题解:
正则表达式!!!我在这就不说了,python很万能哈哈,想了解的同学可以看一下我之前写的一篇博客
https://blog.csdn.net/weixin_42412973/article/details/98480252
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
题目中“你能不将整数转为字符串来解决这个问题吗?”,一想,那就将整数倒置比对吧,可能会int溢出,所以改为long(不过考虑空间的话就不行了),然后就过了:
class Solution {
public:
bool isPalindrome(int x) {
bool ans=false;
if(x<0){
ans=false;
}else{
long temp=0;
long temp2=x;
while(x!=0){
int y=x%10;
x/=10;
temp=temp*10+y;
}
if(temp==temp2){
ans=true;
}
}
return ans;
}
};
看了下题解,果然自己太年轻了:
这里需要考虑如何到一半了?题解中(C#)给出的很精妙,同时考虑了是否为回文串:
public class Solution {
public bool IsPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if(x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int revertedNumber = 0;
while(x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber/10;
}
}
这里的while(x>revertedNumber)咱想想为啥这个可以当作已经到一半的标志:
对于偶数位的数:在此循环中,要想x<=revertedNumber只能是当revertedNumber的位数超过或等于原数的一半,同时也可以判断是否为回文串了,若是的话,应该是x==revertedNumber
对于奇数位的数同理,不过此时revertedNumber一定比x多一位循环才结束