LeetCode刷题之路(一)(1~10)
突然想起有一个做题的网站(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]
[code]class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { } };
上面是一开始系统给的
居然有vector,感觉自己并不太熟,于是查了下vector的用法:
从菜鸟教程上摘了部分:
一、什么是vector?
向量是一个封装了动态大小数组的顺序容器,他能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
二、容器特性
1、顺序序列。顺序容器中的元素按照严格的线性顺序排序。可以通过元素在序列中的位置(!!!)访问对应的元素
2、动态数组。支持对序列中的任意元素进行快速直接访问,甚至可以通过指针算述进行该操作。操供了在序列末尾相对快速地添加/删除元素的操作。
3、能够感知内存分配器的(Allocator-aware)容器使用一个内存分配器对象来动态地处理它的存储需求。
三、基本用法及函数
[code]引用: #include < vector> using namespace std;
Vector< vector< int> >v; 二维向量//这里最外的<>要有空格。否则在比较旧的编译器下无法通过
补充:网上一般都会有一维向量的去重,二维向量的去重是这样的类似,
vector< vector <int> >ans;
vector<vector<int>>::iterator IE = unique(ans.begin(),ans.end());
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
第一种方法:两遍哈希
[code]class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int>ans; map<int,int> mapTest; for(int i=0;i<nums.size();i++){ mapTest[nums[i]]=i; } for(int i=0;i<nums.size();i++){ if(mapTest.find(target-nums[i])!=mapTest.end()&&mapTest[target-nums[i]]!=i){ ans.push_back(i); ans.push_back(mapTest[target-nums[i]]); break; } } return ans; } };
Amazing!
题解中还提供了一种一遍循环的方法,就是在边构造hash,边查找
[code]class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int>ans; map<int,int> mapTest; for(int i=0;i<nums.size();i++){ if(mapTest.find(target-nums[i])!=mapTest.end()){ ans.push_back(i); ans.push_back(mapTest[target-nums[i]]); } mapTest[nums[i]]=i; } return ans; 7ff7 } };
这种方法有个比上一个方法较好的地方就是:
我给你一组【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++中字符串的遍历!!!
[code]class Solution { public: int lengthOfLongestSubstring(string s) { int ans=0; map<char,int>mapTest; int i=0,j=0; for(;i<s.length();i++){ if(mapTest.find(s[i])!=mapTest.end()){ //只能往前移 if(mapTest[s[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@ 就是插入一个不常用的字符串即可
结果如下:
代码如下:
[code]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字形变换
又是字符串的题
这种题我想了想,觉得一般都是有规律的,然后我找了找,果然有:
我将转化为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)的处理,然后注意边界就可以了
[code]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;i++){ if(i==0){ for(int j=0;j<groupNum;j++){ ans+=s[j*sum]; } if(shengyuNum!=0){ ans+=s[groupNum*sum]; } }else if(i==numRows-1){ for(int j=0;j<groupNum;j++){ ans+=s[numRows-1+j*sum]; } if(shengyuNum>=numRows){ ans+=s[numRows-1+groupNum*sum]; } }else{ for(int j=0;j<groupNum;j++){ ans+=s[i+j*sum]; ans+=s[(j+1)*sum-i]; } if(shengyuNum<=numRows){ if((groupNum*sum+i)<s.length()){ ans+=s[groupNum*sum+i]; } }else { int temp=shengyuNum-numRows; if(i>=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感觉有点儿投机取巧)
[code]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; } };
速度不快,看看人家题解怎么写的:
[code]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)
这题是一道字符串处理题吧,有点儿复杂。。
请你来实现一个 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) 。
这题我的思路就是先去掉原串中字符前面的空格,然后进行处理即可,里面题目中的溢出,我们就可以用第七题中的检测方法来做了!!!
[code]class Solution { public: int myAtoi(string str) { int ans=0; string s=""; //先删掉所有字符前的空格吧 int i=0; while(i<str.length()){ if(str[i]!=' '){ break; } i++; } while(i<str.length()){ s+=str[i++]; } //进行处理 if(s.length()==0){ ans=0; }else if(s[0]<48||s[0]>57){ if(s[0]!='+'&&s[0]!='-'){ ans=0; }else{ //这种情况一开始是+、-号 if(s[0]=='-'){ for(int g=1;g<s.length();g++){ if(s[g]<48||s[g]>57){ break; } if(ans<INT_MIN/10||(ans==INT_MIN/10&&-1*(s[g]-'0')<-8)){ ans=INT_MIN; break; } if(ans>0){ ans=ans*-1; } ans=ans*10+-1*(s[g]-'0'); } }else{ for(int g=1;g<s.length();g++){ if(s[g]<48||s[g]>57){ 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;h<s.length();h++){ if(s[h]<48||s[h]>57){ 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(不过考虑空间的话就不行了),然后就过了:
[code]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#)给出的很精妙,同时考虑了是否为回文串:
[code]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多一位循环才结束
- leetcode 刷题之路 10 Same Tree
- 算法系列(10)LeetCode520 Detect Capital
- LeetCode题目: 10. Regular Expression Matching
- LeetCode刷题之路(一)——easy的开始
- leetcode 刷题之路 48 Implement strStr()
- LeetCode(10)--RegularExpresssionMatching
- leetcode 刷题之路 67 Reverse Linked List II
- Leetcode个人题解10
- leetcode 557. Reverse Words in a String III(C语言)10
- 面试之路(10)-BAT面试之java实现单链表的插入和删除
- LeetCode - 10 - Regular Expression Matching
- leetcode 19:Remove Nth Node From End of List(15-10-7)
- 【leetcode 66题 之 10 斐波那契数列】
- [Leetcode 10, Hard] Regular Expression Matching
- 【一】Leetcode之Python刷题之路
- [leetcode] 10.Regular Expression Matching
- [leetcode]88. Merge Sorted Array[facebook](10)
- LeetCode刷题之路(三)——medium的进阶
- leetcode 10 Regular Expression Matching
- hadoop学习之路_10、自定义序列化类