目录
1. 两数之和
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
//给vector拓展,加上id并排序
template<typename T>
bool cmp(T x,T y)
{
return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
vector<pair<T,int>>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
return ans;
}
//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
template<typename T>
vector<vector<int>> findSum(vector<T>v1,vector<T>v2,T s)
{
vector<vector<int>>ans;
vector<int>tmp(2);
vector<pair<T,int>>v3=sortWithId(v2);
sort(v2.begin(), v2.end(),cmp<T>);
for(int i=0;i<v1.size();i++)
{
auto it1=lower_bound(v2.begin(), v2.end(), s-v1[i]);
auto it2=upper_bound(v2.begin(), v2.end(), s-v1[i]);
tmp[0]=i;
for(auto j=it1;j<it2;j++)
{
tmp[1]=v3[j-v2.begin()].second;
ans.push_back(tmp);
}
}
return ans;
}
//删除二维vector中,含有重复元素的行
template<typename T>
vector<vector<T>> deleteSame(vector<vector<T>>&v)
{
vector<vector<int>>ans;
ans.reserve(v.size());
for(int i=0;i<v.size();i++)
{
for(int j=0;j<v[i].size();j++)for(int k=j+1;k<v[i].size();k++)if(v[i][j]==v[i][k])goto here;
ans.push_back(v[i]);
here:;
}
return ans;
}
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<vector<int>>ans=findSum(nums,nums,target);
return deleteSame(ans)[0];
}
};
9. 回文数
题目:
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
代码:
class Solution {
public:
bool isPalindrome(int x) {
if (x < 0)return false;
vector<int>num;
while (x)
{
num.insert(num.end(), x % 10);
x /= 10;
}
int len = num.size();
for (int i = 0, j = len - 1; i < j; i++, j--)
{
if (num[i] - num[j])return false;
}
return true;
}
};
11. 盛最多水的容器
题目:
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
代码:
class Solution {
public:
int maxArea(vector<int>& height) {
int i = 0, j = height.size() - 1;
int ans = 0;
while (i < j)
{
ans = max(ans, (j - i)*min(height[i], height[j]));
if (height[i] < height[j])i++;
else j--;
}
return ans;
}
};
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
经过各种优化之后,还是卡在了第311 / 313个用例,这个用例有3000个数。
//给vector拓展,加上id并排序
template<typename T>
bool cmp(T x,T y)
{
return x<y;
}
template<typename T>
vector<pair<T,int>> sortWithId(vector<T>v)
{
vector<pair<T,int>>ans;
ans.resize(v.size());
for(int i=0;i<v.size();i++)ans[i].first=v[i],ans[i].second=i;
sort(ans.begin(),ans.end(),[](pair<T,int>p1,pair<T,int>p2){return cmp(p1.first,p2.first);});
return ans;
}
//2个vector中寻找和为s的对,返回结果的每一行都是[id1,id2]
template<typename T>
vector<vector<int>> findSum(vector<T>v1,vector<T>v2,T s)
{
vector<vector<int>>ans;
int m=min(int(v1.size()*v2.size()),1234567);
ans.reserve(m);
vector<int>tmp(2);
vector<pair<T,int>>v3=sortWithId(v2);
sort(v2.begin(), v2.end(),cmp<T>);
for(int i=0;i<v1.size();i++)
{
auto it1=lower_bound(v2.begin(), v2.end(), s-v1[i]);
auto it2=upper_bound(v2.begin(), v2.end(), s-v1[i]);
tmp[0]=i;
for(auto j=it1;j<it2;j++)
{
tmp[1]=v3[j-v2.begin()].second;
ans.push_back(tmp);
}
}
return ans;
}
//删除二维vector中,含有重复元素的行
template<typename T>
vector<vector<T>> deleteSame(vector<vector<T>>&v)
{
vector<vector<int>>ans;
ans.reserve(v.size());
for(int i=0;i<v.size();i++)
{
for(int j=0;j<v[i].size();j++)for(int k=j+1;k<v[i].size();k++)if(v[i][j]==v[i][k])goto deleteSameHere;
ans.push_back(v[i]);
deleteSameHere:;
}
return ans;
}
//枚举2个vector的两数之和
template<typename T>
vector<T> everySum(vector<T>&v1,vector<T>&v2)
{
vector<T>ans;
ans.resize(v1.size()*v2.size());
int k=0;
for(int i=0;i<v1.size();i++)for(int j=0;j<v2.size();j++)ans[k++]=v1[i]+v2[j];
return ans;
}
//把id数组转化为对应的数v[id]
template<typename T>
vector<T> fgetNumFromId(vector<T> &v,vector<int>id)
{
vector<T>ans;
ans.resize(id.size());
for(int i=0;i<id.size();i++)ans[i]= (id[i]>=0 && id[i]<v.size()) ? v[id[i]] : -1;
return ans;
}
//二维id数组转化对应的数的二维数组
template<typename T>
vector<vector<T>> fgetNumFromId2(vector<T> &v,vector<vector<int>>&id)
{
vector<vector<T>>ans;
ans.reserve(id.size());
for(int i=0;i<id.size();i++)ans.push_back(fgetNumFromId(v,id[i]));
return ans;
}
//二维数组每一行排序
template<typename T>
vector<vector<T>> sort2(vector<vector<T>>&v)
{
for(int i=0;i<v.size();i++)sort(v[i].begin(),v[i].end());
return v;
}
//判断2个vector是否全等
template<typename T>
bool isSame(vector<T>&v1,vector<T>&v2)
{
if(v1.size()-v2.size())return false;
for(int i=0;i<v1.size();i++)if(v1[i]!=v2[i])return false;
return true;
}
//二维数组去掉重复行
template<typename T>
vector<vector<T>> deleteSameLine(vector<vector<T>>&v)
{
vector<vector<T>>ans;
ans.reserve(v.size());
for(int i=0;i<v.size();i++)
{
for(int j=i+1;j<v.size();j++)if(isSame(v[i],v[j]))goto deleteSameLineHere;
ans.push_back(v[i]);
deleteSameLineHere:;
}
return ans;
}
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
map<int,int>m;
for(int i=0;i<nums.size();i++)m[nums[i]]++;
vector<int>tmp(3);
vector<vector<int>>ans2;
tmp[0]=tmp[1]=tmp[2]=0;
if(m[0]>=3)ans2.push_back(tmp);
sort(nums.begin(),nums.end());
for(int i=2;i<nums.size();i++)if(nums[i]==nums[i-2])nums.erase(nums.begin()+i--);
for(int i=1;i<nums.size();i++)if(nums[i]==nums[i-1])
{
if(m[-nums[i]*2] && nums[i])tmp[0]=tmp[1]=nums[i],tmp[2]=-nums[i]*2,ans2.push_back(tmp);
nums.erase(nums.begin()+i--);
}
vector<int>num2=everySum(nums,nums);
vector<vector<int>>ans=findSum(num2,nums,0);
for(int i=0;i<ans.size();i++)ans[i].push_back(ans[i][0]%nums.size()),ans[i][0]/=nums.size();
ans=deleteSame(ans);
ans=fgetNumFromId2(nums,ans);
ans=sort2(ans);
ans=deleteSameLine(ans);
ans.resize(ans.size()+ans2.size());
copy(ans2.begin(),ans2.end(),ans.end()-ans2.size());
return ans;
}
};
如果不是因为最近沉迷写小模板,其实根本不需要这么做的。
用三指针挪来挪去的方法更快。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
map<int,int>m;
for(int i=0;i<nums.size();i++)m[nums[i]]++;
vector<int>tmp(3);
vector<vector<int>>ans,ans2;
ans.reserve(nums.size()*nums.size());
tmp[0]=tmp[1]=tmp[2]=0;
if(m[0]>=3)ans2.push_back(tmp);
sort(nums.begin(),nums.end());
for(int i=2;i<nums.size();i++)if(nums[i]==nums[i-2])nums.erase(nums.begin()+i--);
for(int i=1;i<nums.size();i++)if(nums[i]==nums[i-1])
{
if(m[-nums[i]*2] && nums[i])tmp[0]=tmp[1]=nums[i],tmp[2]=-nums[i]*2,ans2.push_back(tmp);
nums.erase(nums.begin()+i--);
}
for(int i=0;i<nums.size();i++)
{
for(int j=i+1,k=nums.size()-1;j<k;)
{
if(nums[i]+nums[j]+nums[k]>0)k--;
else if(nums[i]+nums[j]+nums[k]<0)j++;
else
{
tmp[0]=nums[i],tmp[1]=nums[j],tmp[2]=nums[k];
ans.push_back(tmp);
k--,j++;
}
}
}
ans.resize(ans.size()+ans2.size());
copy(ans2.begin(),ans2.end(),ans.end()-ans2.size());
return ans;
}
};
19. 删除链表的倒数第N个节点 (链表的离散跳跃)
题目:
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
分析:
第一个想到的思路就是双指针,但是当n较小时,这个思路其实就是等同于扫描了2遍。
双指针一起扫描一遍和一个指针扫描2遍真的有区别吗?我认为没区别。
而我给出了基本上可以说是真正只扫描了一遍的算法:
链表的离散跳跃
当n比较小时,比如n<10,还是双指针,但是可以每次往前跳10步,一个指针要遍历10个节点,另外一个指针可以直接跳过去。
当n比较大时,比如n>100,每次跳10步的话,就需要保存很多中间指针,不可取。
我的算法是,如果n>100,那么每次跳n/10步,只需要保存11个指针即可。
注意,这里需要用到循环覆盖的技巧。
代码:
#define m 10
#define m2 100
typedef pair<ListNode*, int>thePair;
class Solution {
public:
thePair move(ListNode* p, int k)
{
while (k && p)
{
k--,p = p->next;
}
return make_pair(p, k);
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* p = head;
ListNode* point[m + 1];//存跳跃点
int nplus = n + m - n % m;
int dif = max(nplus / m, m2);//单次跳跃距离
int num = 0;//跳跃次数
thePair pa;
while (true)
{
pa = move(p, dif);
if (pa.second > 0)break;
point[num % (m + 1)] = pa.first;
num++;
}
int dist = dif - pa.second;
while (dist < n)
{
num = (num + m) % (m + 1);
dist += dif;
}
ListNode* ans = point[num];
if (num == 0)ans = head;
if (dist == n)return head->next;
while (dist > n+1)
{
ans = ans->next;
dist--;
}
ans->next = ans->next->next;
return head;
}
};
29. 两数相除
题目:
给定两个整数,被除数 dividend 和除数 divisor。将两数相除,要求不使用乘法、除法和 mod 运算符。
返回被除数 dividend 除以除数 divisor 得到的商。
示例 1:
输入: dividend = 10, divisor = 3
输出: 3
示例 2:
输入: dividend = 7, divisor = -3
输出: -2
说明:
被除数和除数均为 32 位有符号整数。
除数不为 0。
假设我们的环境只能存储 32 位有符号整数,其数值范围是 [−231, 231 − 1]。本题中,如果除法结果溢出,则返回 231 − 1。
代码:
class Solution {
public:
int divide(int dividend, int divisor) {
if(divisor==-1 && dividend==-2147483648 )return 2147483647;
return dividend/divisor;
}
};
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<int>& nums, int target) {
if (nums.empty())return -1;
int low = 0, high = nums.size() - 1, mid;
while (low < high)
{
mid = (high + low) / 2;
if (nums[mid] == target)return mid;
if (nums[mid] >= nums[low])
{
if (target >= nums[low] && target < nums[mid])high = mid - 1;
else low = mid + 1;
}
else
{
if (target>nums[mid] && target <= nums[high])low = mid + 1;
else high = mid - 1;
}
}
if (nums[low] == target)return low;
return -1;
}
};
PS:
我在大三找实习的时候,面试一家公司做过这个题目。
当时我第一感觉就是O(n)的算法,面试官问我有没有O(log n)的算法,我简单说了大概思路,
面试官还要我把具体分类情况列出来,我没能成功。
其实不难,真的一点不难,但是面试稍微有点紧张,没有理清楚。
36. 有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.' 表示。
示例 1:
输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
示例 2:
输入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。
class Solution {
public:
bool ok(vector<vector<char>>& board, int i, int j, int k)
{
for (int jj = 0; jj < 9; jj++)if (jj!=j && board[i][jj] == k)return false;
for (int ii = 0; ii < 9; ii++)if (ii!=i && board[ii][j] == k)return false;
int x = i / 3 * 3, y = j / 3 * 3;
for (int ii = x; ii < x + 3; ii++)for (int jj = y; jj < y + 3; jj++)
if ((ii!=i||jj!=j) && board[ii][jj] == k)return false;
return true;
}
bool isValidSudoku(vector<vector<char>>& board) {
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(board[i][j]!='.' && !ok(board,i,j,board[i][j])){
return false;
}
}
}
return true;
}
};
42. 接雨水
题目:
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
代码:
class Solution {
public:
int getMax(vector<int>& height, int i, int j)
{
int ansMax = -1, ans = -1;
for (int k = i; k <= j; k++){
if (ansMax < height[k]){
ans = k, ansMax = height[k];
}
}
return ans;
}
int getSum(vector<int>& height, int i,int j)
{
int ans = 0;
for (int k = i; k <= j; k++)ans += height[k];
return ans;
}
int trap(vector<int>& height,int i,int j) {
if (j - i <= 1)return 0;
int k = getMax(height, i + 1, j - 1);
if (height[k] <= height[i] && height[k] <= height[j]){
return min(height[i], height[j])*(j - i) + max(height[i], height[j]) - getSum(height, i, j);
}
return trap(height, i, k) + trap(height, k, j);
}
int trap(vector<int>& height) {
if (height.size() < 2)return 0;
return trap(height, 0, height.size() - 1);
}
};
45. 跳跃游戏 II
题目:
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:
假设你总是可以到达数组的最后一个位置。
代码:
class Solution {
public:
int jump(vector<int>& nums,int left,int right) {
if(right>=nums.size()-1)return 0;
int k=0;
for(int i=left;i<=right;i++)k=max(k,i+nums[i]);
return jump(nums,right+1,k)+1;
}
int jump(vector<int>& nums) {
return jump(nums,0,0);
}
};
49. 字母异位词分组
题目:
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
思路:
逐个读取字符串,如果是第一次出现,就加入到一个新列表中,如果已经出现过在某个列表中,就加入这个列表的末尾。
由于一个列表中的字符串都是一样的,所以只要和每个列表的第一个字符串比较就行了。
但是,怎么比较两个字符串呢?
思路一:
计数,统计字母出现个数,从而比较2个字符串。
代码:
class Solution {
public:
bool issame(string s1, string s2)
{
int num[26] = { 0 };
for (int i = 0; i < s1.length(); i++)num[s1[i] - 'a']++;
for (int i = 0; i < s2.length(); i++)num[s2[i] - 'a']--;
for (int i = 0; i < 26; i++)if (num[i])return false;
return true;
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
for (int i = 0; i < strs.size(); i++)
{
string str = strs[i];
bool flag = true;
for (int i = 0; flag && i < ans.size(); i++)
{
if (issame(ans[i][0], str))
{
flag = false;
ans[i].insert(ans[i].end(), str);
}
}
if (flag)
{
vector<string>tmp;
tmp.insert(tmp.end(), str);
ans.insert(ans.end(), tmp);
}
}
return ans;
}
};
在最后一个用例超时了。
思路二:
把一个字符串映射到一个整数,比较两个字符串是否一样时只看是不是一个整数就行了。
同时,把每次映射的整数存到map里面,就不用一个个找,可以直接找到下标。
代码:
class Solution {
public:
int change(string s)
{
int num[26] = { 0 }, ans = 0;
long long k = 12345;
for (int i = 0; i < s.length(); i++)num[s[i] - 'a']++;
for (int i = 0; i < 26; i++)ans += num[i] * k, k = (k*k + 1) % 1234567890;
return ans;
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
map<int, int>m;
int k = 0;
for (int i = 0; i < strs.size(); i++)
{
int x = change(strs[i]);
if (m[x]==0)
{
m[x] = ++k;
vector<string>tmp;
tmp.insert(tmp.end(), strs[i]);
ans.insert(ans.end(), tmp);
}
else
{
ans[m[x]-1].insert(ans[m[x]-1].end(), strs[i]);
}
}
return ans;
}
};
51. N皇后
题目:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
代码:
int m;
int list_[100];
vector<vector<string>>ans;
void place(int row)
{
if (row == m){
string s;
vector<string>res;
for (int j = 0; j < m; j++){
s = "";
for (int i = 0; i < list_[j]; i++)s.append(".");
s.append("Q");
for (int i = list_[j]+1; i < m; i++)s.append(".");
res.insert(res.end(), s);
}
ans.insert(ans.end(), res);
return;
}
for (int i = 0; i < m; i++)
{
bool flag = true;
for (int j = 0; j < row; j++){
if (list_[j] == i || list_[j] - j == i - row || list_[j] + j == i + row){
flag = false;
}
}
if (flag)
{
list_[row] = i;
place(row + 1);
}
}
}
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
ans.clear();
if (n <= 0)return ans;
m = n;
place(0);
return ans;
}
};
52. N皇后 II
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回 n 皇后不同的解决方案的数量。
示例:
输入: 4
输出: 2
解释: 4 皇后问题存在如下两个不同的解法。
[
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
代码:
int m, sum;
int list_[100];
void place(int row)
{
if (row == m)sum++;
for (int i = 0; i < m; i++)
{
bool flag = true;
for (int j = 0; j < row; j++)
if (list_[j] == i || list_[j] - j == i - row || list_[j] + j == i + row)flag = false;
if (flag)
{
list_[row] = i;
place(row + 1);
}
}
}
class Solution {
public:
int totalNQueens(int n) {
m = n;
sum = 0;
place(0);
return sum;
}
};
55. 跳跃游戏
题目:
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
代码:
class Solution {
public:
bool canJump(vector<int>& nums) {
int i=0,k=0;
while(i<nums.size() && i<=k)k = max(k,nums[i] + i++);
return i==nums.size();
}
};
56. 合并区间
题目:
给出一个区间的集合,请合并所有重叠的区间。
示例 1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
思路:
先排序,再按顺序依次合并即可
代码:
struct node{
int a, b;
};
bool cmp(node n1,node n2){
return n1.a < n2.a;
}
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
for (auto it = intervals.begin(); it != intervals.end(); ){
if ((*it).size()<2 || (*it)[0] > (*it)[1])intervals.erase(it);
else it++;
}
vector<vector<int>>ans;
int size = intervals.size();
if (intervals.empty())return ans;
node *p = new node[size];
for (int k = 0; k < size; k++){
p[k].a = intervals[k][0], p[k].b = intervals[k][1];
}
sort(p, p + size, cmp);
vector<int>tmp;
for (int k = 0; k < size;){
tmp.clear();
tmp.insert(tmp.end(), p[k].a);
tmp.insert(tmp.end(), p[k].b);
k++;
while (k < size){
if (p[k].a <= tmp[1]){
tmp[1] = max(tmp[1],p[k].b);
k++;
}
else{
break;
}
}
ans.insert(ans.end(), tmp);
}
return ans;
}
};
第二次做这个题目:
bool cmp(vector<int> x, vector<int> y)
{
return x[0]<y[0];
}
vector<vector<int>> fmerge(vector<vector<int>>v)
{
if(v.empty())return v;
vector<vector<int>>ans;
sort(v.begin(),v.end(),cmp);
vector<int>k,x;
for(int i=0;i<v.size();i++)
{
x=v[i];
if(k.empty())k=x;
if(k[1]>=x[0])k[1]=max(k[1],x[1]);
else
{
ans.push_back(k);
k=x;
}
}
ans.push_back(k);
return ans;
}
60. 第k个排列
给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
说明:
给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: "213"
示例 2:
输入: n = 4, k = 9
输出: "2314"
int listt[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320};
class Solution {
public:
string getPermutation(int n, int k) {
vector<int>v(10,1);
char c='0';
string s="";
for(int i=1;i<=9;i++)v[i]=i;
for(int i=n-1;i>=0;i--)
{
int fac=listt[i];
int r=(k+fac-1)/fac;
s+=c+v[r];
k-=(r-1)*fac;
v.erase(v.begin()+r);
}
return s;
}
};
63. 不同路径 II (二维DP)
一个机器人位于一个 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:
vector<vector<int>>ans;
int dp(vector<vector<int>>& obs,int x,int y)
{
if(x<0||x>=obs.size()||y<0||y>=obs[0].size()||obs[x][y])return 0;
if(ans[x][y])return ans[x][y];
return ans[x][y]=dp(obs,x,y-1)+dp(obs,x-1,y);
}
int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
ans=obstacleGrid;
for(int i=0;i<ans.size();i++)for(int j=0;j<ans[0].size();j++)ans[i][j]=0;
ans[0][0]=1;
return dp(obstacleGrid,obstacleGrid.size()-1,obstacleGrid[0].size()-1);
}
};
我的非递归写法:
class Solution {
public:
vector<vector<int>>ans;
int uniquePathsWithObstacles(vector<vector<int>>& obs) {
vector<vector<int>> ans=obs;
ans[0][0]=(obs[0][0]?0:1);
for(int j=1;j<obs[0].size();j++)ans[0][j]=(obs[0][j]?0:ans[0][j-1]);
for(int i=1;i<obs.size();i++){
ans[i][0]=(obs[i][0]?0:ans[i-1][0]);
for(int j=1;j<obs[0].size();j++)ans[i][j]=(obs[i][j]?0:ans[i][j-1]+ans[i-1][j]);
}
return ans[ans.size()-1][ans[0].size()-1];
}
};
我的空间压缩写法:
class Solution {
public:
vector<vector<int>>ans;
int uniquePathsWithObstacles(vector<vector<int>>& obs) {
vector<int> ans=obs[0];
ans[0]=(obs[0][0]?0:1);
for(int j=1;j<obs[0].size();j++)ans[j]=(obs[0][j]?0:ans[j-1]);
for(int i=1;i<obs.size();i++){
ans[0]=(obs[i][0]?0:ans[0]);
for(int j=1;j<obs[0].size();j++)ans[j]=(obs[i][j]?0:ans[j-1]+ans[j]);
}
return ans[ans.size()-1];
}
};
64. 最小路径和
题目:
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
代码:
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty())return -1;
vector<int>ans1 = grid[0];
for (int i = 1; i < ans1.size(); i++)
{
ans1[i] += ans1[i - 1];
}
vector<int>ans2;
for (int i = 1; i < grid.size(); i++)
{
ans2 = grid[i];
ans1[0] += ans2[0];
for (int i = 1; i < ans1.size(); i++)
{
ans1[i] = min(ans1[i], ans1[i - 1]) + ans2[i];
}
}
return ans1[ans1.size() - 1];
}
};
66. 加一
题目:
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例 1:
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
示例 2:
输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。
代码:
class Solution {
public:
vector<int> plusOne(vector<int>& digits) {
int key = 1;
for (auto it = digits.end() - 1;key && it >= digits.begin(); it--)
{
*it += key, key--;
key += (*it) / 10, *it %= 10;
}
if (key == 1)digits.insert(digits.begin(), 1);
return digits;
}
};
69. x 的平方根
题目:
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例 1:
输入: 4
输出: 2
示例 2:
输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。
代码:
class Solution {
public:
int mySqrt(int x) {
return int(sqrt(x));
}
};
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) {
if (n > 45)return 0;
int list[46];
list[0] = list[1] = 1;
for (int i = 2; i <= n; i++)list[i] = list[i - 1] + list[i - 2];
return list[n];
}
};
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<vector<int>>& matrix, int target) {
if (matrix.size() == 0)return false;
if (matrix[0].size() == 0)return false;
vector<int>head;
for (auto it = matrix.begin(); it != matrix.end(); it++)
{
head.insert(head.end(), (*it)[0]);
}
auto it1 = lower_bound(head.begin(), head.end(), target);
auto it2 = upper_bound(head.begin(), head.end(), target);
if (it1 != it2)return true;
if (it1 == head.begin())return false;
head = matrix[it1 - head.begin() - 1];
it1 = lower_bound(head.begin(), head.end(), target);
it2 = upper_bound(head.begin(), head.end(), target);
return it1 != it2;
}
};
76. 最小覆盖子串
题目:
给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路:
首先记录t中每个字符出现的次数,比如假设字符A一共有3个,
然后扫描s,记录以当前字符结尾的包含3个A的最短子串长度,也就是说从这个字符往前第3个A的位置。
对于每个字符都有这么一个位置,即往前多少字符才有足够的字符,
取其中最大者就是所有字符都足够的最小长度。
实现方法一:只用数组
C语言AC代码:
#define MAX 1234567890
int getMin(int x[128])
{
int ans = MAX;
int i;
for (i = 0; i < 128; i++){
if (ans > x[i] && x[i] >= 0){
ans = x[i];
}
}
return ans;
}
char * minWindow(char * s, char * t){
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
int startLoc, tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
}
char *tmp = t;
while (*tmp != '\0'){
numOfLetter[*tmp]++; //初始化numOfLetter
haveLetter[*tmp] = true;
tmp++;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
numOfLetter[s[i]]--;
if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
while (true){
startLocOfLetter[s[i]]++; //滑动startLocOfLetter
if (s[startLocOfLetter[s[i]]] == s[i]){
break;
}
}
}
if (numOfLetter[s[i]] == 0){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = getMin(startLocOfLetter);
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
length = 0;
}
static char *ans;
ans = (char*)malloc(sizeof(char)*(length + 1));
for (i = 0; i<length; i++){
ans[i] = s[i + startLoc];
}
ans[length] = '\0';
return ans;
}
C++语言AC代码:
#include<string>
#define MAX 1234567890
int getMin(int x[128])
{
int ans = MAX;
int i;
for (i = 0; i < 128; i++){
if (ans > x[i]&&x[i]>=0){
ans = x[i];
}
}
return ans;
}
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
int startLoc, tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
}
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
numOfLetter[s[i]]--;
if (numOfLetter[s[i]] < 0 || startLocOfLetter[s[i]] < 0){
while (true){
startLocOfLetter[s[i]]++; //滑动startLocOfLetter
if (s[startLocOfLetter[s[i]]] == s[i]){
break;
}
}
}
if (numOfLetter[s[i]] == 0){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = getMin(startLocOfLetter);
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
实现方法二:利用C++的队列
C++语言AC代码:
#include<string>
#include<queue>
#define MAX 1234567890
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
queue<int>loca[128];
int startLoc,tempStartLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (int i = 0; i < 128; i++){
numOfLetter[i] = 0;
startLocOfLetter[i] = -1;
haveLetter[i] = false;
while (!loca[i].empty()){
loca[i].pop();
}
}
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
sumOfLetter--; //每当一个字母的数量足够时,sumOfLetter减1
}
loca[s[i]].push(i);
if (loca[s[i]].size() > numOfLetter[s[i]]){
loca[s[i]].pop();//保持数量
}
if (sumOfLetter <= 0){ //sumOfLetter小于0表示所有字母的个数已经足够
tempStartLoc = MAX;
for (int i = 0; i < 128; i++){
if (haveLetter[i] && tempStartLoc > loca[i].front()){
tempStartLoc = loca[i].front();
}
}
if (length > i - tempStartLoc + 1){
length = i - tempStartLoc + 1;
startLoc = tempStartLoc;
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
实现方法三:利用C++的队列+堆
C++语言AC代码:
#include<string>
#include<set>
#include<queue>
#define MAX 1234567890
class Solution {
public:
string minWindow(string s, string t) {
int numOfLetter[128];//numOfLetter表示每个字符的数量
int startLocOfLetter[128];//startLocOfLetter表示每个字符的第一个出现位置
queue<int>loca[128];
set<int>sLoca;
int startLoc;
int length = MAX;
bool haveLetter[128];//haveLetter表示每个字符是否出现过
int sumOfLetter = 0;//一共有多少不同的字符
int i;
for (i = 0; i < 128; i++){
numOfLetter[i] = 0;
haveLetter[i] = false;
while (!loca[i].empty()){
loca[i].pop();
}
}
sLoca.clear();
for (i = 0; i < t.length(); i++){
numOfLetter[t[i]]++; //初始化numOfLetter
haveLetter[t[i]] = true;
}
for (i = 0; i < 128; i++){
if (haveLetter[i]){
sumOfLetter++;
}
}
for (i = 0; s[i] != '\0'; i++){
if (!haveLetter[s[i]]){
continue;
}
if (loca[s[i]].size() == numOfLetter[s[i]] - 1){
loca[s[i]].push(i);
sLoca.insert(loca[s[i]].front());
}
else{
loca[s[i]].push(i);
}
if (loca[s[i]].size() > numOfLetter[s[i]]){
sLoca.erase(loca[s[i]].front());
loca[s[i]].pop();//保持数量
sLoca.insert(loca[s[i]].front());
}
if (sumOfLetter == sLoca.size()){ //表示所有字母的个数已经足够
if (length > i - *sLoca.begin() + 1){
length = i - *sLoca.begin() + 1;
startLoc = *sLoca.begin();
}
}
}
if (length == MAX){
return "";
}
string ans = s.substr(startLoc, length);
return ans;
}
};
81. 搜索旋转排序数组 II
题目:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6]
可能变为 [2,5,6,0,0,1,2]
)。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true
,否则返回 false
。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false
分析:
本题没有复杂度优于O(n)的算法。
代码:
class Solution {
public:
bool search(vector<int>& nums, int target) {
for (auto it = nums.begin(); it != nums.end(); it++)if (*it == target)return true;
return false;
}
};
82. 删除排序链表中的重复元素 II
给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。
示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
map<int,int>m;
ListNode*p=head;
while(p)m[p->val]++,p=p->next;
p=head;
while(p && m[p->val]>1)p=p->next;
head=p;
while(p)
{
while(p->next && m[p->next->val]>1)p->next=p->next->next;
p=p->next;
}
return head;
}
};
93. 复原IP地址
给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 '.' 分隔。
示例:
输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]
class Solution {
public:
vector<string> restoreIpAddresses(string s,int k) {
vector<string>ans;
if(s.length()==0)return ans;
if(k>1)for(int i=0;i<3 && i<s.length()-1;i++)
{
string s1=s.substr(0,i+1),s2=s.substr(i+1,s.length()-i-1);
int x=atoi(s1.c_str());
if(x>255 || s[0]=='0' && s1.length()>1 || s1.length()>3)continue;
vector<string>tmp=restoreIpAddresses(s2,k-1);
for(int j=0;j<tmp.size();j++)
{
string s3=tmp[j];
ans.push_back(s1+"."+s3);
}
}
else
{
int x=atoi(s.c_str());
if(x>255 || s[0]=='0' && s.length()>1 || s.length()>3);
else ans.push_back(s);
}
return ans;
}
vector<string> restoreIpAddresses(string s) {
return restoreIpAddresses(s,4);
}
};
94. 二叉树的中序遍历
题目:
给定一个二叉树,返回它的中序 遍历。
示例:
输入: [1,null,2,3]
1
\
2
/
3
输出: [1,3,2]
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
代码:
/*
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>v1;
if (root == NULL)return v1;
v1 = inorderTraversal(root->left);
v1.insert(v1.end(), root->val);
vector<int>v2 = inorderTraversal(root->right);
v1.insert(v1.end(), v2.begin(), v2.end());
return v1;
}
};
95. 不同的二叉搜索树 II
题目:
给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
示例:
输入: 3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
以上的输出对应以下 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
代码:
class Solution {
public:
vector<TreeNode*> generateTrees(int low,int high) {
vector<TreeNode*>ans;
if (low > high)
{
ans.insert(ans.end(), NULL);
return ans;
}
if (low == high)
{
TreeNode *p = new TreeNode(low);
ans.insert(ans.end(), p);
return ans;
}
for (int i = low; i <= high; i++)
{
vector<TreeNode*>v1 = generateTrees(low, i - 1);
vector<TreeNode*>v2 = generateTrees(i + 1, high);
for (int k = 0; k < v1.size(); k++)
{
for (int j = 0; j < v2.size(); j++)
{
TreeNode *p = new TreeNode(i);
p->left = v1[k], p->right = v2[j];
ans.insert(ans.end(), p);
}
}
}
return ans;
}
vector<TreeNode*> generateTrees(int n) {
vector<TreeNode*>ans;
if (n <= 0)return ans;
return generateTrees(1, n);
}
};
96. 不同的二叉搜索树
题目:
给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?
示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
思路:
递推式f(n)=f(n-1)+f(1)f(n-2)+...+f(n-2)f(1)+f(n-1)
代码:
class Solution {
public:
int numTrees(int n) {
if (n <= 0)return 1;
static map<int, int>ans;
if (ans[n])return ans[n];
int res = 0;
for (int i = 0; i < n; i++)res += numTrees(i)*numTrees(n - 1 - i);
return ans[n] = res;
}
};
98. 验证二叉搜索树
题目:
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
代码:
class Solution {
public:
bool isValidBST(TreeNode* root, long long low, long long high){
if (!root)return true;
if (root->val <= low || root->val >= high)return false;
return isValidBST(root->left, low, root->val) && isValidBST(root->right, root->val, high);
}
bool isValidBST(TreeNode* root) {
return isValidBST(root, -3312345678, 3312345678);
}
};