开宗明义:本系列基于小象学院林沐老师课程《面试算法 LeetCode 刷题班》,刷题小白,旨在理解和交流,重在记录,望各位大牛指点!
Leetcode学习之深度优先搜索与宽度优先搜索(2)
1、火柴棍摆正方形 LeetCode 200.
题目来源:
L
e
e
t
C
o
d
e
473.
M
a
t
c
h
s
t
i
c
k
s
t
o
S
q
u
a
r
e
LeetCode \ 473. \ Matchsticks \ to \ Square
LeetCode 473. Matchsticks to Square
描述:已知一个数组,保存了n个火柴棍(n<=15),问可否使用这n个火柴棍摆成1个正方形(这边是n个全农)?
思考:
1.1 无优化的深度搜索
明显时间成本太高,pass!
1.2 优化与剪枝
分析:对上述暴力搜索添加约束就是优化:
测试代码:
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;
class Solution {
public:
bool makesquare(vector<int>& nums) {
if (nums.size() < 4) { //数量小于4的时候返回假
return false;
}
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum = sum + nums[i];
}
if (sum % 4) { //n个火柴杆的总和对4取余为0,否则返回假
return false;
}
std::sort(nums.rbegin(), nums.rend()); //排序
int bucket[4] = { 0 }; //vector<int>
return generate(0, nums, sum / 4, bucket);
}
private:
bool generate(int i, vector<int>& nums, int target, int bucket[]) { //target 1/4的sum和
if (i >= nums.size()) {
return bucket[0] == target&&bucket[1] == target&&bucket[2] == target
&&bucket[3] == target;
}
for (int j = 0; j < 4; j++) {
if (bucket[j] + nums[i] > target) { //4个桶中分别尝试
continue;
}
bucket[j] += nums[i]; //放在j桶中 <=
if (generate(i + 1, nums, target, bucket)) {
return true;
}
bucket[j] = bucket[j] - nums[i];
}
return false;
}
};
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(1);
nums.push_back(2);
nums.push_back(4);
nums.push_back(3);
nums.push_back(2);
nums.push_back(3);
Solution solve;
printf("%d\n", solve.makesquare(nums));
system("pause");
return 0;
}
效果图:
1.3 位运算法
描述:
- 使用位运算法,构造出所有和为 t a r g e t target target (总和/4)的子集,存储在向量 o k − s u b s e t ok-subset ok−subset 中,这些是候选的边组合。
- 遍历所有的 o k − s u b s e t ok-subset ok−subset,两两进行对比,如果 o k − s e t [ i ] ok-set[i] ok−set[i] 和 o k − s e t [ j ] ok-set[j] ok−set[j] 进行与运算的结果为0,则说明 o k − s e t [ i ] ok-set[i] ok−set[i] 和 o k − s e t [ j ] ok-set[j] ok−set[j] 表示的是无交集的两个集合(没有选择同样的火柴棍),这两个集合可以代表两个可以同时存在的满足的条件的边;将 o k − s e t [ i ] ok-set[i] ok−set[i] 与 o k − s e t [ j ] ok-set[j] ok−set[j] 求或,结果存储在 o k − h a l f ok-half ok−half 中,它代表所有满足一半结果的情况。
- 遍历所有的
o
k
−
h
a
l
f
ok-half
ok−half ,两两进行对比,如果
o
k
−
h
a
l
f
[
i
]
ok-half[i]
ok−half[i] 和
o
k
−
h
a
l
f
[
j
]
ok-half[j]
ok−half[j] 进行与运算的结果为0,则返回true(说明有4个满足条件的边,即可组成正方形);否则返回
f
a
l
s
e
false
false。
测试代码:
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;
class Solution {
public:
bool makesquare(vector<int>& nums) {
if (nums.size() < 4) { //nums.size()<4 返回假
return false;
}
int sum = 0;
for (int i = 0; i < nums.size(); i++) {
sum = sum + nums[i];
}
if (sum % 4) { //总和不是4的倍数,肯定不行
return false;
}
int target = sum / 4; //目标为:总和的四分之一
vector<int> ok_subset; //所有满足条件的一个边代表的集合,这边很重要
vector<int> ok_half; //所有满足条件的两个边代表的集合。同样很重要
int all = 1 << nums.size(); //左移一位,就是乘以2
//构造出所有和为 sum/4 的子集,存储在sub_set中
for (int i = 0; i < all; i++) {
int sum = 0;
for (int j = 0; j < nums.size(); j++) {
if (i&(1 << j)) {
sum = sum + nums[j];
}
}
if (sum == target) {
ok_subset.push_back(i);
}
}
for (int i = 0; i < ok_subset.size(); i++) {
for (int j = i + 1; j < ok_subset.size(); j++) {
if ((ok_subset[i] & ok_subset[j]) == 0) {
ok_half.push_back(ok_subset[i] | ok_subset[j]);
}
}
}
for (int i = 0; i < ok_half.size(); i++) {
for (int j = i + 1; j < ok_half.size(); j++) {
if ((ok_half[i] & ok_half[j]) == 0) {
return true;
}
}
}
return false;
}
};
int main() {
vector<int> nums;
nums.push_back(1);
nums.push_back(1);
nums.push_back(2);
nums.push_back(4);
nums.push_back(3);
nums.push_back(2);
nums.push_back(3);
Solution solve;
printf("%d\n", solve.makesquare(nums));
system("pause");
return 0;
}
2、收集雨水2 LeetCode 407.
题目来源:
L
e
e
t
C
o
d
e
407.
T
r
a
p
p
i
n
g
R
a
i
n
W
a
t
e
r
I
I
LeetCode \ 407. \ Trapping \ Rain \ Water \ II
LeetCode 407. Trapping Rain Water II
描述:已知一个
m
∗
n
m*n
m∗n 的二维数组,数组存储正整数,代表一个个单元的高度(立方体),将这些立方体想象成水槽,问如果下雨这些立方体中会有多少积水。
分析:
算法思路:
具体分析:
测试代码:
#include <stdio.h>
#include <queue>
using namespace std;
struct Qitem {
int x;
int y;
int h;
Qitem(int x, int y, int h) :x(x), y(y), h(h) {}
};
struct cmp {
bool operator()(const Qitem &a, const Qitem &b) {
return a.h > b.h;
}
};
class Solution {
public:
int trapRainWater(vector<vector<int>>& heightMap) { //这个heightMap 是二维数组
priority_queue<Qitem, vector<Qitem>, cmp> Q; //设置优先级队列Q,最小堆
if (heightMap.size() < 3 || heightMap[0].size() < 3) {
return 0; //行数或列数小于3,无法积水
}
int row = heightMap.size(); //行
int column = heightMap[0].size(); //列
vector<vector<int>> mark; //标记数组mark
for (int i = 0; i < row; i++) {
mark.push_back(vector<int>());
for (int j = 0; j < column; j++) {
mark[i].push_back(0);
}
} //将二维标记数组都置0
for (int i = 0; i < row; i++) {
Q.push(Qitem(i, 0, heightMap[i][0]));
mark[i][0] = 1;
Q.push(Qitem(i, column - 1, heightMap[i][column - 1]));
mark[i][column - 1] = 1;
}
for (int i = 1; i < column - 1; i++) { //列
Q.push(Qitem(0, i, heightMap[0][i])); //将四周的点添加至优先级队列Q,并做标记mark。这个添加优先级队列里面自己会处理
mark[0][i] = 1;
Q.push(Qitem(row - 1, i, heightMap[row - 1][i]));
mark[row - 1][i] = 1;
} //最上面一行和最下面一行
static const int dx[] = { -1,1,0,0 };
static const int dy[] = { 0,0,-1,1 }; //方向数组
int result = 0; //最终积水量
while (!Q.empty()){ //搜索的点
int x = Q.top().x; //取队列头部信息
int y = Q.top().y;
int h = Q.top().h;
Q.pop();
for (int i = 0; i < 4; i++) {
int newx = x + dx[i];
int newy = y + dy[i];
if (newx < 0 || newx >= row ||
newy < 0 || newy >= column || mark[newx][newy]) {
continue;
}
if (h > heightMap[newx][newy]) { //当新拓展的点超出边界或者已加入队列
result = result + h - heightMap[newx][newy];
heightMap[newx][newy] = h;
}
Q.push(Qitem(newx, newy, heightMap[newx][newy]));
mark[newx][newy] = 1; //标记已经搜索好了
}
}
return result;
}
};
int main() {
int test[][10] = {
{1,4,3,1,3,2},
{3,2,1,3,2,4},
{2,3,3,2,3,1}
};
vector<vector<int>> heightMap;
for (int i = 0; i < 3; i++) {
heightMap.push_back(vector<int>());
for (int j = 0; j < 6; j++) {
heightMap[i].push_back(test[i][j]);
}
}
Solution solve;
printf("%d\n", solve.trapRainWater(heightMap));
system("pause");
return 0;
}
效果图:
2.1 结构体的STL优先级队列
注意:里面最小堆的用法。
测试代码:
#include <stdio.h>
#include <queue>
using namespace std;
struct Qitem {
int x;
int y;
int h;
Qitem(int x, int y, int h) :x(x), y(y), h(h) {}
};
//判断两个值的高度哪个大
struct cmp {
bool operator()(const Qitem &a, const Qitem &b) {
return a.h > b.h;
}
};
int main() {
priority_queue<Qitem, vector<Qitem>, cmp> Q; //优先输出小数据。这边注意
Q.push(Qitem(0, 0, 5)); //导入元素
Q.push(Qitem(1, 3, 2));
Q.push(Qitem(5, 2, 4));
Q.push(Qitem(0, 1, 8));
Q.push(Qitem(6, 7, 1));
while (!Q.empty()) {
int x = Q.top().x;
int y = Q.top().y;
int h = Q.top().h;
printf("x = %d, y = %d, h = %d\n", x, y, h);
Q.pop();
}
system("pause");
return 0;
}
效果图: