698. 划分为k个相等的子集
给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
示例 1:
输入: nums = [4, 3, 2, 3, 5, 2, 1], k = 4
输出: True
说明: 有可能将其分成 4 个子集(5),(1,4),(2,3),(2,3)等于总和。
1 <= k <= len(nums) <= 16
0 < nums[i] < 10000
使用dfs 注意剪枝 优化,避免访问不必要的节点
class Solution {
public:
bool dfs(vector<int>& nums,vector<bool>& flag,int subsum,int target,int k,int cnt,int start){
if(cnt == k) return true;
if(subsum == target) {
return dfs(nums,flag,0,target,k,cnt+1,0);
}
if(subsum < target){
for(int i =start;i<nums.size();i++) {
if (flag[i] == false) {
flag[i] = true;
if(dfs(nums, flag, subsum + nums[i], target, k, cnt, i + 1))
return true;
flag[i] = false;
}
}
}
return false;
}
bool canPartitionKSubsets(vector<int>& nums, int k) {
int sum = 0;
for(auto i:nums)
sum+=i;
if(sum%k != 0) return false;
int target = sum/k;
// 从大到小排序,可以减少很多不必要的组合,因为全是正数
sort(nums.begin(), nums.end(), greater<int>());
if(nums[0] > target) return false;
vector<bool> flag(nums.size(), false);
return dfs(nums,flag,0,target,k,0,0);
}
};
/* boolean help(int[] nums, int cur, int[] arr, int k){ //已经遍历到了-1说明前面的所有数都正好可以放入桶里,那所有桶的值此时都为0,说明找到了结果,返回true if(cur < 0){ return true; } //遍历k个桶 for(int i = 0; i < k; i++){ //如果正好能放下当前的数或者放下当前的数后,还有机会继续放前面的数(剪枝) if(arr[i] == nums[cur] || (cur > 0 && arr[i] - nums[cur] >= nums[0])){ //放当前的数到桶i里 arr[i] -= nums[cur]; //开始放下一个数 if(help(nums, cur - 1, arr, k)){ return true; } //这个数不该放在桶i中 //从桶中拿回当前的数 arr[i] += nums[cur]; } } return false; } */
130. 被围绕的区域
给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:
X X X X
X X X X
X X X X
X O X X
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
所以只需要利用BFS找到所有与边界O相连的O即可,没有与边界O相连的全部置为X
class Solution {
public:
typedef pair<int,int> P;
void bfs(vector<vector<char>>& grid,int x,int y,int rows,int cols){
queue<P> mq;
mq.push(P(x,y));
int dx[]={-1,0,1,0};
int dy[]={0,1,0,-1};
while(!mq.empty()){
x = mq.front().first;
y = mq.front().second;
mq.pop();
for(int i=0;i<4;i++){
int newx = x+dx[i];
int newy = y+dy[i];
if(newx>=0 && newx<rows && newy>=0 && newy<cols && grid[newx][newy] == 'O'){
grid[newx][newy] = '-';
mq.push(P(newx,newy));
}
}
}
}
void solve(vector<vector<char>>& board) {
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if((i==0 || i==board.size()-1 || j==0 || j==board[0].size()-1) && board[i][j]=='O'){
bfs(board,i,j,board.size(),board[0].size());
board[i][j] = '-';
}
}
}
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(board[i][j] == 'O')
board[i][j] = 'X';
else if(board[i][j] == '-')
board[i][j] = 'O';
}
}
}
};
137 只出现一次的数字II
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
首先每一个整数都可以用32位的二进制进行表示,然后统计每个位上1的个数,然后找对k取余的位即可,k==3,然后将所有的1位组合起来即可,使用一个32位的数组存储对应位的1/0;
假如例子是 1 2 6 1 1 2 2 3 3 3, 3 个 1, 3 个 2, 3 个 3,1 个 6
1 0 0 1
2 0 1 0
6 1 1 0
1 0 0 1
1 0 0 1
2 0 1 0
2 0 1 0
3 0 1 1
3 0 1 1
3 0 1 1
看最右边的一列 1001100111 有 6 个 1
再往前看一列 0110011111 有 7 个 1
再往前看一列 0010000 有 1 个 1
我们只需要把是 3 的倍数的对应列写 0,不是 3 的倍数的对应列写 1
也就是 1 1 0,也就是 6。
class Solution {
public:
int singleNumber(vector<int>& nums) {
vector<int> bits(32,0);
int ans = 0;
for (int i = 0; i < 32; i++) {
for (int j = 0; j < nums.size(); j++)
bits[i] += (nums[j]>>i) & 1;
ans |= (bits[i] % 3) << i;
}
return ans;
}
};
473
class Solution {
public:
bool makesquare(vector<int>& nums) {
int sum = 0;
if(nums.size()<4) return false;
for(auto i:nums)
sum+=i;
if(sum%4!=0) return false;
int target = sum/4;
sort(nums.begin(),nums.end(),greater<int>());
if(nums[0] > target) return false;
vector<bool> flag(nums.size(),false);
vector<int> sums(4,0);
return dfs2(nums,sums,target,0);
// return dfs(nums,flag,target,0,0,0);
}
bool dfs2(vector<int>& nums,vector<int>& sums,int target,int start){
// 搜索完了即火柴全部放入桶中,成功
if(start == nums.size()) return true;
// 尝试把火柴放入4个桶中
for(int j=0;j<4;j++){
if(sums[j] + nums[start] <= target){
// 这里有一个非常重要的剪枝过程,即前一个桶和当前桶火柴数一致时,可以直接跳过,因为对于1根火柴来讲,两个桶如果当前大小一样,再放入时是没有区别的,而前面那个桶放入失败,则如果重新放入也肯定失败,故可以直接跳过,实测效率可以提高几十倍
if(j==0 || sums[j] != sums[j-1]){
//在当前桶放入火柴 并开始下一轮递归 开始放入下一根火柴
sums[j] += nums[start];
if(dfs2(nums,sums,target,start+1))
return true;
// 放入失败,取出火彩并尝试下一个桶
sums[j] -= nums[start];
}
}
}
// 四个桶都不能放火柴时既需要回溯,体现在上述过程中的取出操作
return false;
}
bool dfs(vector<int>& nums,vector<bool>& flag,int target,int start,int subsum,int cnt){
if(cnt==4) return true;
if(subsum == target) return dfs(nums,flag,target,0,0,cnt+1);
if(subsum<target){
for(int i=start;i<nums.size();i++){
if(flag[i] == false)
{
flag[i] = true;
if(dfs(nums,flag,target,i+1,subsum+nums[i],cnt))
return true;
flag[i] = false;
}
}
}
return false;
}
};
class Solution {
public:
vector<vector<int>> res;
set<vector<int>> ans;
vector<vector<int>> findSubsequences(vector<int>& nums) {
if(nums.size() <= 1) return res;
vector<int> tmp;
vector<bool> used(nums.size(),false);
// sort(nums.begin(),nums.end());
dfs(nums,used,tmp,0);
return vector<vector<int>>(ans.begin(),ans.end());
}
void dfs(vector<int>& nums,vector<bool>& used,vector<int>& tmp,int start){
if(tmp.size()>=2){
ans.insert(tmp);
// res.push_back(tmp);
}
if(start == nums.size()) return;
for(int i=start;i<nums.size();i++){
if((tmp.empty() || tmp.back()<=nums[i])){
// if(used[i] == false && (tmp.empty() || tmp.back()<=nums[i])){
// used[i] = true;
tmp.push_back(nums[i]);
dfs(nums,used,tmp,i+1);
tmp.pop_back();
// used[i] = false;
}
}
}
};
515. 在每个树行中找最大值
您需要在二叉树的每一行中找到最大的值。
广度优先遍历BFS 深度优先遍历DFS
class Solution {
public:
// 广度优先遍历 借用队列实现
vector<int> largestValues(TreeNode* root) {
vector<int> res;
if(!root) return res;
queue<TreeNode*> mq;
mq.push(root);
while(!mq.empty()){
int size = mq.size();
int max_val = mq.front()->val;
for(int i=0;i<size;i++){
TreeNode* cur = mq.front();
mq.pop();
max_val = max(cur->val,max_val);
if(cur->left) mq.push(cur->left);
if(cur->right) mq.push(cur->right);
}
res.push_back(max_val);
}
return res;
}
// 深度优先遍历, 递归实现 递归时传递当前节点的深度,即可对于树的每一层进行操作
vector<int> res;
void dfs(TreeNode* root,int dep){
if(dep == res.size())
res.push_back(root->val);
else
res[dep] = max(root->val,res[dep]);
if(root->left) dfs(root->left,dep+1);
if(root->right) dfs(root->right,dep+1);
}
vector<int> largestValues(TreeNode* root) {
if(!root) return res;
dfs(root,0);
return res;
}
};
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
输入:
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 isOk(TreeNode* root,long long min , long long max){
if(!root) return true;
if(root->val <= min || root->val >= max) return false;
return isOk(root->left,min,root->val) && isOk(root->right,root->val,max);
}
bool isValidBST(TreeNode* root) {
return isOk(root,LONG_LONG_MIN,LONG_LONG_MAX);
}
// 中序遍历,左根右,确保中序遍历为升序 既满足二叉搜索树
TreeNode* pre = nullptr;
bool isValidBST(TreeNode* root) {
if(!root) return true;
if(!isValidBST(root->left)) return false;
if(pre && pre->val >= root->val) return false;
pre = root;
if(!isValidBST(root->right)) return false;
return true;
}
};
109. 有序链表转换二叉搜索树
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定的有序链表: [-10, -3, 0, 5, 9],
一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
// 递归构建 快慢指针找链表中点,即为搜索树的根节点
TreeNode* sortedListToBST(ListNode* head) {
if (head == nullptr) return nullptr;
if (head->next == nullptr) return new TreeNode(head->val);
ListNode* pre=head, *slow=head, *fast=head;
while(fast && fast->next){
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = nullptr;
TreeNode* root = new TreeNode(slow->val);
root->left = sortedListToBST(head);
root->right = sortedListToBST(slow->next);
return root;
}