给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字)。
示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {//该方法非常笨,虽然一开始我也觉得应该用动态规划,但死活没有思路,因为有个负数可能会出现最大值变成最小值的情况
/*整体思路:
1以0为分界点分为几个小块,取几个小块的乘最大值,(0)...A...0...B...(0)
2第一种小块:单独一个0,
3第二种小块:块内负数个数为偶数个,则返回范围全部乘积
4第三种小块:块内负数个数为奇数个,则返回max(从左乘到最后一个负数前,从右乘到最后一个负数前)
*/
public:
long long resMax=INT_MIN;
int maxProduct(vector<int>& nums) {
if(nums.size()==0)return 0;
if(nums.size()==1)return nums[0];
bool hasZero=false;
int minusCount=0,le=0;
for(int i=0;i<nums.size();++i){
if(nums[i]<0)++minusCount;
if(nums[i]==0){
hasZero=true;
helper(nums,le,i-1,minusCount);
le=i+1;
minusCount=0;
}
}
helper(nums,le,nums.size()-1,minusCount);
return resMax<0&&hasZero?0:resMax;//resMax存放第二第三种情况,hasZero判断比较第一种情况
}
void helper(vector<int> &nums,int le,int ri,int c){
if(ri<0||le>ri)return;
if(le==ri)
resMax=max(resMax,(long long)nums[le]);
long long pathMax=1;
if(c%2==0){//第二种情况
for(int i=le;i<=ri;++i)
pathMax*=nums[i];
resMax=max(resMax,pathMax);
}
else{//第三种情况
for(int i=le,j=0;;++i){
if(nums[i]<0)++j;
if(j==c)break;
pathMax*=nums[i];
resMax=max(resMax,pathMax);
}
pathMax=1;
for(int i=ri,j=0;;--i){
if(nums[i]<0)++j;
if(j==c)break;
pathMax*=nums[i];
resMax=max(resMax,pathMax);
}
}
}
};
//动态规划:
dpmax=max(dpmax*nums[i],nums[i]);
dpmin=max(dpmin*nums[i],nums[i]);
给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。
示例 1:
输入:
11110
11010
11000
00000
输出: 1
示例 2:
输入:
11000
11000
00100
00011
输出: 3
class Solution {//直觉 深度优先遍历
public:
int resCount=0;
vector<vector<char>> grid;
int numIslands(vector<vector<char>>& grid) {
if(grid.size()==0)return 0;
this->grid=grid;
vector<vector<bool>> covered(grid.size(),vector<bool>(grid[0].size(),false));//可以用grid[i][j]=‘0’;的方法表示已遍历过
for(int i=0;i<grid.size();++i)
for(int j=0;j<grid[0].size();++j)
if(!covered[i][j]&&grid[i][j]=='1'){
helper(covered,i,j);
++resCount;
}
return resCount;
}
void helper(vector<vector<bool>> &covered,int i,int j){
if(i<0||i>=grid.size()||j<0||j>=grid[0].size()||covered[i][j]||grid[i][j]=='0')return;
covered[i][j]=true;
helper(covered,i+1,j);
helper(covered,i-1,j);
helper(covered,i,j+1);
helper(covered,i,j-1);
}
};
//方法二:广度优先遍历,队列
什么是并查集(Disjoint-set)
对于一个集合S={a1, a2, ..., an-1, an},我们还可以对集合S进一步划分: S1,S2,...,Sm-1,Sm,我们希望能够快速确定S中的两两元素是否属于S的同一子集。
举个栗子,S={0,1, 2, 3, 4, 5, 6},如果我们按照一定的规则对集合S进行划分,假设划分后为S1={1, 2, 4}, S2={3, 6},S3={0, 5},任意给定两个元素,我们如何确定它们是否属于同一子集?某些合并子集后,又如何确定两两关系?基于此类问题便出现了并查集这种数据结构。
并查集有两个基本操作:
- Find: 查找元素所属子集
- Union:合并两个子集为一个新的集合
并查集(初始化+find+union)
class UnionFind {
//private:
vector<int> parent;//存放 目标数组 对应元素所属集合的下标/父节点下标
vector<int> rank;//秩/数高度 合并优化措施
int count; //连通个数
//根据需要设置其他参数
public:
UnionFind(vector<vector<char>>& grid) {//初始化 目标/操作数组 一维或者二维
count = 0;
int m = grid.size();
int n = grid[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
//初始化start
//一般一维数组:parend.push_back(i),++count,rand.push_back(0);
//一般二维数组:parend.push_back(i * n + j),++count,rand.push_back(0);二维映射到一维i*n+j
//一般情况下,传入参数为对应数组的总元素个数即可
if (grid[i][j] == '1') {//目标数组中实际要合并的可能元素,一般是全体元素无需判断
parent.push_back(i * n + j);
++count;
}
else parent.push_back(-1);//
rank.push_back(0);
//初始化end
}
}
}
int find(int x) { //路径压缩优化,找到则直接指向最终下标/父节点下标
return x == parent[x] ? x : (parent[x] = find(parent[x]));
}
void Union(int x, int y) { // 按秩合并优化
int rootx = find(x);
int rooty = find(y);
if (rootx != rooty) {
if (rank[rootx] > rank[rooty])
parent[rooty] = rootx;
else if (rank[rootx] < rank[rooty])
parent[rootx] = rooty;
else {
parent[rooty] = rootx; //parent[rootx] = rooty,++rank[rooty];
++rank[rootx];
}
--count;
}
}
int getCount() const {
return count;
}
};
//官方并查集答案
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int nr = grid.size();
if (!nr) return 0;
int nc = grid[0].size();
UnionFind uf (grid);//初始化
int num_islands = 0;
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';
//满足条件则合并
if (r - 1 >= 0 && grid[r-1][c] == '1') uf.Union(r * nc + c, (r-1) * nc + c);
if (r + 1 < nr && grid[r+1][c] == '1') uf.Union(r * nc + c, (r+1) * nc + c);
if (c - 1 >= 0 && grid[r][c-1] == '1') uf.Union(r * nc + c, r * nc + c - 1);
if (c + 1 < nc && grid[r][c+1] == '1') uf.Union(r * nc + c, r * nc + c + 1);
}
}
}
return uf.getCount();
}
};
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
提示:
输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5
class Solution {
public:
vector<vector<int>> v;
unordered_map<int,vector<int>> m;
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
if(prerequisites.size()==0||numCourses<=1)return true;
vector<int> flag(numCourses,1);//0表示有先决课程,1表示没有先决课程,2表示这个课程是当前正在dfs判断的课程
this->v=prerequisites;
for(auto i:v){
m[i[0]].push_back(i[1]);
flag[i[0]]=0;
}
for(auto i:m)
if(!helper(i.first,flag))return false;
return true;
}
bool helper(int mi,vector<int> &flag){
if(flag[mi]==1)return true;
if(flag[mi]==2)return false;
flag[mi]=2;
for(auto i:m[mi])
if(!helper(i,flag))
return false;
flag[mi]=1;
return true;
}
};
拓扑排序(一维存入度,二维存出度,队列遍历0入度节点)
在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。
先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。
一直做改操作,直到所有的节点都被分离出来。
如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> indegree(numCourses,0);//入度
vector<vector<int>> graph(numCourses);//存的是出边
for (int i = 0; i < prerequisites.size(); i++)
{
indegree[prerequisites[i][0]]++;
graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
}
//将入度为0的顶点入队
queue<int> myqueue;
for (int i = 0; i < numCourses; i++)
if (indegree[i] == 0)
myqueue.push(i);
int cnt = 0;//vector<int> res;拓扑排序顺序存储
while (!myqueue.empty())
{
int temp = myqueue.front();
myqueue.pop();
cnt++;//res.push_back(temp);
for (int i = 0; i < graph[temp].size(); i++)
{
indegree[graph[temp][i]]--;
if (indegree[graph[temp][i]] == 0)
myqueue.push(graph[temp][i]);
}
}
return cnt == numCourses;//return res.size()==numCourses
}
};