本文章记录贪心法的一些 LeetCode 题目,是我学习b站小象学院视频教程所做笔记,文末注明教程出处。侵删 ¯\_( ͡° ͜ʖ ͡°)_/¯
LeetCode [78] 子集
问题描述
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
递归与回溯方法
算法思路
图片来自小象学院教程
算法代码
class Solution {
public:
vector
<vector
<int>> subsets(vector
<int>& nums
) {
vector
<vector
<int>> result
;
vector
<int> item
;
result
.push_back(item
);
generate(0,nums
,item
,result
);
return result
;
}
private:
void generate(int i
,vector
<int>& nums
,vector
<int> &item
,vector
<vector
<int>>& 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
);
}
};
位运算方法
算法思路
用2进制数代表子集情况,每一位则可以代表每一个元素。例如A,B,C三个元素,则可以用三位的二进制数表示子集的情况,如001可代表集合中只有A这个元素,111即表示三个元素都存在的子集; 用按位与就可以判断每一个二进制码(代表子集的情况)是否含该元素,例如,用001代表A元素,010代表B元素,100代表C元素,则如果有101按位与代表A这个元素的特定码,即001,有:101 & 001 = 1,即 true,则代表其中含有A这个元素,然后将A加入item,这样一来就把二进制码101识别为[A,C],以此类推。
算法代码
class Solution {
public:
vector
<vector
<int>> subsets(vector
<int>& nums
) {
vector
<vector
<int>> result
;
int all_set
= 1 << nums
.size();
for(int i
=0;i
<all_set
;i
++){
vector
<int> item
;
for(int j
=0;j
<nums
.size();j
++){
if(i
& (1<<j
)){
item
.push_back(nums
[j
]);
}
}
result
.push_back(item
);
}
return result
;
}
};
LeetCode [90] 子集Ⅱ
题目描述
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例
输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
算法思想
在上文第一道题的递归算法思想上,要使得有不重复的子集,例如[1,2,1]和[1,1,2]也是属于重复情况。第一种重复情况是顺序一致的两个子集,另一种重复情况是顺序不一致的两个子集。故在前文递归算法基础上,只要对sums先进性排序,就能保证只有顺序一致的子集重复的情况,这时便可以用set集合进行查重去重操作。
算法代码
class Solution {
public:
vector
<vector
<int>> subsetsWithDup(vector
<int>& nums
) {
vector
<vector
<int>> result
;
vector
<int> item
;
set
<vector
<int>> 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
<int>& nums
,vector
<int> &item
,vector
<vector
<int>>& result
,set
<vector
<int>> &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
);
}
};
LeetCode [40] 组合总和Ⅱ
题目描述
给定一个数组 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] ]
算法思路
也是在[90]那道题上略微修改,运用剪枝操作,遇到子集构造过程中子集元素和已大于target目标时,提前回溯,大大提高算法效率!
算法代码
class Solution {
public:
vector
<vector
<int>> combinationSum2(vector
<int>& candidates
, int target
) {
vector
<vector
<int>> result
;
vector
<int> item
;
set
<vector
<int>> res_set
;
sort(candidates
.begin(),candidates
.end());
generate(0,candidates
,item
,result
,res_set
,0,target
);
return result
;
}
private:
void generate(int i
,vector
<int>& nums
,vector
<int> &item
,vector
<vector
<int>>& result
,set
<vector
<int>> &res_set
,int sum
,int target
)
{
if(i
>=nums
.size() || sum
>target
){
return;
}
sum
+= nums
[i
];
item
.push_back(nums
[i
]);
if(target
==sum
&& res_set
.find(item
) == res_set
.end()){
result
.push_back(item
);
res_set
.insert(item
);
}
generate(i
+1,nums
,item
,result
,res_set
,sum
,target
);
item
.pop_back();
sum
-= nums
[i
];
generate(i
+1,nums
,item
,result
,res_set
,sum
,target
);
}
};
LeetCode [20] 括号生成
题目描述
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例
输入:n = 3 输出:[ “((()))”, “(()())”, “(())()”, “()(())”, “()()()” ]
算法思路
递归,每次走两条路,一条路为添加左括号,一条路为添加右括号考虑上左右括号匹配,必须要有左括号才能有右括号,即每次左括号在n范围内可以直接加入,但右括号要判断一下右括号的个数必须小于左括号的个数才能加入。
算法代码
class Solution {
public:
vector
<string
> generateParenthesis(int n
) {
vector
<string
> result
;
generate("",n
,n
,result
);
return result
;
}
private:
void generate(string item
,int left
,int right
,vector
<string
> &result
){
if(left
==0 && right
==0){
result
.push_back(item
);
return ;
}
if(left
> 0){
generate(item
+'(',left
-1,right
,result
);
}
if(left
<right
){
generate(item
+')',left
,right
-1,result
);
}
}
};
LeetCode [51] N皇后
问题描述
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例
输入:4 输出:[ [".Q…", // 解法 1 “…Q”, “Q…”, “…Q.”],
["…Q.", // 解法 2 “Q…”, “…Q”, “.Q…”] ] 解释: 4 皇后问题存在两个不同的解法。
算法思路
先写一个函数用来计算当棋盘中(x,y)被放置皇后后,整个棋盘会剩下多少个可以放另外的皇后而不被攻击的格子,用一个矩阵表示,初始矩阵为 0,后计算出会被攻击的格子置为 1.利用回溯算法,每次考虑一行的皇后放置,按列遍历看哪个格子为安全格子,便进行放置,如果到某种情况下皇后没有格子可放了就回溯。直接上图吧
图片来自小象学院教程
算法代码
class Solution {
public:
vector
<vector
<string
>> solveNQueens(int n
) {
vector
<vector
<string
>> result
;
vector
<string
> location
;
vector
<vector
<int>> mark
;
for(int i
=0;i
<n
;i
++){
mark
.push_back((vector
<int>()));
for(int j
=0;j
<n
;j
++){
mark
[i
].push_back(0);
}
location
.push_back("");
location
[i
].append(n
,'.');
}
generate(0,n
,location
,result
,mark
);
return result
;
}
private:
void put_down_the_queen(int x
,int y
,vector
<vector
<int>> &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
<mark
.size();i
++){
for(int j
=0;j
<8;j
++){
int new_x
= x
+ i
*dx
[j
];
int new_y
= y
+ i
*dy
[j
];
if(new_x
>=0 && new_x
<mark
.size()
&& new_y
>=0 && new_y
<mark
.size()){
mark
[new_x
][new_y
] = 1;
}
}
}
}
void generate(int k
,int n
,vector
<string
> &location
,vector
<vector
<string
>> &result
,vector
<vector
<int>> &mark
){
if(k
==n
){
result
.push_back(location
);
return;
}
for(int i
=0;i
<n
;i
++){
if(mark
[k
][i
] == 0){
vector
<vector
<int>> tmp_mark
= mark
;
location
[k
][i
] = 'Q';
put_down_the_queen(k
,i
,mark
);
generate(k
+1,n
,location
,result
,mark
);
mark
= tmp_mark
;
location
[k
][i
] = '.';
}
}
}
};
LeetCode [315] 计算右侧小于当前元素的个数(求逆序数)
题目描述
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例
输入:nums = [5,2,6,1] 输出:[2,1,1,0] 解释: 5 的右侧有 2 个更小的元素 (2 和 1) 2 的右侧仅有 1 个更小的元素 (1) 6 的右侧有 1 个更小的元素 (1) 1 的右侧有 0 个更小的元素
算法思路
分治法的归并排序
这道题的重要思路是由归并排序扩展开的,先补充一下归并排序
#include<iostream>
#include<vector>
using namespace std
;
void merge_sort_two_vec(vector
<int>& sub_vec1
, vector
<int>& sub_vec2
, vector
<int>& vec
) {
int i
= 0;
int j
= 0;
while (i
< sub_vec1
.size() && j
< sub_vec2
.size())
{
if (sub_vec1
[i
] <= sub_vec2
[j
]) {
vec
.push_back(sub_vec1
[i
]);
i
++;
}
else
{
vec
.push_back(sub_vec2
[j
]);
j
++;
}
}
for (; i
< sub_vec1
.size(); i
++)
{
vec
.push_back(sub_vec1
[i
]);
}
for (; j
< sub_vec2
.size(); j
++)
{
vec
.push_back(sub_vec2
[j
]);
}
}
void merge_sort(vector
<int>& vec
) {
if (vec
.size() < 2) {
return;
}
int mid
= vec
.size() / 2;
vector
<int> sub_vec1
;
vector
<int> sub_vec2
;
for (int i
= 0; i
< mid
; i
++)
{
sub_vec1
.push_back(vec
[i
]);
}
for (int i
= mid
; i
< vec
.size(); i
++) {
sub_vec2
.push_back(vec
[i
]);
}
merge_sort(sub_vec1
);
merge_sort(sub_vec2
);
vec
.clear();
merge_sort_two_vec(sub_vec1
, sub_vec2
, vec
);
}
int main()
{
int nums
[] = { -3,9,4,10,23,5,11,-6,21,-7 };
vector
<int> vec
;
for (int i
= 0; i
< 10; i
++)
{
vec
.push_back(nums
[i
]);
}
merge_sort(vec
);
for (int i
= 0; i
< 10; i
++)
{
cout
<< vec
[i
] << " ";
}
return 0;
}
算法思路
每次有两个已经排序好的两个数组,按上面归并排序第一个函数(把两个已排序的数组有序合成)的算法加入新的数组,而其中第一个数组中的count即为j的值(i为第一个数组的索引,j为第二个数组的索引)。
算法代码
class Solution {
public:
vector
<int> countSmaller(vector
<int>& nums
) {
vector
<pair
<int, int>> vec
;
vector
<int> count
;
for (int i
= 0; i
< nums
.size(); i
++)
{
vec
.push_back(make_pair(nums
[i
], i
));
count
.push_back(0);
}
merge_sort(vec
, count
);
return count
;
}
private:
void merge_sort_two_vec(
vector
<pair
<int, int>>& sub_vec1
,
vector
<pair
<int, int>>& sub_vec2
,
vector
<pair
<int, int>>& vec
,
vector
<int>& count
) {
int i
= 0;
int j
= 0;
while (i
< sub_vec1
.size() && j
< sub_vec2
.size())
{
if (sub_vec1
[i
].first
<= sub_vec2
[j
].first
) {
count
[sub_vec1
[i
].second
] += j
;
vec
.push_back(sub_vec1
[i
]);
i
++;
}
else
{
vec
.push_back(sub_vec2
[j
]);
j
++;
}
}
for (; i
< sub_vec1
.size(); i
++)
{
count
[sub_vec1
[i
].second
] += j
;
vec
.push_back(sub_vec1
[i
]);
}
for (; j
< sub_vec2
.size(); j
++)
{
vec
.push_back(sub_vec2
[j
]);
}
}
void merge_sort(vector
<pair
<int, int>>& vec
, vector
<int>& count
) {
if (vec
.size() < 2) {
return;
}
int mid
= vec
.size() / 2;
vector
<pair
<int, int>> sub_vec1
;
vector
<pair
<int, int>> sub_vec2
;
for (int i
= 0; i
< mid
; i
++)
{
sub_vec1
.push_back(vec
[i
]);
}
for (int i
= mid
; i
< vec
.size(); i
++) {
sub_vec2
.push_back(vec
[i
]);
}
merge_sort(sub_vec1
,count
);
merge_sort(sub_vec2
, count
);
vec
.clear();
merge_sort_two_vec(sub_vec1
, sub_vec2
, vec
, count
);
}
};
ps: 小象学院教程 https://www.bilibili.com/video/BV1GW411Q77S?t=7029&p=2