今天登陆leetcode发现探索区多了字节跳动的专栏,特意用了一下午去刷,有些是之前刷过的。但题目不错,就当是复习一遍吧,这里记录一下我会的以及自己觉得不错的题目。
原题链接请点击题目
一:挑战字符串
分析:这题要求连续的不重复的最长子序列的长度,注意这里是需要连续,利用这个特性,我们可以维护一个窗口,窗口装的是无重复的字符串,一开始窗口的左边在起点(即下标为0处)。我们用 i 遍历字符串,如果当前字符没有出现过或者当前字符虽然出现过但不在当前的窗口,则窗口向右扩张。而当当前字符出现在窗口内,则窗口的左边收缩到当前字符前一个出现的位置。详见代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
//维护一个滑动窗口,最大的窗口大小即为结果
int hash[128] = {0};
int left = 0,res = 0;
for(int i = 0;i<s.size();i++)
{
if(hash[s[i]] == 0||hash[s[i]]<left) //1:没出现过 2:出现过但没在窗口内
res = max(res,i-left+1); //不断更新res值
else
left = hash[s[i]]; //窗口内出现重复,缩小左边窗口
hash[s[i]] = i+1;
}
return res;
}
};
简单题,以第一个字符串作为模版,逐个拿出模版的每个字符,然后其余的字符串也逐个拿出相对应位置的字符比较是否相同。注意字符串长度的问题即可。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return "";
string res = "";
for(int i = 0;i<strs[0].size();i++)
{
char c = strs[0][i]; //逐个拿出模版字符串的字符
for(int j = 1;j<strs.size();j++) //后面的字符串
{
if(i>=strs[j].size()||strs[j][i]!=c) //当i已经超过字符串的长度或者字符不相同时直接返回
return res;
}
res+=c;
}
return res;
}
};
分析:对s2全排再一一跟s1对比肯定会超时。所以我们可以维护两个窗口,一个窗口装s1,另一个窗口装s2。假设s1长度为len1,s2长度为len2。开始先分别装s1和s2的前lne1个字符进各自的窗口。如果此时两个窗口相等则直接返回true,如果不等则s2的窗口从len1开始装s2的字符,同时窗口的左边要删除一个元素,因为两个窗口要保持大小,期间如果两个窗口相等则返回true
class Solution {
public:
bool checkInclusion(string s1, string s2) {
//v1、v2维护一个大小相同的窗口,先计算出len1前的字符出现的次数,如果相等直接返回TURE,如果不等则操作v2继续往后走,后面的字符添上,窗口左边的字符删除
int len1 = s1.size(),len2 = s2.size();
vector<int> v1(128,0),v2(128,0);
for(int i = 0;i<len1;i++)
{
v1[s1[i]]++;
v2[s2[i]]++;
}
if(v1==v2) return true;
for(int i = len1;i<len2;i++) //v2从len1位置开始装
{
v2[s2[i]]++; //装新的字符
v2[s2[i-len1]]--; //删除早装入的字符
if(v1 == v2)
return true;
}
return false;
}
};
分析:这里主要开了三个数组来做,一个数组存第一个字符串,另一个数组存第二个字符串,最后一个数组存结果。保存两个字符串的时候要反着顺序存,因为我们平时做乘法的时候也是从数字的最后一位向前乘的,所以其实这道题主要是模拟了平时在纸上做的乘法。
class Solution {
public:
string multiply(string num1, string num2) {
int x[120] = {0},y[120] = {0},z[250] = {0};
int len1 = num1.size(),len2 = num2.size();
for(int i = len1-1,k = 0;i>=0;i--)
x[k++] = num1[i]-'0';
for(int i = len2-1,k = 0;i>=0;i--)
y[k++] = num2[i]-'0';
for(int i = 0;i<len1;i++) //在这里进行相乘,但没进位
{
for(int j = 0;j<len2;j++)
z[i+j] += (x[i]*y[j]);
}
for(int i = 0;i<249;i++) //现在进位
{
if(z[i]>9)
{
z[i+1] += z[i]/10;
z[i]%=10;
}
}
int i;
for(i = 249;i>=0;i--)
if(z[i] != 0)
break;
string res = "";
for(;i>=0;i--)
res+=(z[i]+'0');
if(res == "") return "0";
return res;
}
};
分析:主要做法就是先把整个字符串反转,然后开始遍历字符串,每遍历完一个单词(注意不是一个字符)的时候将这个单词再反转。
class Solution {
public:
void reverseWords(string &s) {
int index = 0,n = s.size();
reverse(s.begin(),s.end()); //反转整个字符串
for(int i = 0;i<n;i++)
{
if(s[i]!=' ') //遇到非空格的字符
{
if(index!=0)
s[index++] = ' ';
int j = i; //令j = i 进行下面的操作
while(j<n&&s[j]!=' ') //遍历完整一个单词
s[index++] = s[j++];
reverse(s.begin()+index-(j-i),s.begin()+index); //对刚才遍历的单词进行反转
i = j;
}
}
s.resize(index);
}
};
分析:一般题目问字符串有多少种可能的排列,十有八九都是用递归做的。我们需要先写一个函数判断一个字符串是否符合ip其中一个结点,符合的标准:1、1到3位长度的字符串。2、长度大于一的话首位不能为0。3、整数大小要在0~255的范围内。
接着就可以递归做正式工作。这里对字符串分别截取一位、二位、三位。。。判断是否能构成ip的一个结点,如果能的话就截断这部分,让剩余的部分递归下去继续做判断。
具体看代码
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
vector<string> res;
// string out = "";
helper(res,s,"",4);
return res;
}
void helper(vector<string>& res,string s,string out,int k)
{
if(k==0)
{
if(s.empty()) //注意点一,原字符串s应该要为空了
res.push_back(out);
}
else
{
for(int i = 1;i<=3;i++)
{
//截取某部分进行判断,如果合法则进入下一个递归
if(s.size()>=i&&isValid(s.substr(0,i))) //注意点二,越界判断
{
if(k==1) //k==1代表当前ip再添加多一个结点就够四个了
helper(res,s.substr(i),out+s.substr(0,i),k-1);
else
helper(res,s.substr(i),out+s.substr(0,i)+'.',k-1);
}
}
}
}
//判断是否合法
bool isValid(string s)
{
if(s.empty()||s.size()>3||(s.size()>1&&s[0]=='0'))
return false;
int num = atoi(s.c_str());
return num>=0&&num<=255;
}
};
二、数组与排序
class Solution {
public:
//做法类似双指针一头一尾向中间靠拢
//需要注意的是题目给出的是三个数字,那么我们只需让目标数(也即是0)减去第一个数得到的结果当做
//剩下两个数的目标和
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
for(int i = 0;i<nums.size();i++)
{
if(nums[i]>0) break;
if(i>0&&nums[i] == nums[i-1])
continue;
int target = 0-nums[i];
int j = i+1,k = nums.size()-1;
while(j<k)
{
if(nums[j]+nums[k] == target)
{
res.push_back({nums[i],nums[j],nums[k]});
while(j<k&&nums[j+1] == nums[j]) j++;
while(j<k&&nums[k-1] == nums[k]) k--;
j++;k--;
}
else if(nums[j]+nums[k]<target)
j++;
else
k--;
}
}
return res;
}
};
class Solution {
public:
int findLengthOfLCIS(vector<int>& nums) {
int res = 0,cnt = 0;
int cur = INT_MAX;
for(int num:nums)
{
if(num>cur)
cnt++;
else cnt = 1;
res = max(res,cnt);
cur = num;
}
return res;
}
};
三、链表与树
/**
* 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) {
int add = 0;
ListNode* res = new ListNode(0);
ListNode* head = res;
while(l1||l2||add)
{
int num = (l1?l1->val:0)+(l2?l2->val:0)+add;
head->next = new ListNode(num%10);
head = head->next;
add = num/10;
l1 = l1?l1->next:l1;
l2 = l2?l2->next:l2;
}
return res->next;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
//对于单链表,归并排序是最简单的了。merge函数实现的功能是合并两个排序链表
ListNode* sortList(ListNode* head) {
if(!head||!head->next) return head;
ListNode* slow = head,*fast = head,*pre = head;
while(fast&&fast->next)
{
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = NULL;
return merge(sortList(head),sortList(slow));
}
ListNode* merge(ListNode* a,ListNode* b)
{
ListNode* head = new ListNode(0);
ListNode* node = head;
while(a&&b)
{
if(a->val<b->val)
{
node->next = a;
a = a->next;
}
else
{
node->next = b;
b = b->next;
}
node = node->next;
}
if(a) node->next = a;
if(b) node->next = b;
return head->next;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
break;
}
if(!fast||!fast->next) return NULL;
slow = head;
while(fast != slow)
{
slow = slow->next;
fast = fast->next;
}
return fast;
}
};
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root||p==root||q==root) return root;
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right = lowestCommonAncestor(root->right,p,q);
if(left&&right) //p和q分别位于左右子树中
return root;
return left?left:right;
}
};
四、动态或贪心
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0,buy = INT_MAX;
for(auto c:prices)
{
buy = min(buy,c);
res = max(res,c-buy);
}
return res;
}
};
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
if(len<=1)
return 0;
vector<int> have(len);
vector<int> unhave(len);
have[0] = -prices[0];
int res = 0;
for(int i = 1;i<len;i++)
{
unhave[i] = max(unhave[i-1],have[i-1]+prices[i]);
have[i] = max(have[i-1],unhave[i-1]-prices[i]);
res = max(res,unhave[i]);
}
return res;
}
};
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if(matrix.empty()||matrix[0].empty()) return 0;
int m = matrix.size(),n = matrix[0].size();
vector<vector<int>> dp(m,vector<int>(n,0));
int res = 0;
for(int i = 0;i<m;i++)
{
for(int j = 0;j<n;j++)
{
if(i == 0||j == 0)
dp[i][j] = matrix[i][j]-'0';
else if(matrix[i][j] == '1')
dp[i][j] = min(min(dp[i-1][j-1],dp[i-1][j]),dp[i][j-1])+1;
res = max(res,dp[i][j]);
}
}
return res*res;
}
};