给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。
你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
我的做法如下,直接暴力,复杂度是O(n2),我们试图通过遍历数组的其余部分来寻找它对应的目标,这将耗费O(n)*O(n)的时间。
class Solution {
public:
vector twoSum(vector& nums, int target)
{
vector res;
for(int i=0;i
也可以使用哈希表
vector twoSum(vector& nums, int target) {
vector twoSum;
map tmpmap;//键值为nums的值,变量值为nums下标
for (int i = 0; i < nums.size(); i++) {
tmpmap[nums[i]] = i;
}
for (int i = 0; i < nums.size(); i++) {
if (tmpmap.count(target - nums[i]) != 0 && tmpmap[target-nums[i]]!=i) {// 如果目标值减去循环处的值存在,且它对应的下标不为i,即存在有另一个数与循环值相加等于target,则返回结果
twoSum.push_back(i);
twoSum.push_back(tmpmap[target - nums[i]]);
break;
}
}
return twoSum;
}
优化版的哈希表如下:
vector twoSum(vector& nums, int target) {
vector twoSum;
map tmpmap;//键值为nums的值,变量值为nums下标
for (int i = 0; i < nums.size(); ++i) {
if (tmpmap.count(nums[i]) != 0) {
twoSum.push_back(tmpmap[nums[i]]);
twoSum.push_back(i);
break;
}
tmpmap[target - nums[i]] = i;
}
return twoSum;
}
给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
例如:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
我一开始的做法:错的!
#include
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
{
int num1,num2=0;
vector vec1;
vector vec2;
while(l1)
{
vec1.push_back(l1->val);
l1=l1->next;
}
while(l2)
{
vec2.push_back(l2->val);
l2=l2->next;
}
for(int i=0;inext=new ListNode(yushu);
p=p->next;
shang=shang/10;
yushu=shang%10;
}
p->next=NULL;
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* addTwoNumbers(ListNode* l1, ListNode* l2)
{ ListNode target(0); //头结点
ListNode* node = ⌖ //结点
int sum = 0; //每个结点的和
while (l1 != NULL || l2 != NULL) {
sum /= 10; //求本次的进位
if (l1 != NULL) {
sum += l1->val;
l1 = l1->next;
}
if (l2 != NULL) {
sum += l2->val;
l2 = l2->next;
}
node->next = new ListNode(sum % 10); //该结点的值 就是结点和的余数
node = node->next; //指向下一个结点
}
if (sum / 10 == 1) //对最后一个结点进行处理
node->next = new ListNode(1);
return target.next;
}
};
给定一个字符串,找出不含有重复字符的最长子串的长度。
示例:
给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3。
给定 "bbbbb" ,最长的子串就是 "b" ,长度是1。
给定 "pwwkew" ,最长子串是 "wke" ,长度是3。请注意答案必须是一个子串,"pwke" 是 子序列 而不是子串。
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int begin=0;
int result=0;
int char_map[128]={0};
string word="";
for(int i=0;i1)
{
char_map[s[begin]]--;
begin++;
}
word="";
for(int j=begin;j<=i;j++)
{
word += s[j];
}
}
}
return result;
}
};
更简便的方法,维护一个窗口
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
map m;
int left=0,res=0;
for(int i=0;i
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。
请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。
你可以假设 nums1 和 nums2 均不为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
中位数是 (2 + 3)/2 = 2.5
class Solution {
public:
double findMedianSortedArrays(vector& nums1, vector& nums2)
{
if(nums1.size()==0 && nums2.size()==0) return 0;
vector vec;
double res=0;
int i=0,j=0;
while(i
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba"也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
class Solution {
public:
string longestPalindrome(string s) {
const int len = s.size();
if(len <= 1)return s;
int start, maxLen = 0;
for(int i = 1; i < len; i++)
{
//寻找以i-1,i为中点偶数长度的回文
int low = i-1, high = i;
while(low >= 0 && high < len && s[low] == s[high])
{
low--;
high++;
}
if(high - low - 1 > maxLen)
{
maxLen = high - low -1;
start = low + 1;
}
//寻找以i为中心的奇数长度的回文
low = i- 1; high = i + 1;
while(low >= 0 && high < len && s[low] == s[high])
{
low--;
high++;
}
if(high - low - 1 > maxLen)
{
maxLen = high - low -1;
start = low + 1;
}
}
return s.substr(start, maxLen);
}
};
将字符串 “PAYPALISHIRING” 以Z字形排列成给定的行数:
P A H N
A P L S I I G
Y I R
之后从左往右,逐行读取字符:"PAHNAPLSIIGYIR"
实现一个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"
示例 2:
输入: s = "PAYPALISHIRING", numRows = 4
输出: "PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
class Solution {
public:
string convert(string s, int numRows) {
int len = s.length();
int nodeLen = 2*numRows-2;//两整列之间的差 也就是等差数列中的d
string result = "";
if (len == 0 || numRows == 0 || numRows == 1)//特殊情况特殊处理
return s;
for (int i = 0; i < numRows; i++)//从第一行遍历到最后一行
for (int j = i; j < len; j += nodeLen) {
result += s[j];//第一行和最后一行 还有普通行的整列数字
if (i != 0 && i != numRows-1 && j - 2*i + nodeLen < len)
result += s[j - 2*i + nodeLen];//单列行的数字
}
return result ;
}
};
给定一个 32 位有符号整数,将整数中的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
class Solution {
public:
int reverse(int x) {
int ans = 0;
while (x) {
int temp = ans * 10 + x % 10;
if(temp/10 !=ans)
{
return 0;
}
ans = temp;
x /= 10;
}
return ans;
}
};
LINK
实现 atoi,将字符串转为整数。
在找到第一个非空字符之前,需要移除掉字符串中的空格字符。如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值。如果第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
字符串可以在形成整数的字符后面包括多余的字符,这些字符可以被忽略,它们对于函数没有影响。
当字符串中的第一个非空字符序列不是个有效的整数;或字符串为空;或字符串仅包含空白字符时,则不进行转换。
若函数不能执行有效的转换,返回 0。
说明:
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。如果数值超过可表示的范围,则返回 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 sign=1;
long res=0;
int i=0;
if(str.empty())
{
return 0;
}
while(str[i]==' ')
{
i++;
}
if(str[i]=='-' || str[i]=='+')
{
sign= (str[i]=='+')?1:-1;
i++;
}
while(str[i]>='0' && str[i]<='9')
{
res=res*10+(str[i]-'0');
i++;
if(res*sign>=INT_MAX) return INT_MAX;
if(res*sign<=INT_MIN) return INT_MIN;
}
return res*sign;
}
};
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
class Solution
{
public:
bool isPalindrome(int x)
{
if(x<0) return false;
int tmp=x;
int reverse=0;
while(tmp)
{
reverse=reverse*10+tmp%10;
tmp=tmp/10;
}
if(x==reverse)
{
return true;
}
return false;
}
};
给定一个字符串 (s) 和一个字符模式 §。实现支持 ‘.’ 和 ‘*’ 的正则表达式匹配。
‘.’ 匹配任意单个字符。
‘*’ 匹配零个或多个前面的元素。
匹配应该覆盖整个字符串 (s) ,而不是部分字符串。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:
输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:
s = "aa"
p = "a*"
输出: true
解释: '*' 代表可匹配零个或多个前面的元素, 即可以匹配 'a' 。因此, 重复 'a' 一次, 字符串可变为 "aa"。
示例 3:
输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。
示例 4:
输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 'c' 可以不被重复, 'a' 可以被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入:
s = "mississippi"
p = "mis*is*p*."
输出: fal
class Solution {
public:
bool isMatch(string s, string p) {
if (p.empty()) return s.empty();
if (p.size() == 1) {
return (s.size() == 1 && (s[0] == p[0] || p[0] == '.'));
}
if (p[1] != '*')
{
if (s.empty()) return false;
return (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
}
while (!s.empty() && (s[0] == p[0] || p[0] == '.')) {
if (isMatch(s, p.substr(2))) return true;
s = s.substr(1);
}
return isMatch(s, p.substr(2));
}
};
LINK
给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
bool cmp(pair &a,pair &b)
{
return a.second>b.second;
}
class Solution {
public:
int maxArea(vector& height)
{
int len=height.size();
if(len<2) return 0;
int res=0;
vector > index_height;
for(int i=0;i
我的做法是错的,以为只需要找到最大的两个数,其实不是!还需要考虑index的距离。
还有一种做法,不知道哪里错了。(后来发现是temp的地方放错了!!!)
class Solution {
public:
int maxArea(vector& height)
{
int len=height.size();
if(len<2) return 0;
int start=0;
int end=len-1;
int h=0;
int res=0,temp=0;
while(startres)
{
res=temp;
}
if(height[start]
下面的这个方法可以ac:
class Solution {
public:
int maxArea(vector& height)
{
int len=height.size();
if(len<2) return 0;
int start=0;
int end=len-1;
int h=0;
int result=0,temp=0;
while(startres)
{
res=temp;
}
if(height[start]
LINK
我的做法如下:
class Solution {
public:
string intToRoman(int num)
{
string res;
vector roman={"M","D","C","L","X","V","I"};
vector val={1000,500,100,50,10,5,1};
int i=0;
for(int i=0;num !=0;i++)
{
while(num!=0)
{
if(num>=val[i])
{
num -= val[i];
res += roman[i];
}
}
}
return res;
}
};
参考做法
class Solution {
public:
string intToRoman(int num)
{
string res;
vector roman={"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
vector val={1000,900,500,400,100,90,50,40,10,9,5,4,1};
for(int i=0;num !=0;i++)
{
while(num>=val[i])
{
num -= val[i];
res += roman[i];
}
}
return res;
}
};
LINK
class Solution {
public:
int romanToInt(string s)
{
unordered_map char_map={{'I',1},{'V',5},{'X',10},{'L',50},{'C',100},{'D',500},{'M',1000}};
int sum=char_map[s.back()];
for(int i=0;i
链接:
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
class Solution {
public:
vector> threeSum(vector& nums)
{
vector > res;
int len=nums.size();
sort(nums.begin(),nums.end());
for(int i=0;i0)
{
k--;
}
else if(nums[i]+nums[j]+nums[k]<0)
{
j++;
}
else
{
res.push_back({nums[i],nums[j],nums[k]});
while(nums[j+1]==nums[j])
{
j++;
}
while(nums[k]==nums[k-1])
{
k--;
}
j++;
}
}
while(nums[i+1]==nums[i])
{
i++;
}
}
return res;
}
};
启发于4数之和的做法,有以下的代码:
class Solution
{
public:
vector > threeSum(vector &num) //相当于target==0
{
vector > res;
if(num.size()<3) return res;
sort(num.begin(),num.end());
for(int i=0;itarget_2)
{
right--;
}
else if(sum_2==target_2)
{
vector item(3,0);
item[0]=num[i];
item[1]=num[left];
item[2]=num[right];
res.push_back(item);
while(num[left]==item[1]) left++;
while(num[right]==item[2]) right--;
}
}
while(num[i+1]==num[i]) i++;
}
return res;
}
};
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2(-1 + 2 + 1 = 2).
可以通过的做法之一,使用左右指针往中间夹逼。
class Solution {
public:
int threeSumClosest(vector& nums, int target)
{
//int closestSum=nums[0]+nums[1]+nums[2];
int closestSum=0;
int diff=99999999999999999;
//int diff=abs(closestSum-target);
sort(nums.begin(),nums.end());
for(int i=0;inew_diff)
{
diff=new_diff;
closestSum=sum;
}
if(sum
定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1:
不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
class Solution {//使用递归回溯做法。非常妙!
public:
vector letterCombinations(string digits)
{
vector res;
if(digits.size()==0) return res;
string local;
vector > table{{'w'}, {'w'}, {'a','b','c'}, {'d','e','f'}, {'g','h','i'}, {'j','k','l'},
{'m','n','o'}, {'p','q','r','s'}, {'t','u','v'}, {'w','x','y','z'}};//前两个,随便定义。。。。
generate(table,res,local,0,digits);
return res;
}
private:
void generate(vector > &table,vector &res,string &local,int index,string digits)
{
int digit=digits[index]-'0';
if(index==digits.size())
{
res.push_back(local);
}
if(index>digits.size()) return;
for(int i=0;i
大坑:
实际上这行代码是省略了的,因为对于一个字符串来说,结束符是‘\0’,index加到最后面递归函数自然会return,所以可以不用return。我一开始,是直接在res.push_back(local);
的下一行加return,那肯定是错的。因为index还没大于digits.size,就不能return.
18 四数之和(回到目录)
class Solution {
public:
vector > fourSum(vector &num, int target) {
vector > res;
if (num.size()<4)//原先是num.empy()
return res;
sort(num.begin(),num.end());
for (int i = 0; i < num.size()-3; i++) {//原先是i target_2) back--;
else {
vector quadruplet(4, 0);
quadruplet[0] = num[i];
quadruplet[1] = num[j];
quadruplet[2] = num[front];
quadruplet[3] = num[back];
res.push_back(quadruplet);
// Processing the duplicates of number 3
while (front < back && num[front] == quadruplet[2]) ++front;
// Processing the duplicates of number 4
while (front < back && num[back] == quadruplet[3]) --back;
}
}
// Processing the duplicates of number 2
while(j + 1 < num.size() && num[j + 1] == num[j]) ++j;
}
// Processing the duplicates of number 1
while (i + 1 < num.size() && num[i + 1] == num[i]) ++i;
}
return res;
}
};
19 删除倒数第N个节点(回到目录)
我一开始的做法
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n)
{
int len=0;
ListNode* p=head;
while(p)
{
len++;
p=p->next;
}
if(len==1 && n==1) return NULL;
if(len!=1 &&len==n)
{
head=head->next;
}
p=head;
int t1=len-n-1;
while(t1>0)
{
t1--;
p=p->next;
}
ListNode* pre=p;
p=head;
int t2=len-n+1;
while(t2>0)
{
t2--;
p=p->next;
}
ListNode* head_next=p;
pre->next=head_next;
return head;
}
};
21 合并两个有序链表(回到目录)
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
{
//新建节点的两种方式
/*
ListNode temp(0);
ListNode* ptr=&temp;
*/
ListNode* pre=ListNode(0);
ListNode* ptr=pre;
while(l1 && l2)
{
if(l1->val < l2->val)
{
ptr=l1;
l1=l1->next;
}
else
{
ptr=l2;
l2=l2->next;
}
ptr=ptr->next;
}
if(l1)
{
ptr->next=l1;
}
if(l2)
{
ptr->next=l2;
}
return pre->next;
}
};
20 有效的括号(回到目录)
给定一个只包括 '(',')','{','}','[',']'
的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
class Solution {
public:
bool isValid(string s)
{
int len=s.size();
if(len==0) return true;
if(len==1) return false;
stack res;
for(int i=0;i
23 合并K个有序链表(回到目录)
合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。
示例:
输入:
[
1->4->5,
1->3->4,
2->6
]
输出: 1->1->2->3->4->4->5->6
方法1:分治归并
class Solution {
public:
ListNode* mergeKLists(vector& lists)
{
if(lists.size()==0) return NULL;
if(lists.size()==1) return lists[0];
if(lists.size()==2) return mergeTwoLists(lists[0],lists[1]);
int mid=lists.size()/2;
vector sublists_1;
vector sublists_2;
for(int i=0;ival < l2->val)
{
ptr->next=l1;
l1=l1->next;
}
else
{
ptr->next=l2;
l2=l2->next;
}
ptr=ptr->next;
}
if(l1)
{
ptr->next=l1;
}
if(l2)
{
ptr->next=l2;
}
return temp.next;
}
};
方法2: 将所有的节点放在一个vector,然后再排序,最后相连。
class Solution {
public:
ListNode* mergeKLists(vector& lists)
{
vector node_vec;
for(int i=0;inext=node_vec[i];
}
node_vec[node_vec.size()-1]->next=NULL;
return node_vec[0];
}
private:
bool cmp(const ListNode* a,const ListNode* b)
{
return a->val < b->val;
}
};
24 两两交换链表中相邻的节点(回到目录)
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
class Solution {//递归
public:
ListNode* swapPairs(ListNode* head) {
if(head == NULL)
return NULL;
if(head->next == NULL)
return head;
ListNode* temp = head->next;
head->next = swapPairs(temp->next);
temp->next = head;//这个temp和上一行函数里面的temp不一样。
return temp;
}
};
26 删除排序数组中的重复项(82,83)(回到目录)
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
方法1
class Solution {
public:
int removeDuplicates(vector& nums)
{
if(nums.size()==0) return 0;
int cnt=0;
for(int i=1;i
方法2(我更倾向)
class Solution {
public:
int removeDuplicates(vector& nums)
{
if(nums.size()==0) return 0;
int index=0;
for(int i=1;i
快慢指针:同方法2
class Solution {
public:
int removeDuplicates(vector& nums) {
if (nums.empty()) return 0;
int pre = 0, cur = 0, n = nums.size();
while (cur < n) {
if (nums[pre] == nums[cur]) ++cur;
else nums[++pre] = nums[cur++];
}
return pre + 1;
}
};
80 删除排序数组中的重复项 II(回到目录)
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定 nums = [1,1,1,2,2,3],
函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,1,2,3,3],
函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。
你不需要考虑数组中超出新长度后面的元素。
class Solution {
public:
int removeDuplicates(vector& nums)
{
if(nums.size()<=2) return nums.size();
int pre=0;
int cur=1;
int count=1;
while(cur
31 下一个排列(回到目录)
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
class Solution {
public:
void nextPermutation(vector& nums)
{
int len=nums.size();
int j;
for(int i=len-2;i>=0;i--)
{
if(nums[i]i;j--)//为了找第一个比nums[i]大的数字
{
if(nums[j]>nums[i]) break;
}
swap(nums[i],nums[j]);
reverse(nums.begin()+i+1,nums.end());
return;
}
}
reverse(nums.begin(),nums.end());
}
};
32 最长有效括号(回到目录)
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
使用栈
class Solution {//使用栈
public:
int longestValidParentheses(string s)
{
stack index;
int res=0;
int start=0;
for(int i=0;i
33 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
class Solution {
public:
int search(vector& nums, int target)
{
int begin=0;
int end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid])
{
return mid;
}
else if(target=nums[begin])
{
end=mid-1;
}
else
{
begin=mid+1;
}
}
else if(nums[begin]>nums[mid])
{
end=mid-1;
}
else if(nums[begin]==nums[mid])
{
begin=mid+1;
}
}
else if(target>nums[mid])
{
if(nums[begin]nums[mid])
{
if(target>=nums[begin])
{
end=mid-1;
}
else
{
begin=mid+1;
}
}
else if(nums[begin]==nums[mid])
{
begin=mid+1;
}
}
}
return -1;
}
};
二分查找
这道题让在旋转数组中搜索一个给定值,若存在返回坐标,若不存在返回-1。我们还是考虑二分搜索法,但是这道题的难点在于我们不知道原数组在哪旋转了,我们还是用题目中给的例子来分析,对于数组[0 1 2 4 5 6 7] 共有下列七种旋转方法:
0 1 2 4 5 6 7
7 0 1 2 4 5 6
6 7 0 1 2 4 5
5 6 7 0 1 2 4
4 5 6 7 0 1 2
2 4 5 6 7 0 1
1 2 4 5 6 7 0
二分搜索法的关键在于获得了中间数后,判断下面要搜索左半段还是右半段,我们观察上面红色的数字都是升序的,由此我们可以观察出规律,如果中间的数小于最右边的数,则右半段是有序的,若中间数大于最右边数,则左半段是有序的,我们只要在有序的半段里用首尾两个数组来判断目标值是否在这一区域内,这样就可以确定保留哪半边了,代码如下:
class Solution {
public:
int search(vector& nums, int target)
{
if(nums.size()==0) return -1;
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=(left+right)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]=nums[right])//说明左半部有序
{
if(target>=nums[left] && target
34 在排序数组中查找元素的第一个和最后一个位置(回到目录)
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
class Solution
{
public:
vector searchRange(vector& nums, int target)
{
vector result;
result.push_back(left_bound(nums,target));
result.push_back(right_bound(nums,target));
return result;
}
private:
int left_bound(vector &nums,int target)
{
int begin=0,end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid])
{
if(nums[mid-1]nums[mid])
{
begin=mid+1;
}
}
return -1;
}
int right_bound(vector &nums,int target)
{
int begin=0,end=nums.size()-1;
while(begin<=end)
{
int mid=(begin+end)/2;
if(target==nums[mid])
{
if(nums[mid+1]>target || mid==nums.size()-1)
{
return mid;
}
begin=mid+1;
}
else if(targetnums[mid])
{
begin=mid+1;
}
}
return -1;
}
};
二分查找法:
使用两次二分查找法,第一次找到左边界,第二次调用找到右边界即可
class Solution {
public:
vector searchRange(vector& nums, int target) {
vector res(2, -1);
if(nums.empty()) return res;
int left = 0, right = nums.size()-1;//这里是因为左边界的数不可能到数组最后一位
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid;
}
if (nums[right]!= target) return res;
res[0] = right;
right = nums.size();
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) left = mid + 1;
else right= mid;
}
res[1] = left - 1;
return res;
}
};
35 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
视频上解法
class Solution {
public:
int searchInsert(vector& nums, int target)
{
int index=-1;
int begin=0;
int end=nums.size()-1;
while(index==-1)
{
int mid=(begin+end)/2;
if(target==nums[mid])
{
index=mid;
}
else if(targetnums[mid-1])
{
index=mid;
}
end=mid-1;
}
else if(target>nums[mid])
{
if(mid==nums.size()-1 || target
其他解法1
class Solution {
public:
int searchInsert(vector& nums, int target) {
for (int i = 0; i < nums.size(); ++i) {
if (nums[i] >= target) return i;
}
return nums.size();
}
};
其他解法2:二分查找法
class Solution {
public:
int searchInsert(vector& nums, int target) {
if (nums.back() < target) return nums.size();
int left = 0, right = nums.size();
while (left < right) {
int mid = (right + left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid;//相当于查找第一个大于target的数
}
return right;
}
};
39 组合总和 I
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
class Solution {
public:
vector> combinationSum(vector& candidates, int target)
{
sort(candidates.begin(),candidates.end());
vector >res;
vector item;
generate(candidates,res,item,target,0);
return res;
}
void generate(vector &candidates,vector > &res,vector &item,int target,int begin)
{
int len=candidates.size();
if(target==0)
{
res.push_back(item);
return;
}
if(target<0) return;
for(int i=begin;i
40 组合总和 II(回到目录)
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
[1,2,2],
[5]
]
方法1
class Solution {
public:
vector > combinationSum2(vector &num, int target)
{
vector> res;
sort(num.begin(),num.end());
vector local;
findCombination(res, 0, target, local, num);
return res;
}
void findCombination(vector>& res, const int start, const int target, vector& local, const vector& num)
{
if(target==0)
{
res.push_back(local);
return;
}
else
{
for(int i = start;istart) continue; // check duplicate combination
local.push_back(num[i]),
findCombination(res,i+1,target-num[i],local,num); // recursive componenet
local.pop_back();
}
}
}
};
另一种做法:使用set容器,代码和上面那题一样的
class Solution {
public:
vector > combinationSum2(vector &num, int target)
{
set> res;
sort(num.begin(),num.end());
vector local;
findCombination(res, 0, target, local, num);
return vector > (res.begin(),res.end());
}
void findCombination(set>& res, const int order, const int target, vector& local, const vector& num)
{
if(target==0)
{
res.insert(local);
return;
}
else
{
for(int i = order;iorder) continue; // check duplicate combination
local.push_back(num[i]),
findCombination(res,i+1,target-num[i],local,num); // recursive componenet
local.pop_back();
}
}
}
};
45 跳跃游戏 II
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
class Solution {
public:
int jump(vector& nums)
{
if(nums.size()<2)
return 0;
int current_max_index=nums[0];
int pre_max_index=nums[0];
int jump_min=1;
for(int i=1;icurrent_max_index)
{
jump_min++;
current_max_index=pre_max_index;
}
if(pre_max_index
46 全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
class Solution {//递归
public:
vector> permute(vector& nums)
{
vector > res;
generate(nums,res,0);
return res;
}
private:
void generate(vector &nums,vector >&res,int begin)
{
if(begin>=nums.size())
{
res.push_back(nums);
return;
}
for(int i=begin;i
注:我们来仔细推敲一下循环体里的代码,当我们对序列进行交换之后,就将交换后的序列除去第一个元素放入到下一次递归中去了,递归完成了再进行下一次循环。这是某一次循环程序所做的工作,这里有一个问题,那就是在进入到下一次循环时,序列是被改变了。可是,如果我们要假定第一位的所有可能性的话,那么,就必须是在建立在这些序列的初始状态一致的情况下,所以每次交换后,要还原,确保初始状态一致。
47 全排列II(回到目录)
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
class Solution {
public:
vector> permuteUnique(vector& nums)
{
vector >res;
set > res_set;
sort(nums.begin(),nums.end());
generate(res,res_set,nums,0);
return res;
}
private:
void generate(vector > &res,set > &res_set,vector &nums,int begin)
{
if(begin>=nums.size() && res_set.find(nums) ==res_set.end())
{
res.push_back(nums);
res_set.insert(nums);
return;
}
for(int i=begin;i
48 旋转图像(回到目录)
给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
说明:你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
示例 1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
给定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋转输入矩阵,使其变为:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
代码
//i表示的是绕中心的层数,j是列数
class Solution {
public:
void rotate(vector>& matrix)
{
int n=matrix.size();
for(int i=0;i
49 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
class Solution {
public:
vector> groupAnagrams(vector& strs)
{
map,vector >anagram;
vector > res;
for(int i=0;i vec;
change_to_vec(strs[i],vec);
if(anagram.find(vec)==anagram.end())
{
vector item;
anagram[vec]=item;
}
anagram[vec].push_back(strs[i]);
}
map,vector >::iterator it;
for(it=anagram.begin();it!=anagram.end();it++)
{
res.push_back((*it).second);
}
return res;
}
private:
void change_to_vec(string &str,vector &vec)
{
for(int i=0;i<26;i++)
{
vec.push_back(0);
}
for(int i=0;i
另一种更为巧妙地方法:使用哈希
class Solution {
public:
vector> groupAnagrams(vector& strs)
{
vector > res;
map > m;
for(int i=0;i
51 N皇后(回到目录)
class Solution {
public:
vector> solveNQueens(int n)
{
vector > result;
vector > mark;
vector location;
for(int i=0;i ());
for(int j=0;j &location,vector > &result,vector > &mark)
{
if(k==n)
{
result.push_back(location);
return;
}
for(int i=0;i > temp_mark;
temp_mark=mark;
location[k][i]='Q';
put_down_queen(k,i,mark);
generate(k+1,n,location,result,mark);
mark=temp_mark;
location[k][i]='.';
}
}
}
void put_down_queen(int x,int y,vector > &mark)
{
static const int dx[]={-1,1,0,0,-1,-1,1,1};
static const int dy[]={0,0,-1,1,-1,1,-1,1};
mark[x][y]=1;
for(int i=1;i=0 && new_x=0 && new_y
53 最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
动态规划
class Solution {
public:
int maxSubArray(vector& nums)
{
vector dp(nums.size()+6,0);
dp[0]=nums[0];
int max_res=dp[0];
for(int i=1;i
常规方法
#include
#include
using namespace std;
int main(){
int n,curSum=0,maxSum=-1e5;
cin>>n;
vector arr(n);
for(int i=0;i>arr[i];
curSum+=arr[i];
if(curSum>maxSum){
maxSum=curSum;
}
if(curSum<0){
curSum=0;
}
}
cout<
55 跳跃游戏(回到目录)
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
代码
class Solution
{
public:
bool canjump(vector nums)
{
vector index;
int jump=0;
for(int i=0;i
另一个好的解法
class Solution {
public:
bool canJump(vector& nums)
{
int reach=0;
for(int i=0;ireach || reach==nums.size()-1) break;
reach=max(reach,i+nums[i]);
}
return reach>=(nums.size()-1);
}
};
61 旋转链表(回到目录)
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
**链表移动位置和数组不一样**
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k)
{
if(head==NULL) return NULL;
int len=1;
ListNode* p=head;
while(p->next)//注意这里一定要使用p->next,不然编译器会报错。
{
len++;
p=p->next;
}
p->next=head;
int begin_pos=len-k%len-1;
ListNode* cur=head;
while(begin_pos)
{
begin_pos--;
cur=cur->next;
}
ListNode* new_head=cur->next;
cur->next=NULL;
return new_head;
}
};
64 最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
代码1:
class Solution {
public:
int minPathSum(vector>& grid)
{
if(grid.size()==0) return 0;
int row=grid.size();
int col=grid[0].size();
vector >dp(row,vector(col,0));
dp[0][0]=grid[0][0];
for(int j=1;j
代码2:
class Solution {
public:
int minPathSum(vector>& grid) {
int m = grid.size();
int n = grid[0].size();
if(m == 0 || n == 0)
return 0;
vector> dp(m, vector(n));
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
if(i == 0 && j == 0)
{
dp[i][j] = grid[i][j];
}
else if(i == 0)
{
dp[i][j] = dp[i][j-1] + grid[i][j];
}
else if(j == 0)
{
dp[i][j] = dp[i-1][j] + grid[i][j];
}
else
{
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
}
return dp[m-1][n-1];
}
};
66 加一(回到目录)
给定一个非负整数组成的非空数组,在该数的基础上加一,返回一个新的数组。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
class Solution {
public:
vector plusOne(vector& digits)
{
int len=digits.size();
for(int i=len-1;i>=0;i--)
{
if(digits[i]!=9)
{
digits[i]++;
break;
}
else
{
digits[i]=0;
}
if(digits[0]==0)
{
digits[0]=1;
digits.push_back(0);
}
}
return digits;
}
};
67 二进制求和(回到目录)
给定两个二进制字符串,返回他们的和(用二进制表示)。
输入为非空字符串且只包含数字 1 和 0。
示例 1:
输入: a = "11", b = "1"
输出: "100"
示例 2:
输入: a = "1010", b = "1011"
输出: "10101"
class Solution {
public:
string addBinary(string a, string b)
{
string res;
int len_a=a.size();
int len_b=b.size();
int n=max(len_a,len_b);
int carry=0;
if(len_a>len_b)
{
for(int i=0;ilen_a)
{
for(int i=0;i=0;i--)
{
int temp=0;
if(carry)
{
temp=(a[i]-'0')+(b[i]-'0')+1;
}
else
{
temp=(a[i]-'0')+(b[i]-'0');
}
switch(temp)
{
case 0:
res.insert(res.begin(),'0');
carry=0;
break;
case 1:
res.insert(res.begin(),'1');
carry=0;
break;
case 2:
res.insert(res.begin(),'0');
carry=1;
break;
case 3:
res.insert(res.begin(),'1');
carry=1;
break;
}
}
if(carry)
{
res.insert(res.begin(),'1');
}
return res;
}
};
69 x 的平方根(回到目录)
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去
解法1
class Solution {
public:
int mySqrt(int x) {
int low = 0, high = x, mid;
if(x<2) return x; // to avoid mid = 0
while(low= mid) low = mid+1;
else high = mid;
}
return high-1;
}
};
解法2
//原来的i是int,不通过,改成long之后,就可以了
class Solution {
public:
int mySqrt(int x)
{
int res=0;
int n=x/2;
if(x<2) return x;
for(long i=0;i<=n;i++)
{
if(x>=i*i && x<(i+1)*(i+1))
{
res=i;
break;
}
}
return res;
}
};
70 爬楼梯答(回到目录)
假设你正在爬楼梯。需要 n 步你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 步 + 1 步
2. 2 步
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 步 + 1 步 + 1 步
2. 1 步 + 2 步
3. 2 步 + 1 步
class Solution {
public:
int climbStairs(int n)
{
vector dp(n,0);
dp[1]=1;
dp[2]=2;
for(int i=3;i
76 最小覆盖子串
给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
class Solution {
private:
bool is_window_ok(int map_s[], int map_t[], std::vector &vec_t){
for (int i = 0; i < vec_t.size(); i++){
if (map_s[vec_t[i]] < map_t[vec_t[i]]){
return false;
}
}
return true;
}
public:
std::string minWindow(std::string s, std::string t) {
const int MAX_ARRAY_LEN = 128;
int map_t[MAX_ARRAY_LEN] = {0};
int map_s[MAX_ARRAY_LEN] = {0};
std::vector vec_t;
for (int i = 0; i < t.length(); i++){
map_t[t[i]]++;
}
for (int i = 0; i < MAX_ARRAY_LEN; i++){
if (map_t[i] > 0){
vec_t.push_back(i);
}
}
int window_begin = 0;
std::string result;
for (int i = 0; i < s.length(); i++){
map_s[s[i]]++;
while(window_begin < i){
char begin_ch = s[window_begin];
if (map_t[begin_ch] == 0){
window_begin++;
}
else if (map_s[begin_ch] > map_t[begin_ch]){
map_s[begin_ch]--;
window_begin++;
}
else
{break;}
}
if (is_window_ok(map_s, map_t, vec_t)){
int new_window_len = i - window_begin + 1;
if (result == "" || result.length() > new_window_len){
result = s.substr(window_begin, new_window_len);
}
}
}
return result;
}
};
77 组合(回到目录)
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
递归回溯
class Solution {
public:
vector> combine(int n, int k)
{
vector >res;
vector item(0,k);
if(k>n) return res;
generate(n,k,0,0,item,res);
return res;
}
private:
void generate(int n,int k,int numOfDigit,int begin,vector &item,vector > &res)
{
if(numOfDigit==k)
{
res.push_back(item);
return;
}
for(int i=begin;i
78 子集(回到目录)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
小象写法
class Solution
{
public:
vector >subsets(vector &nums)
{
vector > result;
vector item;
result.push_back(item);
generate(0,nums,item,result);
return result;
}
private:
void generate(int i,vector &nums,vector &item, vector >&result)
{
if(i>=nums.size()) return;
item.push_back(nums[i]);
result.push_back(item);
generate(i+1,nums,item,result);
item.pop_back();
generate(i+1,nums,item,result);
}
};
for循环里递归
class Solution {//for循环
public:
vector> subsets(vector& nums)
{
vector >res;
vector item;
res.push_back(item);
sort(nums.begin(),nums.end());//解答错误不是没有排序的问题
generate(res,item,nums,0);
return res;
}
private:
void generate(vector > &res, vector &item, vector &nums, int begin)
{
if(begin>=nums.size())
{
return;
}
for(int i=begin;i
90 子集 II(回到目录)
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
小象解法:
class Solution {
public:
vector> subsetsWithDup(vector& nums)
{
vector >result;
vector item;
set > res_set;
sort(nums.begin(),nums.end());
result.push_back(item);
generate(0,nums,item,result,res_set);
return result;
}
private:
void generate(int i, vector &nums,vector & item, vector > &result, set > &res_set)
{
if(i>=nums.size())
return;
item.push_back(nums[i]);
if(res_set.find(item)==res_set.end())
{
result.push_back(item);
res_set.insert(item);
}
generate(i+1,nums,item,result,res_set);
item.pop_back();
generate(i+1,nums,item,result,res_set);
}
};
for循环里面递归
class Solution {
public:
vector> subsetsWithDup(vector& nums)
{
vector >res;
vector item;
res.push_back(item);
sort(nums.begin(),nums.end());//解答错误不是没有排序的问题
generate(res,item,nums,0);
return res;
}
private:
void generate(vector > &res, vector &item, vector &nums, int begin)
{
if(begin>=nums.size())
{
return;
}
for(int i=begin;i
82 删除排序链表中的重复元素II(回到目录)
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3
新建链表头结点指针pDel,pDel->next=head
,并设置指针prev
指针指向pDel
,curr
指针指向head->next
(代表遍历指针);当curr->next
不为NULL
,如果curr->next->val == curr->val,curr=curr->next
;如果curr->next->val != curr->val
;则需判断prev->next=curr
?如果是,则prev=curr
;如果不是,则prev->next=curr->next
.(这里是说,prev
先假设一个next
指针,即curr=curr->next
;当进行下一步判断时,如果curr->next->val != curr->val
且 prev->next==curr
,则说明假设正确,prev直接指向curr)
//这题一定要借用头节点,来获取pre指针。
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head)
{
if(head==NULL || head->next==NULL)
{
return head;
}
ListNode* pre_head=new ListNode(0);
pre_head->next=head;
ListNode* pre=pre_head;
ListNode* cur=head;
while(cur->next)
{
if(cur->val != cur->next->val)
{
if(pre->next==cur)
{
pre=cur;
}
else
{
pre->next=cur->next;
}
}
cur=cur->next;
}
if(pre->next !=cur)
{
pre->next=cur->next;
}
return pre_head->next;
}
};
83 删除排序链表中的重复元素(回到目录)
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例1:
输入: 1->1->2
输出: 1->2
示例2:
输入: 1->1->2->3->3
输出: 1->2->3
代码
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head)
{
if(!head) return NULL;
ListNode* pre=head;
ListNode* cur=head->next;
while(cur)
{
if(cur->val==pre->val)
{
pre->next=cur->next;
}
else
{
pre=pre->next;
}
cur=cur->next;
}
return head;
}
};
86 分割链表(回到目录)
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5
class Solution {
public:
ListNode* partition(ListNode* head, int x)
{
ListNode less_head(0);
ListNode more_head(0);
ListNode* less_ptr= &less_head;
ListNode* more_ptr= &more_head;
while(head)
{
if(head->val next=head;
less_ptr=head;
}
else
{
more_ptr->next=head;
more_ptr=head;
}
head=head->next;
}
less_ptr->next=more_head.next;
more_ptr->next=NULL;
return less_head.next;
}
};
88 合并有序数组
给定两个有序整数数组nums1
和 nums2
,将nums2
合并到nums1
中,使得num1
成为一个有序数组。
说明:
初始化 nums1
和 nums2
的元素数量分别为m
和n
。
你可以假设nums1
有足够的空间(空间大小大于或等于 m + n
)来保存nums2
中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
class Solution {
public:
void merge(vector& nums1, int m, vector& nums2, int n)
{
int i=m-1;
int j=n-1;
int k=m+n-1;
while(i>=0 && j>=0)
{
if(nums2[j]>=nums1[i])
{
nums1[k]=nums2[j];
k--;
j--;
}
else
{
nums1[k]=nums1[i];
k--;
i--;
}
}
while(j>=0)
{
nums1[k]=nums2[j];
k--;
j--;
}
}
};
92 反转链表II(回到目录)
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度
。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n)
{
int list_len=n-m+1;
ListNode* pre_head=NULL;
ListNode* result=head;
while(head && --m)
{
pre_head=head;
head=head->next;
}
ListNode* modify_list_tail=head;
ListNode* new_head=NULL;
while(head && list_len)
{
ListNode* next=head->next;
head->next=new_head;
new_head=head;
head=next;
list_len--;
}
modify_list_tail->next=head;
if(pre_head)
{
pre_head->next=new_head;
}
else
{
result=new_head;
}
return result;
}
};
94 二叉树的中序遍历(144,145)(回到目录)
递归方法
class Solution {
public:
vector inorderTraversal(TreeNode* root)
{
vector res;
if(root==NULL) return res;
vector temp1=inorderTraversal(root->left);
for(int i=0;ival);
vector temp2=inorderTraversal(root->right);
for(int i=0;i
迭代方法
class Solution {
vector res;
public:
vector inorderTraversal(TreeNode* root)
{
if(root==NULL) return res;
vector res;
stack s;
s.push(root);
while(!s.empty())
{
TreeNode* node=s.top();
if(node->left)
{
s.push(node->left);
node->left=NULL;
}
else
{
res.push_back(node->val);
s.pop();
if(node->right)
{
s.push(node->right);
}
}
}
return res;
}
};
101 对称二叉树(回到目录)
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树[1,2,2,3,4,4,3]
是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
代码
class Solution {
public:
bool isSymmetric(TreeNode* left,TreeNode* right)
{
if(left==NULL && right==NULL)
return true;
if(left==NULL && right!=NULL)
return false;
if(left!=NULL && right==NULL)
return false;
if(left->val !=right->val)
return false;
bool res=(isSymmetric(left->left,right->right) && isSymmetric(left->right,right->left));
return res;
}
bool isSymmetric(TreeNode* root)
{
if(root==NULL)
return true;
bool res=isSymmetric(root->left,root->right);
return res;
}
};
102 二叉树的层次遍历
非递归
分析:先建立一个queue,然后先把根节点放进去,这时候找根节点的左右两个子节点,这时候去掉根节点,此时queue里的元素就是下一层的所有节点,用一个for循环遍历它们,然后存到一个一维向量里,遍历完之后再把这个一维向量存到二维向量里,以此类推,可以完成层序遍历。代码如下:
class Solution {
public:
vector > levelOrder(TreeNode *root) {
vector > res;
if (root == NULL) return res;
queue q;
q.push(root);
while (!q.empty()) {
vector oneLevel;
int size = q.size();
for (int i = 0; i < size; ++i) {
TreeNode *node = q.front();
q.pop();
oneLevel.push_back(node->val);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
res.push_back(oneLevel);
}
return res;
}
};
递归做法
class Solution {//递归方法
public:
vector> levelOrder(TreeNode* root)
{
vector >res;
preorder(root,res,0);
return res;
}
private:
void preorder(TreeNode* root,vector > &res,int depth)
{
if(root==NULL) return;
if(depth==res.size())
{
res.push_back(vector());
}
res[depth].push_back(root->val);
preorder(root->left,res,depth+1);
preorder(root->right,res,depth+1);
}
};
107 二叉树的层次遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
递归方式
class Solution {
public:
vector> levelOrderBottom(TreeNode* root)
{
vector > res;
recursive(0,res,root);
return vector > (res.rbegin(),res.rend());
}
void recursive(int level,vector > &res,TreeNode* root)
{
if(root==NULL) return;
if(level==res.size()) res.push_back({});
res[level].push_back(root->val);
recursive(level+1,res,root->left);
recursive(level+1,res,root->right);
}
};
迭代方式
class Solution {
public:
vector> levelOrderBottom(TreeNode* root)
{
vector > res;
if(root==NULL) return res;
queue q;
q.push(root);
while(!q.empty())
{
vector level;
TreeNode* node=NULL;
int len=q.size();
for(int i=0;ival);
q.pop();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
res.insert(res.begin(),level);
}
return res;
}
};
104 二叉树的最大深度(递归)
深度优先
class Solution {
public:
int maxDepth(TreeNode* root)
{
int depth=0;
if(root==NULL) return 0;
int left=maxDepth(root->left);
int right=maxDepth(root->right);
depth=1+max(left,right);
return depth;
}
};
宽度优先
int maxDepth(TreeNode *root)
{
if(root == NULL)
return 0;
int res = 0;
queue q;
q.push(root);
while(!q.empty())
{
++ res;
for(int i = 0, n = q.size(); i < n; ++ i)
{
TreeNode *p = q.front();
q.pop();
if(p -> left != NULL)
q.push(p -> left);
if(p -> right != NULL)
q.push(p -> right);
}
}
return res;
}
111 二叉树的最小深度(回到目录)
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
class Solution {//递归
public:
int minDepth(TreeNode* root)
{
int depth=0;
if(root==NULL) return 0;
int left=minDepth(root->left);
int right=minDepth(root->right);
if(left==0 || right==0)
{
depth=left+right+1;
}
else
{
depth=1+min(left,right);
}
return depth;
}
};
108 将有序数组转换为二叉搜索树(回到目录)
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
代码
/**
* 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* sortedArrayToBST(vector& nums)
{
if(nums.size()==0) return NULL;
if(nums.size()==1) return new TreeNode(nums[0]);
int mid=nums.size()/2;
TreeNode* root=new TreeNode(nums[mid]);
vector left(nums.begin(),nums.begin()+mid);
vector right(nums.begin()+mid+1,nums.end());
root->left=sortedArrayToBST(left);
root->right=sortedArrayToBST(right);
return root;
}
};
109 有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
使用了递归,和那个24题差不多,关键是如何找到链表的中间位置。
class Solution {//使用了递归,和那个24题差不多
public:
TreeNode* sortedListToBST(ListNode* head)
{
if(head==NULL) return NULL;
else if(head->next==NULL)
{
return (new TreeNode(head->val));
}
ListNode* fast=head->next->next;
ListNode* slow=head;
while(fast && fast->next)//这段代码是在找链表中间位置的元素
{
fast=fast->next->next;
slow=slow->next;
}
TreeNode* root=new TreeNode(slow->next->val);//这就是找到的中间位置
printf("%d\n",root->val);
root->right=sortedListToBST(slow->next->next);
slow->next=NULL;
root->left=sortedListToBST(head);
return root;
}
};
110 平衡二叉树(回到目录)
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:
给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
代码
class Solution {
public:
bool isBalanced(TreeNode* root)
{
if(root==NULL) return true;
if(abs(depth(root->left)-depth(root->right))>1)
{
return false;
}
return isBalanced(root->left)&&isBalanced(root->right);
}
private:
int depth(TreeNode* root)
{
if(root==NULL) return 0;
return 1+max(depth(root->left),depth(root->right));
}
};
112 路径之和(二叉树)(回到目录)
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
bool result=false;
vector path;
int path_sum=0;
preorder(root,path_sum,sum,path,result);
return result;
}
private:
void preorder(TreeNode* node, int &path_sum,int sum,vector &path,bool &result)
{
if(!node)
{
return;
}
path_sum=path_sum+node->val;
path.push_back(node->val);
if(sum==path_sum && !node->left && !node->right)
{
result=true;
}
preorder(node->left,path_sum,sum,path,result);
preorder(node->right,path_sum,sum,path,result);
path_sum=path_sum-node->val;
path.pop_back();
}
};
118 杨辉三角
给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
class Solution {
public:
vector> generate(int numRows)
{
vector >res(numRows,vector());
//vector >res;
if(numRows==0) return res;
res[0].push_back(1);
for(int i=1;i
119 杨辉三角II(回到目录)
给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。
示例:
输入: 3
输出: [1,3,3,1]
//后面的覆盖前面的数据 节省空间
class Solution {
public:
vector getRow(int rowIndex)
{
vector res(rowIndex+1,0);
res[0]=1;
for(int i=1;i<=rowIndex;i++)
{
for(int j=i;j>=1;j--)
{
res[j]=res[j]+res[j-1];
}
}
return res;
}
};
125 验证回文串(回到目录)
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
class Solution {
public:
bool isPalindrome(string s)
{
int l=0;
int r=s.size()-1;
while(l
121 买卖股票的最佳时机(回到目录)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public:
int maxProfit(vector& prices)
{
if(prices.size()<=1) return 0;
int max_pro=0;
int temp_min=prices[0];
int len=prices.size();
for(int i=1;iprices[i])
{
temp_min=prices[i];
}
else
{
int temp_max=prices[i]-temp_min;
max_pro=max(max_pro,temp_max);
}
}
return max_pro;
}
};
134 加油站
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:
输入:
gas = [1,2,3,4,5]
cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。
贪心相关:
class Solution {
public:
int canCompleteCircuit(vector& gas, vector& cost)
{
int total=0;
int sum=0;
int start=0;
for(int i=0;i
135 分发糖果(回到目录)
老师想给孩子们分发糖果,有 N个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。
示例 2:
输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这已满足上述两个条件。
思路:
假设每个孩子分到的糖果数组为A[N]
,初始化为{1},因为每个人至少分到一颗糖。
1、与前面的邻居比较,前向遍历权重数组ratings,如果ratings[i]>ratings[i-1],则A[i]=A[i-1]+1
;
2、与后面的邻居比较,后向遍历权重数组ratings,如果ratings[i]>ratings[i+1]且A[i],则更新A
,A[i]=A[i+1]+1
;
3、对A求和即为最少需要的糖果。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public:
int candy(vector& ratings)
{
int sum=0;
int len=ratings.size();
vector nums(len,1);
for(int i=1;iratings[i-1])
{
nums[i]=nums[i-1]+1;
}
}
sum=nums[len-1];
for(int i=len-2;i>=0;i--)
{
if(ratings[i]>ratings[i+1] && nums[i]
136 只出现一次的数字
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
我的做法
class Solution {
public:
int singleNumber(vector& nums)
{
map hash_map;
int res=0;
for(int i=0;i
按位异或
用到位运算之异或的特性:n ^ n = 0, 0 ^ x = x。
class Solution {
public:
int singleNumber(vector& nums)
{
int res=0;
for(int i=0;i
137 只出现一次的数字II(回到目录)
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
我的做法
class Solution {
public:
int singleNumber(vector& nums)
{
map hash_map;
int res=0;
for(int i=0;i
位运算
class Solution {
public:
int singleNumber(vector& nums) {
int length = nums.size();
int result = 0;
for(int i = 0; i<32; i++){
int count = 0;
int mask = 1<< i;
for(int j=0; j
141 环形链表(回到目录)
给定一个链表,判断链表中是否有环。
方法1
class Solution
{
public:
bool hasCycle(ListNode *head)
{
set node_set;
while(head)
{
if(node_set.find(head)!=node_set.end())
{
return true;
}
node_set.insert(head);
head=head->next;
}
return false;
}
};
方法2
class Solution
{
public:
bool hasCycle(ListNode *head)
{
if(head==NULL || head->next==NULL || head->next->next==NULL) return false;
ListNode* fast=head;
ListNode* slow=head;
while(fast && fast->next)
{
fast=fast->next;
slow=slow->next;
fast=fast->next;
if(fast==slow)
{
return true;
}
}
return false;
}
};
142 环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
说明:不允许修改给定的链表。
class Solution {
public:
ListNode *detectCycle(ListNode *head)
{
ListNode* fast=head;
ListNode* slow=head;
ListNode* meet=NULL;
while(fast && fast->next)
{
slow=slow->next;
fast=fast->next->next;
if(fast==slow)
{
meet=fast;
break;
}
}
if(meet==NULL)
{
return NULL;
}
while(head && meet)
{
if(head==meet)
{
return head;
}
head=head->next;
meet=meet->next;
}
return NULL;
}
};
144 二叉树的前序遍历
题目描述提示帮助提交记录社区讨论阅读解答
给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
递归方法
class Solution {
public:
vector preorderTraversal(TreeNode* root)
{
vector res;
if(root==NULL)
{
return res;
}
res.push_back(root->val);
vector temp1=preorderTraversal(root->left);
for(int i=0;i temp2=preorderTraversal(root->right);
for(int i=0;i
迭代方法
class Solution {
public:
vector preorderTraversal(TreeNode* root)
{
vector res;
stack s;
if(root==NULL)
{
return res;
}
s.push(root);
while(!s.empty())
{
TreeNode* temp=s.top();
res.push_back(temp->val);
s.pop();
if(temp->right)
{
s.push(temp->right);
}
if(temp->left)
{
s.push(temp->left);
}
}
return res;
}
};
145 二叉树的后序遍历
递归方法
递归1
class Solution {
public:
vector postorderTraversal(TreeNode* root)
{
vector res;
postorder(root,res);
return res;
}
private:
void postorder(TreeNode* root,vector &res)
{
if(root==NULL)
{
return;
}
postorder(root->left,res);
postorder(root->right,res);
res.push_back(root->val);
}
};
递归2
class Solution {
public:
vector postorderTraversal(TreeNode* root)
{
vector res;
if(root==NULL) return res;
vector temp1=postorderTraversal(root->left);
for(int i=0;i temp2=postorderTraversal(root->right);
for(int i=0;ival);
return res;
}
};
迭代法
class Solution {
public:
vector postorderTraversal(TreeNode* root)
{
vector res;
if(root==NULL) return res;
stack s;
s.push(root);
while(!s.empty())
{
TreeNode* temp=s.top();
s.pop();
res.push_back(temp->val);
if(temp->left)
{
s.push(temp->left);
}
if(temp->right)
{
s.push(temp->right);
}
}
//vector反转的两种方式
//return vector(res.rbegin(),res.rend());//vector反转方式1
reverse(res.begin(),res.end());//vector反转方式2
return res;
}
};
160 求两个链表的交点。(回到目录)
已知链表A的头节点指针headA,链表B的头节点指针headB,两个链表相交,求两个链表交点对应的节点。
方法一:使用std中的set函数
class Solution
{
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
std::set node_set;
while(headA)
{
node_set.insert(headA);
headA=headA->next;
}
while(headB)
{
if(node_set.find(headB)!=node_set.end())
{
return headB;
}
headB=headB->next;
}
return NULL;
}
};
方法二
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution
{
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
{
int lenA=compute_len(headA);
int lenB=compute_len(headB);
int t=0;
if(lenB>lenA)
{
t=lenB-lenA;
while(headB && t)
{
headB=headB->next;
t--;
}
while(headA && headB)
{
if(headA==headB) return headA;
headA=headA->next;
headB=headB->next;
}
}
else
{
t=lenA-lenB;
while(headA && t)
{
headA=headA->next;
t--;
}
while(headA && headB)
{
if(headA==headB) return headB;
headA=headA->next;
headB=headB->next;
}
}
return NULL;
}
private:
int compute_len(ListNode* head)
{
int len=0;
while(head)
{
len++;
head=head->next;
}
return len;
}
};
169 求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
class Solution {
public:
int majorityElement(vector& nums)
{
map hash_map;
for(int i=0;itemp)
{
temp=hash_map[nums[i]];
res=nums[i];
}
}
return res;
}
};
189 旋转数组
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
我的做法
class Solution {
public:
void rotate(vector& nums, int k)
{
int len=nums.size();
for(int i=0;i0;j--)
{
nums[j]=nums[j-1];
}
nums[0]=temp;
}
}
};
191 位1的个数
编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。
示例 :
输入: 11
输出: 3
解释: 整数 11 的二进制表示为 00000000000000000000000000001011
示例 2:
输入: 128
输出: 1
解释: 整数 128 的二进制表示为 00000000000000000000000010000000
class Solution {
public:
int hammingWeight(uint32_t n)
{
int cnt=0;
while(n)
{
if(n%2)
{
cnt++;
}
n=n/2;
}
return cnt;
}
};
法2:位运算
class Solution {
public:
int hammingWeight(uint32_t n)
{
int res=0;
for(int i=0;i<32;i++)
{
if(n&(1<
199 二叉树的右视图(回到目录)
递归做法
//递归做法
class Solution {
public:
void preorder(TreeNode *root, int depth, vector &res)
{
if(root==NULL) return ;
if(res.size()val);
preorder(root->right, depth+1, res);
preorder(root->left, depth+1, res);
}
vector rightSideView(TreeNode *root) {
vector res;
preorder(root, 1, res);
return res;
}
};
普通方法
class Solution {
public:
vector rightSideView(TreeNode* root)
{
vector view;
queue >Q;
if(root)
{
Q.push(make_pair(root,0));
}
while(!Q.empty())
{
TreeNode *node=Q.front().first;
int depth=Q.front().second;
if(view.size()==depth)
{
view.push_back(node->val);
}
else
{
view[depth]=node->val;
}
Q.pop();
if(node->left)
{
Q.push(make_pair(node->left,depth+1));
}
if(node->right)
{
Q.push(make_pair(node->right,depth+1));
}
}
return view;
}
};
206 反转链表(回到目录)
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
class Solution
{
public:
ListNode* reverseList(ListNode* head)
{
ListNode* new_head=NULL;
while(head)
{
ListNode* next=head->next;
head->next=new_head;
new_head=head;
head=next;
}
return new_head;
}
};
215 数组中的第k个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
class Solution
{
public:
int findKthLargest(vector& nums, int k)
{
set >nums_set;
for(int i=0;i ::iterator it=nums_set.begin();
if(nums_set.size()>=k)
{
k=k-1;
while(k)
{
k--;
it++;
}
}
return *it;
}
};
225 用队列实现栈
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
queue temp_queue;
temp_queue.push(x);
while(!data_queue.empty())
{
temp_queue.push(data_queue.front());
data_queue.pop();
}
while(!temp_queue.empty())
{
data_queue.push(temp_queue.front());
temp_queue.pop();
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int x=data_queue.front();
data_queue.pop();
return x;
}
/** Get the top element. */
int top() {
return data_queue.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return data_queue.empty();
}
private:
queue data_queue;
};
226 翻转二叉树(回到目录)
使用递归方法
class Solution {
public:
TreeNode* invertTree(TreeNode* root)
{
if(root==NULL) return NULL;
TreeNode* temp=root->left;
root->left=invertTree(root->right);
root->right=invertTree(temp);
return root;
}
};
非递归
// Non-Recursion
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return NULL;
queue q;
q.push(root);
while (!q.empty()) {
TreeNode *node = q.front(); q.pop();
TreeNode *tmp = node->left;
node->left = node->right;
node->right = tmp;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return root;
}
};
258 各位相加(回到目录)
给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。
示例:
输入: 38
输出: 2
解释: 各位相加的过程为:3 + 8 = 11, 1 + 1 = 2。 由于 2 是一位数,所以返回 2。
进阶:
你可以不使用循环或者递归,且在 O(1) 时间复杂度内解决这个问题吗?
递归方法
class Solution {
public:
int addDigits(int num)
{
if(num<10) return num;
int sum=0;
while(num)
{
sum=num%10 +sum;
num=num/10;
}
return addDigits(sum);
}
};
循环方法
class Solution {
public:
int addDigits(int num) {
int i,sum = 0;
while(1)
{
while(num != 0)
{
sum += num%10;
num /= 10;
}
if(sum >= 10)
{
num = sum;
sum = 0;
}
else
break;
}
return sum;
}
};
295 数据流的中位数(回到目录)
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如:
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
class MedianFinder {
public:
MedianFinder() {
}
void addNum(int num) {
if (big_queue.empty()){
big_queue.push(num);
return;
}
if (big_queue.size() == small_queue.size()){
if (num < big_queue.top()){
big_queue.push(num);
}
else{
small_queue.push(num);
}
}
else if(big_queue.size() > small_queue.size()){
if (num > big_queue.top()){
small_queue.push(num);
}
else{
small_queue.push(big_queue.top());
big_queue.pop();
big_queue.push(num);
}
}
else if(big_queue.size() < small_queue.size()){
if (num < small_queue.top()){
big_queue.push(num);
}
else{
big_queue.push(small_queue.top());
small_queue.pop();
small_queue.push(num);
}
}
}
double findMedian(){
if (big_queue.size() == small_queue.size()){
return (big_queue.top() + small_queue.top()) / 2;
}
else if (big_queue.size() > small_queue.size()){
return big_queue.top();
}
return small_queue.top();
}
private:
//priority_queue big_queue;//最大堆的两种定义方式
priority_queue,less > big_queue;//最大堆
priority_queue, greater > small_queue;//最小堆
};
315 计算右侧小于当前元素的个数(回到目录)
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
class Solution
{
public:
vector countSmaller(vector &nums)
{
vector count;
vector > vec;
for(int i=0;i > &sub_vec1,vector > &sub_vec2,vector > &vec,vector &count)
{
int i=0;
int j=0;
while(i > &vec,vector &count)
{
if(vec.size()<2)
{
return;
}
int mid=vec.size()/2;
vector > sub_vec1;
vector > sub_vec2;
for(int i=0;i
389 找不同
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例:
输入:
s = "abcd"
t = "abcde"
输出:
e
解释:
'e' 是那个被添加的字母。
class Solution {
public:
char findTheDifference(string s, string t)
{
map hash_map;
char res;
for(int i=0;i
402 移掉k个数字(回到目录)
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。
示例 1 :
输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
示例 2 :
输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :
输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0。
class Solution {
public:
string removeKdigits(string num, int k)
{
vector S;
string result="";
for(int i=0;i0 && S[S.size()-1]>number)
{
S.pop_back();
k--;
}
if(number !=0 || S.size()!=0)
{
S.push_back(number);
}
}
while(k>0 && S.size()!=0)
{
S.pop_back();
k--;
}
for(int i=0;i
414 第三大的数
给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
示例 1:
输入: [3, 2, 1]
输出: 1
解释: 第三大的数是 1.
示例 2:
输入: [1, 2]
输出: 2
解释: 第三大的数不存在, 所以返回最大的数 2 .
示例 3:
输入: [2, 2, 3, 1]
输出: 1
解释: 注意,要求返回第三大的数,是指第三大且唯一出现的数。
存在两个值为2的数,它们都排第二。
class Solution {
public:
int thirdMax(vector& nums)
{
set >nums_set;//从大到小排序
for(int i=0;i ::iterator it=nums_set.begin();
if(nums_set.size()>=3)
{
int k=2;
while(k)
{
k--;
it++;
}
}
return *it;
}
};
520 检测大写字母(回到目录)
给定一个单词,你需要判断单词的大写使用是否正确。
我们定义,在以下情况时,单词的大写用法是正确的:
全部字母都是大写,比如"USA"。
单词中所有字母都不是大写,比如"leetcode"。
如果单词不只含有一个字母,只有首字母大写, 比如 “Google”。
否则,我们定义这个单词没有正确使用大写字母。
示例 1:
输入: "USA"
输出: True
示例 2:
输入: "FlaG"
输出: False
注意: 输入是由大写和小写拉丁字母组成的非空单词。
class Solution {
public:
bool detectCapitalUse(string word)
{
int len=word.size();
if(len==1) return true;
int cnt=0;
for(int i=0;i='A')
{
cnt++;
}
}
if((cnt==1 && word[0]<='Z' && word[0]>='A') || cnt==len || cnt==0)
{
return true;
}
return false;
}
};
633 平方数之和
给定一个非负整数c,你要判断是否存在两个整数a和b,使得$a^{2}+b^{2}=c$
示例1:
输入: 5
输出: True
解释: 1 * 1 + 2 * 2 = 5
示例2:
输入: 3
输出: False
我的做法如下:
class Solution {
public:
bool judgeSquareSum(int c)
{
int a=0,b=sqrt(c);
while(a<=b)
{
if(a*a+b*b>c)
{
b--;
}
else if(a*a+b*b
下面这种方法用到了集合set,从0遍历到c的平方根,对于每个i*i
,都加入集合set中,然后计算c-i*i
,如果这个差值也在集合set中,返回true,遍历结束返回false,参见代码如下:
class Solution {
public:
bool judgeSquareSum(int c) {
unordered_set s;
for (int i = 0; i <= sqrt(c); ++i) {
s.insert(i * i);
if (s.count(c - i * i)) return true;
}
return false;
}
};
784 字母大小写全排列(回到目录)
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。
示例:
输入: S = "a1b2"
输出: ["a1b2", "a1B2", "A1b2", "A1B2"]
输入: S = "3z4"
输出: ["3z4", "3Z4"]
输入: S = "12345"
输出: ["12345"]
**注意**:
S 的长度不超过12。
S 仅由数字和字母组成。
char chang_upOrLow(char s)//递归
{
char res;
if(s>='A' && s<='Z')
{
res=s+32;
}
if(s>='a' && s<='z')
{
res=s-32;
}
return res;
}
void generate(string &S,vector &res,int index)
{
if(index>=S.size())
{
res.push_back(S);
return;
}
if((S[index]>='a' && S[index]<='z') ||(S[index]>='A' && S[index]<='Z'))
//if(S[index]>='A' && S[index]<='z')
{
S[index]=chang_upOrLow(S[index]);
generate(S,res,index+1);
S[index]=chang_upOrLow(S[index]);
}
generate(S,res,index+1);
}
class Solution {
public:
vector letterCasePermutation(string S)
{
vector res;
generate(S,res,0);
return res;
}
};
或者:
char chang_upOrLow(char s)
{
char res;
if(s>='A' && s<='Z')
{
res=s+32;
}
if(s>='a' && s<='z')
{
res=s-32;
}
return res;
}
void generate(string &S,vector &res,int index)
{
if(index>=S.size())
{
res.push_back(S);
return;
}
if((S[index]>='a' && S[index]<='z') ||(S[index]>='A' && S[index]<='Z'))
//if(S[index]>='A' && S[index]<='z')
{
S[index]=chang_upOrLow(S[index]);
generate(S,res,index+1);
S[index]=chang_upOrLow(S[index]);
generate(S,res,index+1);
}
else
{
generate(S,res,index+1);
}
}
class Solution {
public:
vector letterCasePermutation(string S)
{
vector res;
generate(S,res,0);
return res;
}
};
leetcode刷题B
50. Pow(x, n)(回到目录)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.
class Solution {
public:
double myPow(double x, int n)
{
if(n<0) return 1/powxn(x,-n);
return powxn(x,n);
}
private:
double powxn(double x,int n)
{
if(n==0) return 1;
double half=powxn(x,n/2);
if(n % 2==0)
{
return half*half;
}
else
{
return half*half*x;
}
}
};
54 螺旋矩阵(59)(回到目录)
给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。
示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]
class Solution {
public:
vector spiralOrder(vector>& matrix)
{
vector res;
if(matrix.empty()) return res;
int m=matrix.size();
int n=matrix[0].size();
int count=min(m,n)/2;
int yushu=min(m,n)%2;
for(int i=0;i=i+1;j--)//这里j>=i+1,不能是j>=i,不然在最左下角的那个元素会重复
{
res.push_back(matrix[m-1-i][j]);
}
for(int j=m-1-i;j>=i+1;j--)//这里j>=i+1,不能是j>=i,不然在左上角的那个元素会重复
{
res.push_back(matrix[j][i]);
}
}
if(yushu==1)
{
if(m==n)
{
res.push_back(matrix[count][count]);
}
else if(m>n)
{
for(int i=count;i
58 最后一个单词的长度(回到目录)
给定一个仅包含大小写字母和空格 ’ ’ 的字符串,返回其最后一个单词的长度。
如果不存在最后一个单词,请返回 0 。
说明:一个单词是指由字母组成,但不包含任何空格的字符串。
示例:
输入: "Hello World"
输出: 5
class Solution {
public:
int lengthOfLastWord(string s)
{
int cnt=0;
int n=s.size()-1;
while(s[n]==' ') n--;//
for(int i=n;i>=0;i--)
{
if(s[i] !=' ')
{
cnt++;
}
else
{
break;
}
}
return cnt;
}
};
59 螺旋矩阵 II(54)(回到目录)
题目描述提示帮助提交记录社区讨论阅读解答
给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3
输出:
[
[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]
]
class Solution {
public:
vector> generateMatrix(int n)
{
vector > matrix(n,vector(n));
int count=n/2;
int yushu=n%2;
int num=1;
for(int i=0;i=i+1;j--)
{
matrix[n-1-i][j]=num++;
}
for(int j=n-1-i;j>=i+1;j--)
{
matrix[j][i]=num++;;
}
}
if(yushu)
{
matrix[count][count]=num++;
}
return matrix;
}
};
62 不同路径(回到目录)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
说明:m 和 n 的值均不超过 100。
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
方法1
//动态规划
class Solution {
public:
int uniquePaths(int m, int n)
{
vector >dp(m,vector(n,0));
for(int i=0;i
方法2
这跟之前那道 Climbing Stairs
爬梯子问题 很类似,那道题是说可以每次能爬一格或两格,问到达顶部的所有不同爬法的个数。而这道题是每次可以向下走或者向右走,求到达最右下角的所有不同走法的个数。那么跟爬梯子问题一样,我们需要用动态规划Dynamic Programming
来解,我们可以维护一个二维数组dp,其中dp[i][j]表示到当前位置不同的走法的个数,然后可以得到递推式为:dp[i][j] = dp[i - 1][j] + dp[i][j - 1],
这里为了节省空间,我们使用一维数组dp,一行一行的刷新也可以,代码如下:
class Solution {
public:
int uniquePaths(int m, int n)
{
vector dp(n,1);
for(int i=1;i
63 不同路径 II(回到目录)
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
说明:m 和 n 的值均不超过 100。
示例 1:
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
解释:
3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
class Solution {
public:
int uniquePathsWithObstacles(vector>& obstacleGrid)
{
if(obstacleGrid.empty() || obstacleGrid[0].empty() || obstacleGrid[0][0]==1)
{
return 0;
}
int m=obstacleGrid.size();
int n=obstacleGrid[0].size();
vector > dp(m,vector(n,0));
for(int i=0;i0)
{
dp[i][j]=dp[i][j-1];
}
else if(i>0 && j==0)
{
dp[i][j]=dp[i-1][j];
}
else if(i>0 && j>0)
{
dp[i][j]=dp[i][j-1]+dp[i-1][j];
}
}
}
return dp[m-1][n-1];
}
};
98. 验证二叉搜索树(回到目录)
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
/**
* 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:
bool isValidBST(TreeNode* root)
{
if(root==NULL) return true;
vector nums;
inorder(root,nums);
for(int i=0;i &nums)
{
if(!root) return;
inorder(root->left,nums);
nums.push_back(root->val);
inorder(root->right,nums);
}
};
120 三角形最小路径和(回到目录)
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
使用常规的动态规划解法
class Solution {
public:
int minimumTotal(vector>& triangle)
{
vector > dp;
for(int i=0;i());
for(int j=0;j=0;i--)
{
for(int j=0;j<=triangle[i].size();j++)
{
dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j];
}
}
return dp[0][0];
}
};
使用原地覆盖
class Solution {
public:
int minimumTotal(vector>& triangle)
{
for(int i=triangle.size()-2;i>=0;i--)
{
for(int j=0;j<=i;j++)
{
triangle[i][j]=min(triangle[i+1][j],triangle[i+1][j+1])+triangle[i][j];
}
}
return triangle[0][0];
}
};
198 打家劫舍(回到目录)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12
// 动态规划
class Solution {
public:
int rob(vector &num) {
if (num.size() <= 1) return num.empty() ? 0 : num[0];
vector dp = {num[0], max(num[0], num[1])};
for (int i = 2; i < num.size(); ++i) {
dp.push_back(max(num[i] + dp[i - 2], dp[i - 1]));
}
return dp.back();
}
};
213 打家劫舍 II(回到目录)
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 1:
输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
在打家劫舍(198)的基础上改动,先去掉第一个,计算一下最高金额,然后只去掉最后一个,计算最高金额。然后比较两种方式的结果,取较大值。
class Solution {//动态规划
public:
int rob(vector& nums)
{
if(nums.size()<=1) return nums.empty()? 0: nums[0];
return max(rob(nums,0,nums.size()-1),rob(nums,1,nums.size()));
}
private:
int rob(vector &nums, int start,int end)
{
if(end-start<=1) return nums[start];
vector dp(end,0);
dp[start]=nums[start];
dp[start+1]=max(nums[start+1],nums[start]);
for(int i=start+2;i
287 寻找重复数(回到目录)
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
输入: [1,3,4,2,2]
输出: 2
示例 2:
输入: [3,1,3,4,2]
输出: 3
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
分析:二分查找这道题和抽屉原理差不多,例如九个抽屉是个钥匙,一定会有一个抽屉是两个要是的。题目要求我们不能改变原数组,即不能给原数组排序,又不能用多余空间,那么哈希表神马的也就不用考虑了,又说时间小于O(n2),也就不能用brute force的方法,那我们也就只能考虑用二分搜索法了,我们在区别[1, n]中搜索,首先求出中点数mid,然后遍历整个数组,统计所有小于等于mid的个数。如果count 小于等于mid, 说明 1 到 mid 这些数字没有重复项, 重复项在右半边 mid+1 到n, 所以缩小到右半边继续搜索;如果count 大于mid, 说明 1 到 mid 这些数字中有重复项,缩小到 左半边继续搜索。
class Solution {
public:
int findDuplicate(vector& nums)
{
int left=1,right=nums.size()-1;
while(left
303 区域和检索 - 数组不可变(回到目录)
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
示例:
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
说明:
你可以假设数组不可变。
会多次调用 sumRange 方法。
动态规划
class NumArray {
public:
NumArray(vector nums)
{
dp=nums;
for(int i=1;i dp;
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray obj = new NumArray(nums);
* int param_1 = obj.sumRange(i,j);
*/
441 排列硬币(回到目录)
你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
示例 1:
n = 5
硬币可排列成以下几行:
¤
¤ ¤
¤ ¤
因为第三行不完整,所以返回2.
示例 2:
n = 8
硬币可排列成以下几行:
¤
¤ ¤
¤ ¤ ¤
¤ ¤
因为第四行不完整,所以返回3.
class Solution {
public:
int arrangeCoins(int n) {
if (n <= 1) return n;
long low = 1, high = n;
while (low < high) {
long mid = low + (high - low) / 2;
if (mid * (mid + 1) / 2 <= n) low = mid + 1;
else high = mid;
}
return low-1;
}
};
442 数组中重复的数据(回到目录)
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[2,3]
使用正负替换法,由于数组中的数字都是在[1,n]区间内,所以,可以数组的元素可以转化成index,将访问过的元素取相反数。如果再次遇到这个数(此时必然是负数了),则将它加入result数组。
class Solution {
public:
vector findDuplicates(vector& nums)
{
vector res;
for(int i=0;i
461 汉明距离(回到目录)
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。
注意:
0 ≤ x, y < 231.
示例:
输入: x = 1, y = 4
输出: 2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
分析:只要将x和y的二进制形式的每一位取出来按位异或,若为1,则res++,最后的res就是汉明距离。
class Solution {
public:
int hammingDistance(int x, int y)
{
int res=0;
for(int i=0;i<32;i++)
{
if(x&(1<
526 优美的排列(回到目录)
假设有从 1 到 N 的 N 个整数,如果从这N个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。
条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?
示例1:
输入: 2
输出: 2
解释:
第 1 个优美的排列是 [1, 2]:
第 1 个位置(i=1)上的数字是1,1能被 i(i=1)整除
第 2 个位置(i=2)上的数字是2,2能被 i(i=2)整除
第 2 个优美的排列是 [2, 1]:
第 1 个位置(i=1)上的数字是2,2能被 i(i=1)整除
第 2 个位置(i=2)上的数字是1,i(i=2)能被 1 整除
说明:
N 是一个正整数,并且不会超过15。
分析:
这道题给了我们1到N,总共N个正数,然后定义了一种优美排列方式,对于该排列中的所有数,如果数字可以整除下标,或者下标可以整除数字,那么我们就是优美排列,让我们求出所有优美排列的个数。那么对于求种类个数,或者是求所有情况,这种问题通常要用递归来做。而递归方法等难点在于写递归函数,如何确定终止条件,还有for循环中变量的起始位置如何确定。那么这里我们需要一个visited数组来记录数字是否已经访问过,因为优美排列中不能有重复数字。我们用变量pos来标记已经生成的数字的个数,如果大于N了,说明已经找到了一组排列,结果res自增1。在for循环中,i应该从1开始,因为我们遍历1到N中的所有数字,如果该数字未被使用过,且满足和坐标之间的整除关系,那么我们标记该数字已被访问过,再调用下一个位置的递归函数,之后不要忘记了恢复初始状态。
//递归方法
class Solution {
public:
int countArrangement(int N) {
int res = 0;
vector visited(N + 1, 0);
dfs(N, visited, 1, res);
return res;
}
void dfs(int N, vector& visited, int pos, int& res) {
if (pos > N) {
++res;
return;
}
//for里面的代码是指某个数字都放在每个位置一次
for (int i = 1; i <= N; ++i) {
if (visited[i] == 0 && (i % pos == 0 || pos % i == 0)) {
visited[i] = 1;
dfs(N, visited, pos + 1, res);
visited[i] = 0;
}
}
}
};
667 优美的排列 II(回到目录)
定两个整数 n 和 k,你需要实现一个数组,这个数组包含从 1 到 n 的 n 个不同整数,同时满足以下条件:
① 如果这个数组是 [a1, a2, a3, … , an] ,那么数组 [|a1 - a2|, |a2 - a3|, |a3 - a4|, … , |an-1 - an|] 中应该有且仅有 k 个不同整数;.
② 如果存在多种答案,你只需实现并返回其中任意一种.
示例 1:
输入: n = 3, k = 1
输出: [1, 2, 3]
解释: [1, 2, 3] 包含 3 个范围在 1-3 的不同整数, 并且 [1, 1] 中有且仅有 1 个不同整数 : 1
示例 2:
输入: n = 3, k = 2
输出: [1, 3, 2]
解释: [1, 3, 2] 包含 3 个范围在 1-3 的不同整数, 并且 [2, 1] 中有且仅有 2 个不同整数: 1 和 2
提示:
n 和 k 满足条件 1 <= k < n <= 104.
分析:这题的意思是给出1到n的不同整数,求k种不同相邻数的绝对值差值情况下的排列(任意一种即可)。
其实,k的最大值是n-1,使用最小最大相邻就可以了。
- 如果给出n=4,k=2,则ans=[4,1,2,3],也就是说,把最大数放在最小数之前,k自动减1,当k减到1时,之后的数按升序排列,就是两种排法了。
- 如果给出n=4,k=3,则ans=[1,4,2,3],把最大数依次插入在最小的数之后,当k减到1时,之后的数依次按升序排列。
class Solution {
public:
vector constructArray(int n, int k)
{
vector res;
int i=1,j=n;
while(i<=j)
{
if(k>1)
{
if(k%2)
{
res.push_back(i);
i++;
}
else
{
res.push_back(j);
j--;
}
k--;
}
else
{
res.push_back(i);
i++;
}
}
return res;
}
};
简化版的代码
class Solution {
public:
vector constructArray(int n, int k) {
vector res;
int i = 1, j = n;
while (i <= j) {
if (k > 1) res.push_back(k-- % 2 ? i++ : j--);
else res.push_back(i++);
}
return res;
}
};
283 移动零(回到目录)
题目描述提示帮助提交记录社区讨论阅读解答
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
分析:这道题很简单,使用双指针i和j,同时指向0。其中i用于扫描nums数组的每一个元素,遇到非零的元素,就和nums[j]交换,然后j++。
class Solution {//双指针
public:
void moveZeroes(vector& nums)
{
for(int i=0,j=0;i
605 种花问题(回到目录)
假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。
示例 1:
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
示例 2:
输入: flowerbed = [1,0,0,0,1], n = 2
输出: False
注意:数组内已种好的花不会违反种植规则。
输入的数组长度范围为 [1, 20000]。
n 是非负整数,且不会超过输入数组的大小。
分析:对于连续的0来说,主要是考虑边界问题。例如000,如果是放在边界,则101,即种2盆花;如果两边是1,就只能放1盆(010)。所以这里有个技巧是,若两边是0,则直接插入一个0,这样,原来的边界处就可以放心的种花了。
class Solution {
public:
bool canPlaceFlowers(vector& flowerbed, int n)
{
if(flowerbed[0]==0) flowerbed.insert(flowerbed.begin(),0);
if(flowerbed.back()==0) flowerbed.push_back(0);
int len=flowerbed.size();
int cnt=0,sum=0;
for(int i=0;i<=len;i++)
{
if(i=n;
}
};
643 子数组最大平均数 I(回到目录)
给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。
示例 1:
输入: [1,12,-5,-6,50,3], k = 4
输出: 12.75
解释: 最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
注意:
1 <= k <= n <= 30,000。
所给数据范围 [-10,000,10,000]。
分析:这道题,我的思路比较暴力,直接计算每个k元组元素的和,计算出最大的那个。然后得到平均数。
class Solution {
public:
double findMaxAverage(vector& nums, int k)
{
int len=nums.size();
if(len==1) return nums[0];
vector sum(len,0);
sum[0]=nums[0];
for(int i=1;i
665 非递减数列(回到目录)
给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 <= i < n),满足 array[i] <= array[i + 1]。
示例 1:
输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
示例 2:
输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。
说明: n 的范围为 [1, 10,000]。
分析:这道题给了我们一个数组,说我们最多有1次修改某个数字的机会,问能不能将数组变为非递减数组。题目中给的例子太少,不能覆盖所有情况,我们再来看下面三个例子:
4,2,3
-1,4,2,3
2,3,3,2,4
我们通过分析上面三个例子可以发现,当我们发现后面的数字小于前面的数字产生冲突后,有时候需要修改前面较大的数字(比如前两个例子需要修改4),有时候却要修改后面较小的那个数字(比如前第三个例子需要修改2),那么有什么内在规律吗?是有的,判断修改那个数字其实跟再前面一个数的大小有关系,首先如果再前面的数不存在,比如例子1,4前面没有数字了,我们直接修改前面的数字为当前的数字2即可。而当再前面的数字存在,并且小于当前数时,比如例子2,-1小于2,我们还是需要修改前面的数字4为当前数字2;如果再前面的数大于当前数,比如例子3,3大于2,我们需要修改当前数2为前面的数3。这是修改的情况,由于我们只有一次修改的机会,所以用一个变量cnt,初始化为1,修改数字后cnt自减1,当下次再需要修改时,如果cnt已经为0了,直接返回false。遍历结束后返回true。
修改的原则:尽量使把数字往小的改。
class Solution {
public:
bool checkPossibility(vector& nums)
{
int cnt=1;
for(int i=1;i=nums[i-2]) nums[i-1]=nums[i];
else
{
nums[i]=nums[i-1];
}
cnt--;
}
}
return true;
}
};
131 分割回文串(回到目录)
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: "aab"
输出:
[
["aa","b"],
["a","a","b"]
]
这是一道需要用DFS来解的题目,既然题目要求找到所有可能拆分成回文数的情况,那么肯定是所有的情况都要遍历到,对于每一个子字符串都要分别判断一次是不是回文数,那么肯定有一个判断回文数的子函数,还需要一个DFS函数用来递归,再加上原本的这个函数,总共需要三个函数来求解。代码如下:
class Solution {//循环里有递归
public:
vector> partition(string s) {
vector> res;
vector out;
partitionDFS(s, 0, out, res);
return res;
}
void partitionDFS(string s, int start, vector &out, vector> &res) {
if (start == s.size()) {
res.push_back(out);
return;
}
for (int i = start; i < s.size(); ++i) {
if (isPalindrome(s, start, i)) {
out.push_back(s.substr(start, i - start + 1));
partitionDFS(s, i + 1, out, res);
out.pop_back();
}
}
}
bool isPalindrome(string s, int start, int end) {
while (start < end) {
if (s[start] != s[end]) return false;
++start;
--end;
}
return true;
}
};
注:那么,对原字符串的所有子字符串的访问顺序是什么呢,如果原字符串是 abcd, 那么访问顺序为:a -> b -> c -> d -> cd -> bc -> bcd-> ab -> abc -> abcd
, 这是对于没有两个或两个以上子回文串的情况。那么假如原字符串是aabc
,那么访问顺序为:a -> a -> b -> c -> bc -> ab -> abc -> aa -> b -> c -> bc -> aab -> aabc
,中间当检测到aa时候,发现是回文串,那么对于剩下的bc当做一个新串来检测,于是有b -> c -> bc
,这样扫描了所有情况,即可得出最终答案。
股票问题
121 买卖股票的最佳时机(回到目录)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
引入两个变量,最小买入价格和最大利润,遍历数组,判断最大和最小值来得到最后的结果。
class Solution {
public:
int maxProfit(vector& prices)
{
/*
vector > stock;
for(int i=1;i<=prices.size();i++)
{
stock.push_back(make_pair(i,prices[i]));
}
*/
if(prices.size()<=1) return 0;
int max_pro=0;
int temp_min=prices[0];
int len=prices.size();
for(int i=1;i
122 买卖股票的最佳时机 II(回到目录)
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
分析:炒股想挣钱当然是低价买入高价抛出,那么这里我们只需要从第二天开始,如果当前价格比之前价格高,则把差值加入利润中,因为我们可以昨天买入,今日卖出,若明日价更高的话,还可以今日买入,明日再抛出。以此类推,遍历完整个数组后即可求得最大利润。
class Solution {
public:
int maxProfit(vector& prices)
{
int len=prices.size();
int res=0;
for(int i=0;i
说明:其他几个股票问题有点难,现在不想做了
516 最长回文子序列(回到目录)
给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。
示例 1:
输入:"bbbab"
输出:4
一个可能的最长回文子序列为 "bbbb"。
示例 2:
输入:"cbbd"
输出:2
一个可能的最长回文子序列为 "bb"。
这道题给了我们一个字符串,让我们求最大的回文子序列,子序列和子字符串不同,不需要连续。而关于回文串的题之前也做了不少,处理方法上就是老老实实的两两比较吧。像这种有关极值的问题,最应该优先考虑的就是贪婪算法和动态规划,这道题显然使用DP更加合适。我们建立一个二维的DP数组,其中dp[i][j]表示[i,j]区间内的字符串的最长回文子序列,那么对于递推公式我们分析一下,如果s[i]==s[j],那么i和j就可以增加2个回文串的长度,我们知道中间dp[i + 1][j - 1]的值,那么其加上2就是dp[i][j]的值。如果s[i] != s[j],那么我们可以去掉i或j其中的一个字符,然后比较两种情况下所剩的字符串谁dp值大,就赋给dp[i][j],那么递推公式如下:
/ dp[i + 1][j - 1] + 2 if (s[i] == s[j])
dp[i][j] =
\ max(dp[i + 1][j], dp[i][j - 1]) if (s[i] != s[j])
代码
class Solution {//动态规划
public:
int longestPalindromeSubseq(string s) {
int n = s.length();
vector> dp(n, vector(n, 0));
for (int i = 0; i < n; i++)
dp[i][i] = 1;
for (int j = 1; j < n; j++) // 子串结束位置
for (int i = j-1; i >=0; i--) { // 子串开始位置
if (s[i] == s[j])
dp[i][j] = dp[i + 1][j - 1] + 2;
else
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
return dp[0][n - 1];
}
};
74 搜索二维矩阵
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true
示例 2:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false
这段代码里用到了两次二分查找,两次查找的代码模板不一样。
这道题要求搜索一个二维矩阵,由于给的矩阵是有序的,所以很自然的想到要用二分查找法,我们可以在第一列上先用一次二分查找法找到目标值所在的行的位置,然后在该行上再用一次二分查找法来找是否存在目标值,代码如下:
// 二分查找法
class Solution {
public:
bool searchMatrix(vector > &matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
if (target < matrix[0][0] || target > matrix.back().back()) return false;
int left = 0, right = matrix.size();
while (left < right) {
int mid = (left + right) / 2;
if (matrix[mid][0] == target) return true;
else if (matrix[mid][0] < target) left = mid + 1;
else right = mid;
}
int tmp = right-1;
left = 0;
right = matrix[tmp].size() - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (matrix[tmp][mid] == target) return true;
else if (matrix[tmp][mid] < target) left = mid + 1;
else right = mid - 1;
}
return false;
}
};
240 搜索二维矩阵 II
题目描述提示帮助提交记录社区讨论阅读解答
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
class Solution {
public:
bool searchMatrix(vector>& matrix, int target)
{
if(matrix.size()==0 || matrix[0].size()==0) return false;
int m=matrix.size()-1,n=matrix[0].size()-1;
if(targetmatrix[m][n]) return false;
int x=m,y=0;
while(1)
{
if(target>matrix[x][y]) y++;
else if(targetn) break;
}
return false;
}
};
107 二叉树的层次遍历 II
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其自底向上的层次遍历为:
[
[15,7],
[9,20],
[3]
]
递归方式
class Solution {
public:
vector> levelOrderBottom(TreeNode* root)
{
vector > res;
recursive(0,res,root);
return vector > (res.rbegin(),res.rend());
}
void recursive(int level,vector > &res,TreeNode* root)
{
if(root==NULL) return;
if(level==res.size()) res.push_back({});
res[level].push_back(root->val);
recursive(level+1,res,root->left);
recursive(level+1,res,root->right);
}
};
迭代方式
class Solution {
public:
vector> levelOrderBottom(TreeNode* root)
{
vector > res;
if(root==NULL) return res;
queue q;
q.push(root);
while(!q.empty())
{
vector level;
TreeNode* node=NULL;
int len=q.size();
for(int i=0;ival);
q.pop();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
res.insert(res.begin(),level);
}
return res;
}
};
344 反转字符串
编写一个函数,其作用是将输入的字符串反转过来。
示例 1:
输入: "hello"
输出: "olleh"
示例 2:
输入: "A man, a plan, a canal: Panama"
输出: "amanaP :lanac a ,nalp a ,nam A"
我的解法
class Solution {
public:
string reverseString(string s)
{
string res;
for(int i=s.size()-1;i>=0;i--)
{
res.push_back(s[i]);
}
return res;
}
};
参考解法
class Solution {
public:
string reverseString(string s)
{
int i=0,j=s.size()-1;
while(i
349 两个数组的交集
给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
说明:
- 输出结果中的每个元素一定是唯一的。
- 我们可以不考虑输出结果的顺序。
使用set容器
class Solution {
public:
vector intersection(vector& nums1, vector& nums2)
{
if(nums1.empty() || nums2.empty()) return vector ();
set s(nums1.begin(),nums1.end()),res;
//printf set
set::iterator iter=s.begin();
while(iter!=s.end())
{
cout<<"the value of s:"<<*iter;
++iter;
}
for(auto num:nums2)
{
if(s.count(num))
{
res.insert(num);
}
}
return vector(res.begin(),res.end());
}
};
788 旋转数字
我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。
如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方;6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。
现在我们有一个正整数 N, 计算从 1 到 N 中有多少个数 X 是好数?
示例:
输入: 10
输出: 4
解释:
在[1, 10]中有四个好数: 2, 5, 6, 9。
注意 1 和 10 不是好数, 因为他们在旋转之后不变。
注意:
N 的取值范围是 [1, 10000]。
class Solution {
public:
int rotatedDigits(int N)
{
int res=0;
for(int i=0;i<=N;i++)
{
if(check(i))
{
res++;
}
}
return res;
}
bool check(int n)
{
string str=to_string(n);
bool flag=false;
for(auto c:str)
{
if(c=='3' || c=='4' || c=='7') return false;
if(c=='2' || c=='5' || c=='6' || c=='9') flag=true;
}
return flag;
}
};
796 旋转字符串
给定两个字符串, A 和 B。
A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = ‘abcde’,在移动一次之后结果就是’bcdea’ 。如果在若干次旋转操作之后,A 能变成B,那么返回True。
示例 1:
输入: A = 'abcde', B = 'cdeab'
输出: true
示例 2:
输入: A = 'abcde', B = 'abced'
输出: false
注意:
A 和 B 长度不超过 100。
class Solution {
public:
bool rotateString(string A, string B)
{
if(A.size()==0 && B.size()==0) return true;
if(A.size()!=B.size()) return false;
for(int i=0;i
804 唯一摩尔斯密码词
国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: “a” 对应 “.-”, “b” 对应 “-…”, “c” 对应 “-.-.”, 等等。
为了方便,所有26个英文字母对应摩尔斯密码表如下:
[".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."]
给定一个单词列表,每个单词可以写成每个字母对应摩尔斯密码的组合。例如,“cab” 可以写成 “-.-.-…-”,(即 “-.-.” + “-…” + ".-"字符串的结合)。我们将这样一个连接过程称作单词翻译。
返回我们可以获得所有词不同单词翻译的数量。
例如:
输入: words = ["gin", "zen", "gig", "msg"]
输出: 2
解释:
各单词翻译如下:
"gin" -> "--...-."
"zen" -> "--...-."
"gig" -> "--...--."
"msg" -> "--...--."
共有 2 种不同翻译, "--...-." 和 "--...--.".
注意:
- 单词列表words 的长度不会超过 100。
- 每个单词 words[i]的长度范围为 [1, 12]。
- 每个单词 words[i]只包含小写字母。
class Solution {
public:
int uniqueMorseRepresentations(vector& words)
{
vector morse{".-","-...","-.-.","-..",".","..-.","--.","....","..",".---","-.-",".-..","--","-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."};
set res;
for(auto word:words)
{
string temp="";
for(char c:word)
{
temp=temp+morse[c-'a'];
}
res.insert(temp);
}
return res.size();
}
};
371 两整数之和
题目描述提示帮助提交记录社区讨论阅读解答
不使用运算符 + 和-,计算两整数a 、b之和。
示例:
若 a = 1 ,b = 2,返回 3。
使用位运算的方法:我们在做加法运算的时候,每位相加之后可能会有进位Carry产生,然后在下一位计算时需要加上进位一起运算,那么我们能不能将两部分拆开呢,我们来看一个例子759+674
-
如果我们不考虑进位,可以得到323
-
如果我们只考虑进位,可以得到1110
-
我们把上面两个数字假期323+1110=1433就是最终结果了
然后我们进一步分析,如果得到上面的第一第二种情况,我们在二进制下来看,不考虑进位的加,0+0=0, 0+1=1, 1+0=1, 1+1=0
,这就是异或的运算规则,如果只考虑进位的加0+0=0, 0+1=0, 1+0=0, 1+1=1
,而这其实这就是与的运算,而第三步在将两者相加时,我们再递归调用这个算法,终止条件是当进位为0时,我们直接返回第一步的结果,参见代码如下:
class Solution {
public:
int getSum(int a, int b)
{
if(b==0) return a;
int sum=a^b;
int carry=(a&b)<<1;
return getSum(sum,carry);
}
};
迭代法:
class Solution {
public:
int getSum(int a, int b) {
while (b) {
int carry = (a & b) << 1;
a = a ^ b;
b = carry;
}
return a;
}
};
172 阶乘后的零
给定一个整数 n,返回 n! 结果尾数中零的数量。
示例 1:
输入: 3
输出: 0
解释: 3! = 6, 尾数中没有零。
示例 2:
输入: 5
输出: 1
解释: 5! = 120, 尾数中有 1 个零.
说明: 你算法的时间复杂度应为 O(log n) 。
分析:这道题是求一个数的阶乘末尾0的个数,也就是要找乘数中10的个数,而10可分解为2和5,而我们可知2的数量又远大于5的数量,那么此题只需要找出5的个数。仍需注意的一点就是,像25,125,这样的不只含有一个5的数字需要考虑进去。
class Solution {
public:
int trailingZeroes(int n)
{
int res=0;
while(n)
{
res +=n/5;
n=n/5;
}
return res;
}
};
235 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
_______6______
/ \
___2__ ___8__
/ \ / \
0 _4 7 9
/ \
3 5
示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
说明:
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉搜索树中。
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
TreeNode* res=NULL;
vector path;
vector path_p;
vector path_q;
int flag=0;
preorder(root,p,path,path_p,flag);
path.clear();
flag=0;
preorder(root,q,path,path_q,flag);
int min_length=min(path_p.size(),path_q.size());
for(int i=0;i &path,vector &res,int flag)
{
//vector path;//不能把path放在这里,要从外面传进来
//int flag=0;//这个变量也是,要从外面进来
if(root==NULL || flag) return;
path.push_back(root);
if(root==node)
{
res=path;
flag=1;
}
preorder(root->left,node,path,res,flag);
preorder(root->right,node,path,res,flag);
path.pop_back();
}
};
236 二叉树的最近公共祖先
代码同235
661 图片平滑器
包含整数的二维矩阵 M 表示一个图片的灰度。你需要设计一个平滑器来让每一个单元的灰度成为平均灰度 (向下舍入) ,平均灰度的计算是周围的8个单元和它本身的值求平均,如果周围的单元格不足八个,则尽可能多的利用它们。
示例 1:
输入:
[[1,1,1],
[1,0,1],
[1,1,1]]
输出:
[[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0
对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0
对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0
注意:
- 给定矩阵中的整数范围为 [0, 255]。
- 矩阵的长和宽的范围均为 [1, 150]。
class Solution {
public:
vector> imageSmoother(vector>& M)
{
if(M.size()==0 || M[0].size()==0) return M;
vector > dirs={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
int m=M.size();
int n=M[0].size();
vector > res(m,vector(n,0));
for(int i=0;i=m || y<0 || y>=n) continue;
cnt++;
sum=sum+M[x][y];
}
res[i][j]=sum/cnt;
}
}
return res;
}
};
217 存在重复元素
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例 1:
输入: [1,2,3,1]
输出: true
示例 2:
输入: [1,2,3,4]
输出: false
示例 3:
输入: [1,1,1,3,3,4,3,2,4,2]
输出: true
代码:一看到题目(和 389 找不同 类似的思路)就写出来了代码,我的思路非常简单。使用map记录每个元素出现的次数,然后每个元素的次数都-1,再判断该元素的次数是否>0,若是,则返回true。
class Solution {
public:
bool containsDuplicate(vector& nums)
{
map m;
for(int i=0;i0)
{
return true;
}
}
return false;
}
};
219 存在重复元素 II
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。
示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true
示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
首先定义一个pair的vector,目的是绑定元素和它对应的index,r然后进行元素大小排列,先判断两个元素相等时候,index之差是否<=k,如果是,就返回true.
bool cmp(pair &a,pair &b)//cmp函数应该定义在类外面
{
return a.first& nums, int k)
{
int len=nums.size();
vector >t;
for(int i=0;i
220 存在重复元素 III
给定一个整数数组,判断数组中是否有两个不同的索引 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ。
示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true
示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true
示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
和上一题的代码基本类似
bool cmp(pair &a,pair &b)//cmp函数应该定义在类外面
{
return a.first& nums, int k,int t)
{
int len=nums.size();
vector >tmp;
for(int i=0;i
231 2的幂
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 20 = 1
示例 2:
输入: 16
输出: true
解释: 24 = 16
示例 3:
输入: 218
输出: false
法1
class Solution {
public:
bool isPowerOfTwo(int n)
{
if(n<0) return false;
int cnt=0;
while(n)
{
cnt=cnt+(n&1);
n=n>>1;
}
return cnt==1;
}
};
万能法
class Solution {
public:
bool isPowerOfTwo(int n)
{
while(n && n%2==0)
{
n=n/2;
}
return n==1;
}
};
326 3的幂
class Solution {
public:
bool isPowerOfThree(int n)
{
while(n && n%3==0)
{
n=n/3;
}
return n==1;
}
};
342 4的幂
class Solution {
public:
bool isPowerOfFour(int n)
{
while(n && n%4==0)
{
n=n/4;
}
return n==1;
}
};
237 删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。
现有一个链表 – head = [4,5,1,9],它可以表示为:
4 -> 5 -> 1 -> 9
示例 1:
输入: head = [4,5,1,9], node = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], node = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
说明:
- 链表至少包含两个节点。
- 链表中所有节点的值都是唯一的。
- 给定的节点为非末尾节点并且一定是链表中的一个有效节点。
- 不要从你的函数中返回任何结果。
因为题目给的是删除节点,那说明这个节点可以舍弃了,我们把下一个节点的值拷贝给当前要删除的节点,再删除下一个节点。
大致过程如下(删除3):
1->2->3->4->5
1->2->4->4->5
1->2->4->5
这题太骚了:用覆盖的方法
class Solution {
public:
void deleteNode(ListNode* node)
{
node->val=node->next->val;
ListNode* temp=node->next;
node->next=temp->next;
delete temp;
}
};
203 删除链表中的节点
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
方法1:和上面那题(237)类似的思路,删除目标节点
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *dummy = new ListNode(-1), *pre = dummy;
dummy->next = head;
while (pre->next) {
if (pre->next->val == val) {
ListNode *t = pre->next;
pre->next = t->next;
t->next = NULL;
delete t;
} else {
pre = pre->next;
}
}
return dummy->next;
}
};
经典方法:跳过目标节点
class Solution {
public:
ListNode* removeElements(ListNode* head, int val)
{
ListNode* dummy=new ListNode(-1);
dummy->next=head;
ListNode* pre=dummy;
ListNode* cur=pre->next;
while(cur)
{
if(cur->val!=val)
{
pre=cur;
cur=cur->next;
}
else
{
pre->next=cur->next;
cur=pre->next;
}
}
return dummy->next;
}
};
876 链表的中间结点
给定一个带有头结点 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
代码:和 109类似的套路
class Solution {
public:
ListNode* middleNode(ListNode* head)
{
if(head==NULL || head->next==NULL)
return head;
ListNode* fast=head->next->next;
ListNode* slow=head;
while(fast && fast->next)
{
fast=fast->next->next;
slow=slow->next;
}
return slow->next;
}
};
205 同构字符串
给定两个字符串 s 和 t,判断它们是否是同构的。
如果 s 中的字符可以被替换得到 t ,那么这两个字符串是同构的。
所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。
示例 1:
输入: s = "egg", t = "add"
输出: true
示例 2:
输入: s = "foo", t = "bar"
输出: false
示例 3:
输入: s = "paper", t = "title"
输出: true
说明:
- 你可以假设 s 和 t 具有相同的长度。
class Solution {
public:
bool isIsomorphic(string s, string t)
{
map char_m1;
map char_m2;
for(int i=0;i
739. 每日温度
题目描述提示帮助提交记录社区讨论阅读解答
根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高的天数。如果之后都不会升高,请输入 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的都是 [30, 100] 范围内的整数。
分析:我的思路很简单,遍历每一个元素,对某一个元素来说,找到第一个大于它的数,此时:如果此时的是小于数组的size的,直接push_back(j-i),否则push 0.
class Solution {
public:
vector dailyTemperatures(vector& temperatures)
{
vector res;
for(int i=0;i
496 下一个更大元素 I
给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。
示例 2:
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于num1中的数字2,第二个数组中的下一个较大数字是3。
对于num1中的数字4,第二个数组中没有下一个更大的数字,因此输出 -1。
注意:
- nums1和nums2中所有元素是唯一的。
- nums1和nums2 的数组大小都不超过1000。
和上一题的套路有点像
class Solution {
public:
vector nextGreaterElement(vector& findNums, vector& nums)
{
vector res(findNums.size(),-1);
for(int i=0;i
503 下一个更大元素 II
给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
注意: 输入数组的长度不会超过 10000。
分析:依次遍历每一个元素。由于数组是循环的,所以先从前面往后面找第一个比它大的数字,如果找不到,就要那么就要从头开始找,第一个比它大的数字。
class Solution {
public:
vector nextGreaterElements(vector& nums)
{
vector res(nums.size(),-1);
if(nums.size()==0) return res;
int j=0;
while(nums[j]<=nums[nums.size()-1])
{
j++;
}
if(j=nums[j])
{
j++;
}
if(j
556 下一个更大元素 III
给定一个32位正整数 n,你需要找到最小的32位整数,其与 n 中存在的位数完全相同,并且其值大于n。如果不存在这样的32位整数,则返回-1。
示例 1:
输入: 12
输出: 21
示例 2:
输入: 21
输出: -1
这道题给了我们一个数字,让我们对各个位数重新排序,求出刚好比给定数字大的一种排序,如果不存在就返回-1。这道题给的例子的数字都比较简单,我们来看一个复杂的,比如12443322,这个数字的重排序结果应该为13222344,如果我们仔细观察的话会发现数字变大的原因是左数第二位的2变成了3,细心的童鞋会更进一步的发现后面的数字由降序变为了升序,这也不难理解,因为我们要求刚好比给定数字大的排序方式。那么我们再观察下原数字,看看2是怎么确定的,我们发现,如果从后往前看的话,2是第一个小于其右边位数的数字,因为如果是个纯降序排列的数字,做任何改变都不会使数字变大,直接返回-1。知道了找出转折点的方法,再来看如何确定2和谁交换,这里2并没有跟4换位,而是跟3换了,那么如何确定的3?其实也是从后往前遍历,找到第一个大于2的数字交换,然后把转折点之后的数字按升序排列就是最终的结果了。最后记得为防止越界要转为长整数型,然后根据结果判断是否要返回-1即可.
class Solution {
public:
int nextGreaterElement(int n)
{
string s=to_string(n);
int len=s.size()-1;
int i=len;
for(;i>0;i--)
{
if(s[i-1]=i;j--)
{
if(s[j]>s[i-1])
{
swap(s[j],s[i-1]);
break;
}
}
sort(s.begin()+i,s.end());
long long res=stoll(s);
return res>INT_MAX? -1:res;
}
};
268 缺失数字
给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 … n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
说明:
- 你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?
class Solution {
public:
int missingNumber(vector& nums)
{
int len=nums.size();
map m;
int res=0;
for(int i=0;i
113 路径总和 II
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
class Solution {
public:
vector> pathSum(TreeNode* root, int sum) {
vector >result;
vector path;
int path_sum=0;
preorder(root,path_sum,sum,path,result);
return result;
}
private:
void preorder(TreeNode* node, int &path_sum,int sum,vector &path,vector > &result)
{
if(!node)
{
return;
}
path_sum=path_sum+node->val;
path.push_back(node->val);
if(sum==path_sum && !node->left && !node->right)
{
result.push_back(path);
}
if(node->left) preorder(node->left,path_sum,sum,path,result);
if(node->right) preorder(node->right,path_sum,sum,path,result);
path_sum=path_sum-node->val;
path.pop_back();
}
};
257 二叉树的所有路径
题目描述提示帮助提交记录社区讨论阅读解答
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明:
- 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
class Solution {
public:
vector binaryTreePaths(TreeNode* root)
{
vector res;
if(!root) return res;
preorder(root,to_string(root->val),res);
return res;
}
private:
void preorder(TreeNode* root,string path,vector &res)
{
if(!root) return;
if(!root->left && !root->right)//说明是叶子节点
{
res.push_back(path);
}
if(root->left) preorder(root->left,path+"->"+to_string(root->left->val),res);
if(root->right) preorder(root->right,path+"->"+to_string(root->right->val),res);
}
};
437 路径总和 III
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等于 8 的路径有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
class Solution {
public:
int pathSum(TreeNode* root, int sum) {
int res = 0;
vector out;
helper(root, sum, 0, out, res);
return res;
}
void helper(TreeNode* node, int sum, int curSum, vector& out, int& res) {
if (!node) return;
curSum += node->val;
out.push_back(node);
if (curSum == sum) ++res;
int t = curSum;
for (int i = 0; i < out.size() - 1; ++i) {
t -= out[i]->val;
if (t == sum) ++res;
}
helper(node->left, sum, curSum, out, res);
helper(node->right, sum, curSum, out, res);
out.pop_back();
}
};
415 字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和。
注意:
- num1 和num2 的长度都小于 5100.
- num1 和num2 都只包含数字 0-9.
- num1 和num2 都不包含任何前导零。
- 你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。
之前LeetCode出过几道类似的题目,比如 67 二进制求和,还有 2 链表相加,和 258 各位相加 基本思路很类似,都是一位一位相加,然后算和算进位,最后根据进位情况看需不需要补一个高位
class Solution {
public:
string addStrings(string num1, string num2)
{
string res;
int i=num1.size()-1;
int j=num2.size()-1;
int sum=0,carry=0;
while(i>=0 || j>=0)
{
int a= i>=0? num1[i--]-'0' : 0;
int b= j>=0? num2[j--]-'0' : 0;
sum=carry+a+b;
res.insert(res.begin(),sum%10+'0');
carry=sum/10;
}
return carry==1? '1'+res : res;
}
};
我之前 2,67 题不是按照这题的代码结构写的,现在重写。
2 链表相加
class Solution {
public:
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
ListNode *res = new ListNode(-1);
ListNode *cur = res;
int carry = 0;
while (l1 || l2) {
int n1 = l1 ? l1->val : 0;
int n2 = l2 ? l2->val : 0;
int sum = n1 + n2 + carry;
carry = sum / 10;
cur->next = new ListNode(sum % 10);
cur = cur->next;
if (l1) l1 = l1->next;
if (l2) l2 = l2->next;
}
if (carry) cur->next = new ListNode(1);
return res->next;
}
};
67 二进制求和
class Solution {
public:
string addBinary(string a, string b) {
string res = "";
int m = a.size() - 1, n = b.size() - 1, carry = 0;
while (m >= 0 || n >= 0) {
int p = m >= 0 ? a[m--] - '0' : 0;
int q = n >= 0 ? b[n--] - '0' : 0;
int sum = p + q + carry;
res = to_string(sum % 2) + res;
carry = sum / 2;
}
return carry == 1 ? "1" + res : res;
}
};
43 字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = "2", num2 = "3"
输出: "6"
示例 2:
输入: num1 = "123", num2 = "456"
输出: "56088"
说明:
- num1 和 num2 的长度小于110。
- num1 和 num2 只包含数字 0-9。
- num1 和 num2 均不以零开头,除非是数字 0 本身。
- 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
分析:
首先我们把每一位相乘,得到一个没有进位的临时结果,如图中中间的一行红色数字就是临时结果,然后把临时结果从低位起依次进位。对于一个m位整数乘以n位整数的结果,最多只有m+n位。
这道题让我们求两个字符串数字的相乘,输入的两个数和返回的数都是以字符串格式储存的,这样做的原因可能是这样可以计算超大数相乘,可以不受int或long的数值范围的约束,那么我们该如何来计算乘法呢,我们小时候都学过多位数的乘法过程,都是每位相乘然后错位相加,那么这里就是用到这种方法,把错位相加后的结果保存到一个一维数组中,然后分别每位上算进位,最后每个数字都变成一位,然后要做的是去除掉首位0,最后把每位上的数字按顺序保存到结果中即可。
class Solution {
public:
string multiply(string num1, string num2)
{
string res;
int n1 = num1.size(), n2 = num2.size();
int k = n1 + n2 - 2, carry = 0;
vector v(n1 + n2, 0);
for (int i = 0; i < n1; ++i)
{
for (int j = 0; j < n2; ++j)
{
v[k - i - j] += (num1[i] - '0') * (num2[j] - '0');
}
}
for (int i = 0; i < n1 + n2; ++i) //处理进位
{
v[i] += carry;
carry = v[i] / 10;
v[i] %= 10;
}
int i = n1 + n2 - 1;
while (v[i] == 0) --i;//去掉乘积的前导0
if (i < 0) return "0";//注意乘积为0的特殊情况
while (i >= 0) res.push_back(v[i--] + '0');
return res;
}
};
202 快乐数
编写一个算法来判断一个数是不是“快乐数”。
一个“快乐数”定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是无限循环但始终变不到 1。如果可以变为 1,那么这个数就是快乐数。
示例:
输入: 19
输出: true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
这道题,很容易超出时间限制。大神的做法:我们可以用set来记录所有出现过的数字,然后每出现一个新数字,在set中查找看是否存在,若不存在则加入表中,若存在则跳出循环,并且判断此数是否为1,若为1返回true,不为1返回false。
class Solution {
public:
bool isHappy(int n)
{
set s;
while(n!=1)
{
int t=0;
while(n)
{
t +=(n%10)*(n%10);
n=n/10;
}
n=t;
if(s.count(n)) break;
else s.insert(n);
}
return n==1;
}
};
263. 丑数
编写一个程序判断给定的数是否为丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。
示例 1:
输入: 6
输出: true
解释: 6 = 2 × 3
示例 2:
输入: 8
输出: true
解释: 8 = 2 × 2 × 2
示例 3:
输入: 14
输出: false
解释: 14 不是丑数,因为它包含了另外一个质因数 7。
说明:
- 1 是丑数。
- 输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]。
输入不会超过 32 位有符号整数的范围: [−231, 231 − 1]。
我习惯性写成下面的代码,少了else,那么它就会一行一行往下走。错错错
class Solution {
public:
bool isUgly(int n)
{
while(n>=2)
{
if(n%2==0) n=n/2;
if(n%3==0) n=n/3;
if(n%5==0) n=n/5;
else return false;
}
return n==1;
}
};
正确的写法:
class Solution {
public:
bool isUgly(int n)
{
while(n>=2)
{
if(n%2==0) n=n/2;
else if(n%3==0) n=n/3;
else if(n%5==0) n=n/5;
else return false;
}
return n==1;
}
};
72 编辑距离
给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
示例 1:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution"
输出: 5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
分析:动态规划题。我觉得还是那位大神牛逼,能把别人认为那么难的问题,一下解释的那么好,顿时开光。题目关键是你要确定好状态变量。这里我们用dp[i][j]来表示word1的前i个字符串变换到word2的前j个字符串的最少操作数。那么我们思考前0个字符串到前j个字符串的解,有几个呢?答案是有j个。请看下图:
可以把可以把前0个字符串用空集来表示,空集到空集自然不用操作。空集到任何数量(设长度为j)的字符串的操作数就是j(就是把j个元素都作 插入 操作。)这样我们就处理好了边界。对于非边界呢,可以观察到,如果word1[i]=word2[j],那么说明dp[i][j]和(word1前i-1个)=>(word2前j-1个) 相等。其他的情况就是看dp[i][j]的相邻位置哪个更小,取最小的,然后+1。
class Solution {
public:
int minDistance(string word1, string word2)
{
int n1=word1.size();
int n2=word2.size();
int dp[n1+1][n2+1];
for(int i=0;i<=n1;i++)
{
dp[i][0]=i;
}
for(int i=0;i<=n2;i++)
{
dp[0][i]=i;
}
for(int i=1;i<=n1;i++)
{
for(int j=1;j<=n2;j++)
{
if(word1[i-1]==word2[j-1])
{
dp[i][j]=dp[i-1][j-1];
}
else
{
dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
}
}
}
return dp[n1][n2];
}
};
583. 两个字符串的删除操作
给定两个单词 word1 和 word2,找到使得 word1 和 word2 相同所需的最小步数,每步可以删除任意一个字符串中的一个字符。
示例 1:
输入: "sea", "eat"
输出: 2
解释: 第一步将"sea"变为"ea",第二步将"eat"变为"ea"
说明:
- 给定单词的长度不超过500。
- 给定单词中的字符只含有小写字母。
分析:
这一题和上一题【编辑距离】其实是一样的,用dp[i][j]来表示word1前i个字符变换到word2前j个字符的最少操作数。只不过没有插入操作,也就不能考虑那个dp[i-1][j-1]了
class Solution {
public:
int minDistance(string word1, string word2)
{
int n1=word1.size();
int n2=word2.size();
int dp[n1+1][n2+1];
for(int i=0;i<=n1;i++)
{
dp[i][0]=i;
}
for(int i=0;i<=n2;i++)
{
dp[0][i]=i;
}
for(int i=1;i<=n1;i++)
{
for(int j=1;j<=n2;j++)
{
if(word1[i-1]==word2[j-1])
{
dp[i][j]=dp[i-1][j-1];
}
else
{
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;
}
}
}
return dp[n1][n2];
}
};
289 生命游戏(回到目录)
根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
示例:
输入:
[
[0,1,0],
[0,0,1],
[1,1,1],
[0,0,0]
]
输出:
[
[0,0,0],
[1,0,1],
[0,1,1],
[0,1,0]
]
由于题目中要求我们用置换方法in-place来解题,所以我们就不能新建一个相同大小的数组,那么我们只能更新原有数组,但是题目中要求所有的位置必须被同时更新,但是在循环程序中我们还是一个位置一个位置更新的,那么当一个位置更新了,这个位置成为其他位置的neighbor时,我们怎么知道其未更新的状态呢,我们可以使用状态机转换:
状态0: 死细胞转为死细胞
状态1: 活细胞转为活细胞
状态2: 活细胞转为死细胞
状态3: 死细胞转为活细胞
最后我们对所有状态对2取余,那么状态0和2就变成死细胞,状态1和3就是活细胞,达成目的。我们先对原数组进行逐个扫描,对于每一个位置,扫描其周围八个位置,如果遇到状态1或2,就计数器累加1,扫完8个邻居,如果少于两个活细胞或者大于三个活细胞,而且当前位置是活细胞的话,标记状态2,如果正好有三个活细胞且当前是死细胞的话,标记状态3。完成一遍扫描后再对数据扫描一遍,对2取余变成我们想要的结果。
class Solution {
public:
void gameOfLife(vector>& board)
{
int m=board.size();
int n=board[0].size();
if(m==0 || n==0) return;
vector > dirs={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
for(int i=0;i=0 && x=0 && y3)) board[i][j]=2;//我这样写不会引起歧义
else if(board[i][j]==0 && cnt==3) board[i][j]=3;
}
}
for(int i=0;i
383 赎金信(回到目录)
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。)
注意:
你可以假设两个字符串均只含有小写字母。
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
老套路,哈希统计出现的次数
class Solution {
public:
bool canConstruct(string ransomNote, string magazine)
{
map m;
for(auto c:magazine)
{
m[c]++;
}
for(auto c:ransomNote)
{
if(--m[c]<0)
return false;
}
return true;
}
};
387 字符串中的第一个唯一字符(回到目录)
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
老套路了
class Solution {
public:
int firstUniqChar(string s)
{
if(s.empty()) return -1;
map m;
for(char c:s)
{
m[c]++;
}
int i=0;
for(;i
451 根据字符出现频率排序(回到目录)
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
"tree"
输出:
"eert"
解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
"cccaaa"
输出:
"cccaaa"
解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
"Aabb"
输出:
"bbAa"
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意’A’和’a’被认为是两种不同的字符。
这道题,比较简单,主要是重写cmp
bool cmp(pair &a,pair &b)
{
return (a.second>b.second || a.second==b.second && a.first > p;
string res;
map m;
for(int i=0;i
347 前K个高频元素(回到目录)
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
- 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
- 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
class Solution {
public:
vector topKFrequent(vector& nums, int k)
{
priority_queue > q;//定义一个pair的最大堆,默认应该是根据第一个参数来排序
map m;
//set > cnt;
vector res;
for(int i=0;i
242 有效的字母异位词(回到目录)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
- 你可以假设字符串只包含小写字母。
老套路,使用map来统计字符数量是否匹配
class Solution {
public:
bool isAnagram(string s, string t)
{
map m;
if(s.size() !=t.size()) return false;
for(int i=0;i
438 找到字符串中所有字母异位词(回到目录)
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
- 字母异位词指字母相同,但排列不同的字符串。
- 不考虑答案输出的顺序。
示例 1:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
示例 2:
输入:
s: "abab" p: "ab"
输出:
[0, 1, 2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。
这道题我也想直接用两个map来做的,但是map不能直接比较,所以就选择了两个vector来模拟哈希表。
class Solution {//使用两个map 统计两个string的字符数量
public:
vector findAnagrams(string s, string p)
{
vector res;
if(s.empty()) return res;
vector ss(256,0);//这里的256改为122也可以,只要大于z的ASCII码值就好
vector pp(256,0);
for(int i=0;i
567 字符串的排列(回到目录)
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo"
输出: False
注意:
- 输入的字符串只包含小写字母
- 两个字符串的长度都在 [1, 10,000] 之间
和上面那一题(438)一模一样
class Solution {
public:
bool checkInclusion(string s1, string s2)
{
if(s1.empty() || s2.empty()) return false;
vector m1(256,0),m2(256,0);
for(int i=0;i
234 回文链表(回到目录)
请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
关键是找出链表中点
class Solution {
public:
bool isPalindrome(ListNode* head)
{
if(!head || !head->next) return true;
ListNode* slow=head;
ListNode* fast=head;
stack s;
s.push(head->val);
while(fast->next && fast->next->next)//先找到中点以前的节点(含中点),并且把它压入栈中
{
slow=slow->next;
fast=fast->next->next;
s.push(slow->val);
}
if(fast->next==NULL) s.pop();//fast->next==NULL;说明此时的slow是中心位置(即,链表是奇数长度),所以要pop掉
while(slow->next)
{
slow=slow->next;
int temp=s.top();
s.pop();
if(temp!=slow->val)
{
return false;
}
}
return true;
}
};
174. 地下城游戏(回到目录)
一些恶魔抓住了公主(P)并将她关在了地下城的右下角。地下城是由 M x N 个房间组成的二维网格。我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主。
骑士的初始健康点数为一个正整数。如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡。
有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数);其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔法球(若房间里的值为正整数,则表示骑士将增加健康点数)。
为了尽快到达公主,骑士决定每次只向右或向下移动一步。
编写一个函数来计算确保骑士能够拯救到公主所需的最低初始健康点数。
例如,考虑到如下布局的地下城,如果骑士遵循最佳路径 右 -> 右 -> 下 -> 下,则骑士的初始健康点数至少为 7。
-2 (K) -3 3
-5 -10 1
10 30 -5 (P)
说明:
- 骑士的健康点数没有上限。
- 任何房间都可能对骑士的健康点数造成威胁,也可能增加骑士的健康点数,包括骑士进入的左上角房间以及公主被监禁的右下角房间。
分析:
class Solution {
public:
int calculateMinimumHP(vector>& dungeon)
{
//int res=0;
int row=dungeon.size();
int col=dungeon[0].size();
vector >dp(row,vector(col,0));
dp[row-1][col-1]=max(1,1-dungeon[row-1][col-1]);
for(int j=col-2;j>=0;j--)
{
dp[row-1][j]=max(1,dp[row-1][j+1]-dungeon[row-1][j]);
}
for(int i=row-2;i>=0;i--)
{
dp[i][col-1]=max(1,dp[i+1][col-1]-dungeon[i][col-1]);
}
for(int i=row-2;i>=0;i--)
{
for(int j=col-2;j>=0;j--)
{
int dp_min=min(dp[i+1][j],dp[i][j+1]);
dp[i][j]=max(1,dp_min-dungeon[i][j]);
}
}
return dp[0][0];
304 二维区域和检索 - 矩阵不可变(回到目录)
给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2)。
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。
示例:
给定 matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]
sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
class NumMatrix {
public:
vector > v;
NumMatrix(vector > matrix) {
int m = matrix.size();
if(matrix.empty())
return ;
int n = matrix[0].size();
v= vector > (m, vector(n));
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
if(i == 0 && j == 0) {
v[i][j] = matrix[0][0];
} else if(i == 0) {
v[i][j] = v[i][j-1] + matrix[i][j];
} else if(j == 0) {
v[i][j] = v[i-1][j] + matrix[i][j];
} else {
v[i][j] = v[i-1][j] + v[i][j-1] + matrix[i][j] - v[i-1][j-1];
}
}
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
if(row1 == 0 && col1 == 0) {
return v[row2][col2];
} else if(row1 == 0) {
return v[row2][col2] - v[row2][col1-1];
} else if(col1 == 0) {
return v[row2][col2] - v[row1-1][col2];
} else {
return v[row2][col2] - v[row1-1][col2] - v[row2][col1-1] + v[row1-1][col1-1];
}
}
};
300 最长上升子序列(回到目录)
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
class Solution {
public:
int lengthOfLIS(vector& nums)
{
if(nums.size()==0) return 0;
vector dp(nums.size(),0);
dp[0]=1;
int res=1;
for(int i=1;inums[i-1])
{
dp[i]=dp[i-1]+1;
}
if(res
338 比特位计数(回到目录)
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
方法:假设构造一颗二叉树,根节点为1,从左到右从上到下分别是1,2,3,4…的二进制,可以发现如下规律:左子树是给根节点在末尾加0,右结点是给根节点在末尾加1,可得10和11;后来,10成为根节点,它左子树是100,右子树是101;11为根节点的树,左子树是110,右子树是111,同样是左边加0,右边加1…也就是当前数为偶数就在左子树,加0;如果当前数是奇数就在右子树,加1。如下图:
class Solution {
public:
vector countBits(int num)
{
vector dp(num+1);
dp[0]=0;
for(int i=1;i<=num;i++)
{
dp[i]=dp[i>>1]+i%2;
}
return dp;
}
};
279 完全平方数(回到目录)
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
分析:建立一个n+1长度的数组dp,dp[i]表示i这个数构成平方和需要数字的最小个数。
当jxj小于i的时候,temp中保存j从1开始每加1得到的dp[i-jxj] + 1的最小值
当jXj=i的时候,dp[i]的值就直接为1。从2一直到n可以计算出dp[i]的所有值。。
最后return dp[n]的值。
class Solution {
public:
int numSquares(int n) {
vector dp(n+1);
dp[1] = 1;
for(int i = 2; i <= n; i++) {
int temp = 99999999;
for(int j = 1; j * j <= i; j++) {
if(j * j == i) {
temp = 1;
break;
}
temp = min(temp, dp[i-j*j] + 1);
}
dp[i] = temp;
}
return dp[n];
}
};
91 解码方法
一条包含字母 A-Z 的消息通过以下方式进行了编码:
'A' -> 1
'B' -> 2
...
'Z' -> 26
给定一个只包含数字的非空字符串,请计算解码方法的总数。
示例 1:
输入: "12"
输出: 2
解释: 它可以解码为 "AB"(1 2)或者 "L"(12)。
示例 2:
输入: "226"
输出: 3
解释: 它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
这道题要求解码方法,跟之前那道 Climbing Stairs 爬梯子问题 非常的相似,但是还有一些其他的限制条件,比如说一位数时不能为0,两位数不能大于26,其十位上的数也不能为0,出去这些限制条件,根爬梯子基本没啥区别,也勉强算特殊的斐波那契数列,当然需要用动态规划Dynamci Programming来解。建立一位dp数组,长度比输入数组长多多2,全部初始化为1,因为斐波那契数列的前两项也为1,然后从第三个数开始更新,对应数组的第一个数。对每个数组首先判断其是否为0,若是将改为dp赋0,若不是,赋上一个dp值,此时相当如加上了dp[i - 1], 然后看数组前一位是否存在,如果存在且满足前一位不是0,且和当前为一起组成的两位数不大于26,则当前dp值加上dp[i - 2], 至此可以看出来跟斐波那契数组的递推式一样,代码如下:
class Solution {
public:
int numDecodings(string s) {
if (s.empty() || (s.size() > 1 && s[0] == '0')) return 0;
vector dp(s.size(), 0);
dp[0] = 1;
for (int i = 1; i < dp.size(); ++i) {
dp[i] = (s[i - 1] == '0') ? 0 : dp[i - 1];
if (i > 1 && (s[i - 2] == '1' || (s[i - 2] == '2' && s[i - 1] <= '6'))) {
dp[i] += dp[i - 2];
}
}
return dp.back();
}
};
96 不同的二叉搜索树(回到目录)
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
二分查找树的定义是,左子树节点均小于root,右子树节点均大于root,所以可以用递推的方法,把dp[i]表示i个数能够构成的二叉搜索树的个数.初始化边界值是 dp[0]=1,dp[1]=1,dp[2]=2。
当i>=3的时候,若以j为root结点,dp[j-1]等于root结点左边的j-1个结点能构成的BST个数.
dp[i-j]等于root结点右边i-j个结点能构成的BST个数。因为j+1i的种数和0i-j的种数一样,所以就是dp[i-j]所以dp[j-1] * dp[i-j]等于以j为root结点能构成的BST种数
j可以取1~i中的任意一个值,把这些所有计算出来的总数相加就是v[i]的值
1 n = 1
2 1 n = 2
/ \
1 2
1 3 3 2 1 n = 3
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
class Solution {
public:
int numTrees(int n)
{
vector dp(n+1);
dp[0]=1;
dp[1]=1;
dp[2]=2;
for(int i=3;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
dp[i] +=dp[i-1]*dp[i-j];
}
}
return dp[n];
}
};
443 压缩字符串
给定一组字符,使用原地算法将其压缩。
压缩后的长度必须始终小于或等于原数组长度。
数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。
在完成原地修改输入数组后,返回数组的新长度。
示例 1:
输入:
["a","a","b","b","c","c","c"]
输出:
返回6,输入数组的前6个字符应该是:["a","2","b","2","c","3"]
说明:
"aa"被"a2"替代。"bb"被"b2"替代。"ccc"被"c3"替代。
示例 2:
输入:
["a"]
输出:
返回1,输入数组的前1个字符应该是:["a"]
说明:
没有任何字符串被替代。
示例 3:
输入:
["a","b","b","b","b","b","b","b","b","b","b","b","b"]
输出:
返回4,输入数组的前4个字符应该是:["a","b","1","2"]。
说明:
由于字符"a"不重复,所以不会被压缩。"bbbbbbbbbbbb"被“b12”替代。
注意每个数字在数组中都有它自己的位置。
注意:
所有字符都有一个ASCII值在[35, 126]区间内。
1 <= len(chars) <= 1000。
class Solution {
public:
int compress(vector& chars)
{
int cur=0;
for(int i=0,j=0;i
693 交替位二进制数
给定一个正整数,检查他是否为交替位二进制数:换句话说,就是他的二进制数相邻的两个位数永不相等。
示例 1:
输入: 5
输出: True
解释:
5的二进制数是: 101
示例 2:
输入: 7
输出: False
解释:
7的二进制数是: 111
示例 3:
输入: 11
输出: False
解释:
11的二进制数是: 1011
示例 4:
输入: 10
输出: True
解释:
10的二进制数是: 1010
class Solution {
public:
bool hasAlternatingBits(int n)
{
//if(n==1) return false;
int bit=-1;
while(n>0)
{
if(n&1==1)
{
if(bit==1) return false;
bit=1;
}
else
{
if(bit==0) return false;
bit=0;
}
n=n>>1;
}
return true;
}
};