Leetcode-How-What 力扣Leetcode刷题指南
About the way how to use Leetcode wisely for preparing the interview and the solutions to some Leetcode problems’. 怎样为准备面试机智聪明的刷题(高效刷题)以及一些题目的题解。 同 github 链接: https://github.com/jiangzhouwang/Leetcode-How-What
How: 如何为准备面试机智的刷题
首先,明确理念和目的:
- 面试中会考察多个方面,编程题只是其中一方面而已,要拿出一部分精力做,同时还要准备面试中的其他部分,要结合个人情况来做题准备
- 考官出编程题目是为了考察面试者的算法和数据结构掌握情况,同时也能看出对编程语言的熟练度,甚至可以看出个人的写代码风格。
刷题方向:
- 明晰大方向:计算机考研或保研机试题目就做PAT+《王道机试指南》,准备互联网面试则刷Leetcode+《剑指offer》(能做的更多最好,本人结合多篇经验贴后的总结最少需要Leetcode,其次补充上书)
- 结合自己情况订计划和目标:包括两方面一方面是时间上的执行,另一方面是这段时间内做什么。
- 以下几条是本人在搜索多个经验贴后的总结:
- 若个人有一定基础,而非从零开始,直接先做Leetcode的两大专题,分别是"Top 100 Liked Questions"和"Top Interview Questions",在中文社区力扣中分别叫做“热题HOT100”以及“精选TOP面试题”。两个系列有一定重叠,总共题目大概在180道。这些题目涉及最常见题目,多数经典题目也包括其中,题目做完后需反复看,也很值得反复看。
- 做完1后可以在英文版的“Explore”栏中看到“Top Interview Questions”,中文版力扣则在“探索”栏中有“算法面试题汇总”。两者并不完全一致。中文版在“题库”下也有一个集合是“力扣精选算法200题”,但这一集合需要付费才可见可以提交,若只想看题目,可以以题目为关键词在搜索引擎搜索。 这里的3个系列个人进行取舍
- 至少做完第1点后,通过标签tag来做,以查漏补缺,对某一类型下从简单到难。如从数据结构角度不同tag:链表、栈、队列、哈希表、图、Trie、二叉树等;从算法角度不同tag:递归、分治、回溯搜索、贪心、动态规划、深度优先、广度优先、二分查找等
- 中文力扣中有 《剑指offer》 的题目,故可以直接刷起来。另外牛客网也有,也可刷题。
- 尽量多尝试不同思路。Discuss高投票解答会很有用。
- 如果时间紧任务重,个人一定要有轻重缓解来准备,如前面总结的几大重要的待刷题目系列中需要有一定的题量准备。可以配合第一遍时自己做,第二遍时看最优解法,后面复习直接眼睛看了就要清楚思路求解。
- 有一些APP和小程序可以配合使用,充分利用零碎时间。 如:力扣APP、牛客APP、校招王者小程序
刷题终极秘诀: “无他,唯手熟尔”
What:up主个人一些题目的题解
1, Two Sum
给定一个数字数组和一个目标数字 target,在数组中寻找2个下标点,使得指向的数字的和为 target 值
- 解法一: 暴力方法,两次循环
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> solution;
for (int i = 0; i < nums.size()-1; ++i){
for (int j = i+1; j < nums.size(); ++j){
if (nums[i] + nums[j] == target){
solution.push_back(i);
solution.push_back(j);
return solution; // 这一行可以不用写,因为第二个 for 循环是从 i+1开始,必然不会重复加入
}
}
}
return solution;
}
};
- 解法二:用空间换时间,map<>,存储的是 <nums[i],i>对,然后一次循环,遍历对每个 nums[i],判断 target-nums[i]是否存在 map<>中,有就可以。这里注意判断条件,是判断 target-nums[i]的值在 map 中且 对应的下标不是 i。为了防止 nums = [1,2,3,4], target=8, 最后输出 [3,3]了
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> solution;
map<int, int> imap;
for (int i = 0; i < nums.size(); ++i){
imap.insert(pair<int, int>(nums[i], i));
}
for (int i = 0; i < nums.size();++i){
int diff = target-nums[i];
map<int,int>::iterator ite = imap.find(diff);
if (ite != imap.end() && ite->second!=i){
solution.push_back(i);
solution.push_back(ite->second);
return solution; // 不能少,否则会重复添加,或者直接这里 return
}
}
return solution;
}
};
- 解法三:进一步空间换时间,map存储,只用一个循环,每次用 target-nums[i] 得到 diff,判断 diff 是否已经在 map 中,如果有那就可以输出了,如果没有则把 nums[i],i 放入 map 中。相当于对每个数判断做差的结果是否在列表,如果没有就把当前数进入列表做后面同样判断的待定差值。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> solution;
map<int, int> imap;
for (int i = 0; i < nums.size(); ++i){
int diff = target - nums[i];
map<int,int>::iterator iter = imap.find(diff);
if (iter != imap.end()){
solution.push_back(iter->second);
solution.push_back(i);
return solution;
}
imap.insert(pair<int,int>(nums[i],i));
}
return solution;
}
};
2, Element Math
给定两个表示倒序十位数字的list,如(2->4->3)和(5->6->4),代表342 和 465 两个数字,返回他们两个相加后的 list,即(7->0->8) 表示 807 数字。 给定语言中的前面有 NodeList 的定义。 题目思路清晰,坑点在于考虑全,两个 List 不同长度比较容易考虑到,可能少考虑的两种情况一种是 存在为空的 List,另一种是 (9->9) + (1) 要返回 (0->0->1),这里进位到最后要保留。
/**
* 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* ans=NULL;
ListNode** tmpnode = &ans;
int carry=0;
ListNode *p=l1,*q=l2;
int add1=0,add2=0,sum=0;
while(p!=NULL || q!=NULL || carry!=0){
if (p!=NULL){
add1 = p->val;
p = p->next;
}else
add1 = 0;
if (q!=NULL){
add2 = q->val;
q = q->next;
}else
add2 = 0;
sum = add1+add2+carry;
(*tmpnode) = new ListNode(sum%10);
carry = sum/10;
tmpnode = &((*tmpnode)->next);
}
return ans;
}
};
3, Longest Substring Without Repeating Characters
题意:寻找一个 string 中最长的内部不出现重复字符的子串的长度。如 “abcabcbb” 返回 3,“bbbb”返回 1。
思路:用一个数组每次初始全为0表示每个字符都没有出现,然后两层循环外层是循环给定string每个字符表示开始,内层循环从这个字符往后寻找还没有出现的字符,统计内层的长度来每次更新。
思考:我想到子串可能是空的,但是没注意此题有个大坑点,就是这个 string 居然可以是一个空格,我惊了,也就是 " ", 返回 1。 本来我想用 set 判断是否出现重复字符,后来发现不行,回顾下来应该也是把空格放进去出问题了,因为我设置的是 set,所以改用了数组,一开始是 alpha[s[temp]-‘A’]=1 来把出现的字符设置为 1,但是也是由于空格类字符的缘故,就出现报错说 index是负的多少,超过了数组的范围,所以改用直接int 得到 ascill 码。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if (s.size() == 0)
return 0;
int beginindexofsubstring=0; // 存储子串的开始下标
int maxnumofsubstring=0;
while (beginindexofsubstring < s.size()){
int temp = beginindexofsubstring;
int alpha[270] = {
0}; // 初始所有字母均没有
int cnt=0;
int ascii= s[temp];
while(temp<s.size() && alpha[ascii] == 0){
alpha[ascii] = 1;
temp++;
ascii= s[temp];
cnt++;
}
maxnumofsubstring = max(maxnumofsubstring, cnt);
beginindexofsubstring = beginindexofsubstring+1;
}
return maxnumofsubstring;
}
};
4, Median of Two Sorted Arrays
给定两个本身已经排序的数列,求得两个数列合并后的中位数。 定义奇数个数的数列中位数为最中间数,偶数个的为中间两个数字的算数平均数。 且题目要求算法复杂度不超过 O(log(m+n))。 解:直接两个合并后用 sort,再返回中位数即可。不过太取巧了
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
for (int i = 0; i< nums1.size(); ++i){
nums2.push_back(nums1[i]);
}
int length=nums2.size();
sort(nums2.begin(), nums2.begin()+length);
if (nums2.size() & 1 ==1){
//奇数
return nums2[length/2];
}else{
return (nums2[length/2]+nums2[length/2-1])*1.0/2;
}
}
};
5, Longest Palindromic Substring
题意:给定一个小写字母组成的字符串,求其最长的回文子串是什么。例子 "babad"返回 bab, aba 之一即可。
解答:因为题目说只有 1000 大小,所以暴力可解。我是参考了dicuss 中的,得到了滑动窗口的解法。即 index 从头到尾滑动,以 index 作为子串的中心,先设定头尾两个指针同 index,接着判断 尾+1是否与 尾指针指向的 字符相同,相同则尾指针++,之后判断 头尾相加是否相同,是则头减尾加。
class Solution {
public:
string longestPalindrome(string s) {
// slidding window solution
int maxlen=0,index=0,start=0,end=0,ansstart=0;
while(index < s.size()){
start=index;
end=index;
while(end < s.size() && s[end]==s[end+1])
end++;
index = end+1;
while(start-1>=0 && end+1<s.size() && s[start-1]==s[end+1]){
start--;
end++;
}
if (end-start+1 > maxlen){
ansstart=start;
maxlen=end-start+1;
}
}
string anss;
return s.substr(ansstart,maxlen);
}
};
7, Reverse Integer
翻转数字,给定一个 int 型数字把它整体左右翻转并返回还为int型,注明如果超出 int 的范围则返回 0。 解:为了处理超过int范围怎么弄,故用 long / long long 来处理 //4 ms, faster than 69.82%; 8.2 MB, less than 95.24%
class Solution {
public:
int reverse(int x) {
long long ans = 0;
while (x != 0){
ans = ans*10+x%10;
x /= 10;
}
if (ans > INT_MAX || ans < INT_MIN)
return 0;
return ans;
}
};
8, String to Interger(atoi)
题意:实现字符串中从左起第一个有效数字字符串到整型数字的转变。 若是空串或全是空格则返回 0,若左起第一个非空格字符非有效数字字符串如" w123"则返回 0。 解:本题题意简单,但是我做的比较费力,我是先把所有该返回 0 的都返回,然后再开始计算。实际并不需要的。 解法一是我做出来的模拟,但是很复杂,其实不必要。
- 解法一: 0 ms, faster than 100.00% 8.6 MB, less than 31.34%
class Solution {
public:
int myAtoi(string str) {
if (str.size() == 0) return 0;
int index = 0;
while (index < str.size() && str[index] == ' ')
index++; // 到第一个不是空白的位置
if (index == str.size()) // 直接到了尾,即 string 全是空白 ' '
return 0;
if (index == str.size()-1 && !('0'<=str[index] && str[index]<='9')) // index 指向最后一个位置,且还不是数字时, 返回 0
return 0;
if ((str[index] == '-' || str[index] == '+') && !('0'<=str[index+1]&&str[index+1]<='9')) //index指向+/-号,且后一位不是数字时,返回 0
return 0;
if (!(('0'<=str[index]&&str[index]<='9')||(str[index]=='-')||(str[index]=='+'))) //index 指向的是非数字,非正负号时,返回 0
return 0;
//接下来index 指向的位置就只能是数字 或 + - 数字要注意范围
int posiornega = 1;
if (str[index] == '-'){
posiornega = -1;
index++;
}else if (str[index] == '+'){
posiornega = 1;
index++;
}
// 这里需要处理 -0000123 => 123 0023=>23 +0023=>23 注意 -000 +000 0000 都返回 0
while (index < str.size() && str[index]=='0')
index++;
if (index == str.size())
return 0;
//到这里把前导零的给处理了, 开始正式处理数字
long long num = 0;
while (index < str.size() && ('0'<=str[index]&&str[index]<='9')){
num = num*10+ str[index]-'0';
++index;
if (num*posiornega>=INT_MAX) return INT_MAX;
if (num*posiornega<=INT_MIN) return INT_MIN;
}
return num * posiornega;
}
};
- 解法二:Discuss其他人的题解
int myAtoi(string str) {
int ret = 0, sign = 1, i = str.find_first_not_of(' '), base = INT_MAX / 10;
if (str[i] == '+' || str[i] == '-') sign = str[i++] == '+' ?: -1;
while (isdigit(str[i])) {
if (ret > base || (ret == base && str[i] - '0' > 7))
return sign > 0 ? INT_MAX : INT_MIN;
ret = 10 * ret + (str[i++] - '0');
}
return sign * ret;
}
10, Regular Expression Matching
题意:给定两个 string s 和 p,其中 s 纯由小写字母组成,p 中有点 . 和星 * 符号,其中点可以代替1个任意字母,星则可以把星号前一个字符进行任意次重复,题意判断 s 和 p 是否匹配,返回 true 或 false。如 “ab” 和 .* 匹配,因为 * 可以把 . 再重复一次。 “aab” 和 c*a*b 可以匹配。题目没说的情况是两个字符串可以为空。 难题
解法:这道题首先要明白是 dp 问题,然后关键在于看清楚最优子结构。使用 dp[i][j] 来表示 s 字符串从开头到 s[i] 的子串和 p 字符串从开头到 p[j]的子串是否匹配,注意加上头行和头列,dp[i][j]实际是 s[i-1]和p[j-1]比较,注意初始化头行和头列时 p 可以使用 * 来匹配空串。则若i-1,j-1此时指向的2个字符相同或j-1指向点号,则当前匹配成功,dp[i][j]结果为dp[i-1][j-1]的值,即 if(s[i-1] == p[j-1] || p[j-1] ==’.’) dp[i][j]=dp[i-1][j-1] ; 若j-1指向星号,由于星号可以让星号前一个位置出现任意次,那么若出现0次即前面这个符号就直接没了,此时 dp[i][j]=dp[i][j-2],在此基础上,若星号表示出现1次则相当于此星号j-1指向位置为空,就需要判断i-1,j-2各指向字符相同或j-2指向为点号情况下,前一个结果或运算上dp[i-1][j],这里一行写出来即为 dp[i][j] = dp[i][j-2] | (dp[i-1][j] && (s[i-1] == p[j-2] || p[j-2] == ‘.’))。 这里给出 aab 和 .*a*b 的dp 数组,1 为 True, 0 为 false:
\ | 空 | . | * | a | * | b |
---|---|---|---|---|---|---|
空 | 1 | 0 | 1 | 0 | 1 | 0 |
a | 0 | 1 | 1 | 1 | 1 | 0 |
a | 0 | 0 | 0 | 1 | 1 | 0 |
b | 0 | 0 | 0 | 0 | 0 | 1 |
思考:建议遇到这种题就拿纸笔画一下二维的横纵列,多试几组不同数据。 另外在做这个题发现自己的知识漏洞,见下面总结
!逻辑非 && 逻辑与 || 逻辑或 , 顺序是 非与或。 &按位与, | 按位或。 设 a=0xaa, b=0x55 则 a&&b=true, a||b=true, a&b=0x0,a|b=0xff。 “&&”跟“|”没有关系,在用“||”的地方一般也可以用|代替,但是用“|”的地方不能用“||”代替。但是注意,运算符顺序是 算数运算>移位运算>位运算>逻辑运算。 1 << 3 + 2 & 7等价于 (1 << (3 + 2))&7,所以这道题中一个式子有多个进行运算,就不能把 | 和 || 替代,除非加括号。
11, Container With Most Water
题意:题目给定一个非负数数列,保证至少有两个数字。数字代表高度,任取两个数字,较小的数字表示的高度乘以两个数字下标距离表示一个蓄水池的横截面面积,求这个数列中能得到的最大面积是多少。例子:[1,8,6,2,5,4,8,3,7] 返回 49。
解:第一个反应是暴力,两次循环,每次更新最大的面积。 看了Discuss才知道两指针法,即一个指向头一个指向尾,计算一次面积后更新最大面积,并使得数字小的那个指针往对面走一个位置,直到两个指针指向同样位置结束
class Solution {
public:
int maxArea(vector<int>& height) {
int front=0,back=height.size()-1,maxamout=0;
while(front != back){
maxamout = max(maxamout, (back-front)*min(height[front],height[back]));
if (height[front] < height[back])
++front;
else
--back;
}
return maxamout;
}
};
13, Roman to Integer
罗马数字转10进制数字。给定罗马数字转换规则和一个字符串,求转换后的 10 进制数字。 Easy 题目,纯模拟。
class Solution {
public:
int romanToInt(string s) {
int alpha[26] = {
0};
alpha['I'-'A']=1; alpha['V'-'A']=5; alpha['X'-'A']=10; alpha['L'-'A']=50;
alpha['C'-'A']=100; alpha['D'-'A']=500; alpha['M'-'A']=1000;
int sum = 0;
int i = 0;
while (i < s.size()){
if (s[i] == 'I'){
if (i+1<s.size() && (s[i+1] == 'V' || s[i+1] == 'X')){
if (s[i+1] == 'V')
sum += 4;
else
sum += 9;
i+=2;
}else{
sum += 1;
i++;
}
}else if (s[i] == 'X'){
if (i+1<s.size() && (s[i+1]=='L'||s[i+1]=='C')){
if (s[i+1]=='L')
sum+=40;
else
sum+=90;
i+=2;
}else{
sum+=10;
i++;
}
}else if (s[i] == 'C'){
if (i+1<s.size() && (s[i+1]=='D'||s[i+1]=='M')){
if (s[i+1] == 'D')
sum+=400;
else
sum+=900;
i+=2;
}else{
sum += 100;
i++;
}
}else{
sum += alpha[s[i]-'A'];
i++;
}
}
return sum;
}
};
14, Longest Common Prefix
寻找给定几个字符串的相同的前缀子字符串。 Easy 题目,模拟,相当于模拟每列字符是否相同,所以两层循环,外层是第一个字符串的字符,内层是对其同列的对应位置其他字符串的字符比较。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
string ans;
if (strs.size() == 0) return ans;
for (int j = 0; j < strs[0].size(); ++j){
char ch = strs[0][j];
bool flag = true;
for (int i = 1; i < strs.size(); ++i){
if (j >= strs[i].size() || strs[i][j] != ch){
flag = false;
break;
}
}
if (flag == true) ans+=ch;
else break;
}
return ans;
}
};
15, 3Sum
题目:给定一个数字序列,输出所有 3 个数字之和为 0 的数字组合。如 [-1, 0, 1, 2, -1, -4],target=0,输出为 [[-1,0,1],[-1,-1,2]]。注意不能重复输出。
解析:是第一题2 Sum 的扩展。两层循环可解。2Sum 是一层循环可解。3Sum 就是先sort 后,外层要循环每个出现的不重复数字,里层循环是剩下的右边序列中是否存在两个值之和为 0-外层循环的数字。 用两个指针一前一后往中间走。
我这里错的:1,没有考虑数字序列可能不足 3个数字要返回空序列 2,防止 [-2,0,0,2,2] 输出两次 [-2,0,2]所以要在里层相同后 ++front 和 --back 后还要判断继续 ++ 和 --。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
if (nums.size()<3)
return ans;
sort(nums.begin(),nums.end());
for (int indexbegin = 0; indexbegin < nums.size()-2; ++indexbegin){
if (indexbegin>0 && nums[indexbegin]==nums[indexbegin-1])
continue;
if (nums[indexbegin]+nums[indexbegin+1]+nums[indexbegin+2]>0)
break;
if (nums[indexbegin]+nums[nums.size()-2]+nums[nums.size()-1]<0)
continue;
int front = indexbegin+1, back = nums.size()-1;
int tmp = 0-nums[indexbegin];
while (front < back){
if (nums[front]+nums[back] == tmp){
ans.push_back({
nums[indexbegin],nums[front],nums[back]});
++front;
while(nums[front]==nums[front-1] && front<back) //必须要有,防止 [-2,0,0,2,2] 输出两次 [-2,0,2]
++front;
--back;
while(nums[back]==nums[back+1]&&front<back)
--back;
}else if (nums[front]+nums[back] < tmp){
front++;
}else{
back--;
}
}
}
return ans;
}
};
17, Letter Combination of a Phone Number
题目:手机9宫格输入键盘上 2~9 每个数字是对应好几个字母的,问给定一个数字序列,输出所有可能的对应字母组合,不要求按特定顺序返回。如输入 “23” 输出 [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”] 。
思路:DFS,当此时 index 大于长度则返回上层,当此时 index 等于长度则添加构建好的 string,否则 对此时对应的所有字符 在此层加入到构建 string 中并+1 层往下 DFS 。这里我把定义函数写在了最后面 }; 后面是不行的,要写在里面。其次是要注意定义函数的 vector & ans,这里要有 &表示是要改变它的。
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> ans;
if (digits.size()==0)
return ans;
map<char,string> mapchar = {
{
'2',"abc"},{
'3',"def"},{
'4',"ghi"},{
'5',"jkl"},{
'6',"mno"},{
'7',"pqrs"},{
'8',"tuv"},{
'9',"wxyz"}};
string tmp="";
int ii=5;
dfs(ans,digits,0,tmp,mapchar);
return ans;
}
void dfs(vector<string> &ans, string digits, int index, string inputstring, map<char,string> mapchar){
if (index == digits.size()){
return;
}
string stemp = mapchar[digits[index]];
for (int i = 0; i < stemp.size(); ++i){
if (index == digits.size()-1)
ans.push_back(inputstring+stemp[i]);
else
dfs(ans,digits,index+1,inputstring+stemp[i],mapchar);
}
}
};
19, Remove Nth Node From End of List
给定一个链表和数字n,求删除倒数第 n 个数字后的链表。思路:用2个指针指向的位置隔着 n个空,这样一遍循环靠尾的指针到头时,靠头指针指向的就是要删除的,而为了删除它,所以还需要第 3 个指针指向它的前面。另外需要注意判断删除的正好是第一个位置时情况。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode*p=head,*q=head,*before=head;
if (!head)
return head;
while(n>1){
q = q->next;
n--;
}
int cnt=1;
while(q->next != NULL){
q = q->next;
p = p->next;
if (cnt!=1)
before = before->next;
else
++cnt;
}
if (cnt!=1)
before->next = p->next;
else
head = head->next;
return head;
}
};
20, Valid Parentheses
题目判断一个由 {}()[] 6 种字符组成的序列是否是合法的。“合法的定义是 开型括号必须由相同类型闭括号关闭 且 开型括号的关闭顺序必须按照一定顺序” 。 补充几个 false 的例子: “)))” “((((” “(]” “)(” 。本题是学数据结构的栈时的经典例题,简单。但是我这里测试时出问题,是在栈为空的时候还取 brackets.top(),这种会报错。
class Solution {
public:
bool isValid(string s) {
stack<char> brackets;
for (int i = 0; i < s.size(); ++i){
if (s[i] == '(' || s[i]=='[' || s[i]=='{'){
brackets.push(s[i]);
}else{
if (s[i] == ')' && !brackets.empty() && brackets.top()=='(')
brackets.pop();
else if (s[i] == ']' && !brackets.empty() && brackets.top()=='[')
brackets.pop();
else if (s[i] == '}' && !brackets.empty() && brackets.top()=='{')
brackets.pop();
else
brackets.push(s[i]);
}
}
return brackets.empty();
}
};
21, Merge Two Sorted Lists
题目给定两个已排序好的链表,返回两个链表合并后的排序链表。题目分类是 Easy。 思路比较简单:在两个链均表不空下,把两个中较小的节点插入新链表中,若之一为空则插入另一个的指向位置。链表的操作还是要熟悉。比如 ListNode ans(0); ListNode *tmp=&ans; 最后返回时是 return ans.next; 而不是 return ans->next,因为 ans 是一个节点而不是一个指针。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode ans(0);
ListNode *tmp=&ans;
while(l1 != NULL || l2 != NULL){
if (l1 == NULL){
tmp->next = l2;
break;
}
if (l2 == NULL){
tmp->next = l1;
break;
}
if (l1->val < l2->val){
tmp->next = l1;
l1 = l1->next;
}else{
tmp->next = l2;
l2 = l2->next;
}
tmp = tmp->next;
}
return ans.next;
}
};
22, Generate Parentheses
给定数字 n,生成 n 对 “(“和”)“组成的所有有效括号。如 n= 3, [”((()))”,"(()())","(())()","()(())","()()()"] ; n=2下 ["(())","()()"] 。 有 DP 解法和递归解法。DP 解法是 vector< vector > dp(n+1, vector()); dp[0] = “”; dp[i]=’(’+ dp[k]+’)’+dp[i-1-k],k=0…i-I 递归解法是传递 2 个参数 l 和 r,在 l 大于 0 时添加 ’ ( ’ 并 --l ,再 l < r 时传递’ ) ’ 并 --r,在 l 和 r 都为 0 时添加此时的 string
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;
dfs(n,n,"",ans);
return ans;
}
void dfs(int l, int r, string path, vector<string> &ans){
if (l==0 && r==0){
ans.push_back(path);
return;
}
if (l>0)
dfs(l-1,r,path+"(",ans);
if (l<r)
dfs(l,r-1,path+")",ans);
}
};
23, Merge k Sorted Lists
给定一个已经排好序且存入 vector<ListNode*> lists 的很多个 List,合并成一个有序 list 并返回。我想到的思路是设置 size 大小的指针指向每个 list 不断比较依次得到最小的并返回。后面看了solution 后转变想法为设计一个合并 2 个 list 的函数,主函数中一遍循环依次把后面的合并到第一个位置。但是超时了。因为有一个测试例子很长,而这种方法是每次都在第一个上循环一次,时间消耗就多了。所以改为一种分治思想,两个待定字符串等长下相比于一个很长一个很短就会有较小的比较次数,所以思想是不断折叠这个 vector 使得最头最尾、次头次尾……两两结合,直到折叠到 vector 中只有 1 个ListNode*
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
int size = lists.size();
if (size == 0)
return NULL;
if (size == 1)
return lists[0];
while (size>1){
for (int i = 0; i < size/2; ++i)
lists[i] = merge2Lists(lists[i], lists[size-i-1]);
size = (size+1)/2;
}
return lists[0];
}
ListNode* merge2Lists(ListNode *list1,ListNode *list2){
if (list1==NULL)
return list2;
if (list2==NULL)
return list1;
ListNode *l,*ltmp;
if (list1->val <= list2->val){
ltmp = new ListNode(list1->val);
list1 = list1->next;
}else{
ltmp = new ListNode(list2->val);
list2 = list2->next;
}
l = ltmp;
while (list1!=NULL && list2!=NULL){
if (list2==NULL || list1->val<=list2->val){
ltmp->next = new ListNode(list1->val);
ltmp = ltmp->next;
list1 = list1->next;
}
if (list1==NULL || list2->val<=list1->val){
ltmp->next = new ListNode(list2->val);
ltmp = ltmp->next;
list2 = list2->next;
}
}
if (list1!=NULL)
ltmp->next = list1;
else
ltmp->next = list2;
return l;
}
};
26, Remove Duplicates from Sorted Array
在从小到大排好序的序列中去掉重复数字,并同时返回不重复数字有几个。 感觉题目一开始没有说清,还是靠下面解释和 run 时候的样例比较才明白,实际中去掉重复数字是把原序列的不重复的放到序列前面去。那两个指针就可以。从左端开始,两个指针,一个走得快,一个在开始位置,走得快的和慢的比数字大小,相同则快的往前走,不同则复制到慢的那里。 简单题目。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if (nums.size() == 0) return 0;
if (nums.size() == 1) return 1;
int left = 0, right = 1;
while (right < nums.size()){
while (right < nums.size() && nums[right] == nums[left])
right++;
if (right == nums.size())
break;
left++;
nums[left] = nums[right];
right++;
}
return left+1;
}
};
28, Implement strStr()
实现在一个字符串中搜索另一个字符串首次出现的下标位置函数。两层循环暴力 4 ms, faster than 91.53%
class Solution {
public:
int strStr(string haystack, string needle) {
int haylen = haystack.size(), needlen = needle.size();
if (needlen == 0) return 0;
if (needlen > haylen) return -1;
int ans = 0,i=0;
for (; i <= haylen-needlen; ++i){
int j = 0;
for (; j < needlen && haystack[i+j] == needle[j]; ++j);
if (j == needlen)
break;
}
return i == haylen-needlen+1?-1:i;
}
};
29, Divide Two Integers
不用除法操作、取余操作、乘法操作,求两个整数相除结果,已保证被除数不为 0,如果结果超过 int 范围,则返回 INT_MAX。 解:我想到可能直接减会 TLE,然后想的是每次减的数字是之前被减数加上自己的值,即 17/3 , 17-3, 14-6, 8-3。但是没有这么做,看了两个题解,解法一是同样的,不过用了位运算左移代替了自加本身。 第二种方法则是用了 log()函数,想法是 a/b = e^(ln(a)) / e^(ln(b)) = e^(ln(a)-ln(b))
class Solution {
public:
int divide(int dividend, int divisor) {
if (dividend == INT_MIN && divisor == -1) return INT_MAX;
long absdivid = abs((long)dividend), absdivisi = abs((long)divisor), ans= 0;
int sign =