Top100(中)
Top100(中)
二叉树
94. 二叉树的中序遍历
int *res;
void inorder(struct TreeNode *root, int *returnSize) {
if (root == NULL) return;
// 左根右
inorder(root->left, returnSize);
res[(*returnSize)++] = root->val;
inorder(root->right, returnSize);
}
int *inorderTraversal(struct TreeNode *root, int *returnSize) {
res = (int *) malloc(sizeof(int) * 100);
*returnSize = 0;
inorder(root, returnSize);
return res;
}
int *inorderTraversal(struct TreeNode *root, int *returnSize) {
int *res = (int *) malloc(sizeof(int) * 100);
*returnSize = 0;
struct TreeNode *stack[100];
int top = 0;
while (top != 0 || root != NULL) {
// 左子树入栈
while (root != NULL) {
stack[top++] = root;
root = root->left;
}
root = stack[--top];
// 访问
res[(*returnSize)++] = root->val;
root = root->right;
}
return res;
}
int *res;
void inorderMorris(struct TreeNode *root, int *returnSize) {
if (root == NULL) return;
struct TreeNode *cur = root;
while (cur != NULL) {
if (cur->left != NULL) {
struct TreeNode *rightMost = cur->left;
while (rightMost->right != NULL && rightMost->right != cur) {
rightMost = rightMost->right;
}
if (rightMost->right == NULL) {
rightMost->right = cur;
cur = cur->left;
} else {
// 有左右孩子的节点第二次被经过,左子树都遍历完了,访问节点
res[(*returnSize)++] = cur->val;
rightMost->right = NULL;
cur = cur->right;
}
} else {
// 只有右孩子的节点只会被经过一次,直接访问
res[(*returnSize)++] = cur->val;
cur = cur->right;
}
}
}
int *inorderTraversal(struct TreeNode *root, int *returnSize) {
res = (int *) malloc(sizeof(int) * 100);
*returnSize = 0;
if (root == NULL) return res;
inorderMorris(root, returnSize);
return res;
}
104. 二叉树的最大深度
// 递归
int maxDepth(struct TreeNode* root) {
if (root == NULL) return 0;
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return (left > right ? left : right) + 1;
}
// 层序遍历
int maxDepth(struct TreeNode *root) {
if (root == NULL) return 0;
int depth = 0;
const int size = 5002;
// 循环队列
struct TreeNode *queue[size];
int front = 0, rear = 0;
queue[rear++] = root;
while (front != rear) {
int count = (rear - front + size) % size;
// 一层加一次
depth++;
while (count-- > 0) {
struct TreeNode *node = queue[(front++) % size];
if (node->left != NULL) queue[(rear++) % size] = node->left;
if (node->right != NULL) queue[(rear++) % size] = node->right;
}
}
return depth;
}
226. 翻转二叉树
struct TreeNode *invertTree(struct TreeNode *root) {
if (root == NULL) return root;
struct TreeNode *left = invertTree(root->right);
struct TreeNode *right = invertTree(root->left);
root->left = left;
root->right = right;
return root;
}
101. 对称二叉树
// 递归
bool dfs(struct TreeNode *L, struct TreeNode *R) {
if (L == NULL && R == NULL) return true;
if (L == NULL || R == NULL || L->val != R->val) return false;
return dfs(L->left, R->right) && dfs(L->right, R->left);
}
bool isSymmetric(struct TreeNode *root) {
if (root == NULL) return true;
return dfs(root->left, root->right);
}
// 迭代
bool isSymmetric(struct TreeNode *root) {
if (root == NULL) return true;
if (root->left == NULL && root->right == NULL) return true;
if (root->left == NULL || root->right == NULL || root->left->val != root->right->val) return false;
const int size = 1001;
struct TreeNode *queue[size];
int front = 0, rear = 0;
// 左右孩子入队
queue[rear++] = root->left;
queue[rear++] = root->right;
while (rear != front) {
struct TreeNode *L = queue[(front++) % size];
struct TreeNode *R = queue[(front++) % size];
if (L == NULL && R == NULL) return true;
if ((L == NULL || R == NULL)
|| (L->val != R->val)
|| (L->left == NULL && R->right != NULL)
|| (L->right == NULL && R->left != NULL)
|| (L->right == NULL && R->left != NULL)
|| (L->left == NULL && R->right != NULL))
return false;
if (L->left != NULL) {
queue[(rear++) % size] = L->left;
queue[(rear++) % size] = R->right;
}
if (L->right != NULL) {
queue[(rear++) % size] = L->right;
queue[(rear++) % size] = R->left;
}
}
return true;
}
543. 二叉树的直径
int res;
// 求树高的同时记录最远距离
int height(struct TreeNode *root) {
if (root == NULL)return 0;
int left = height(root->left);
int right = height(root->right);
if (left + right > res) res = left + right;
return (left > right ? left : right) + 1;
}
int diameterOfBinaryTree(struct TreeNode *root) {
res = 0;
height(root);
return res;
}
102. 二叉树的层序遍历
int **levelOrder(struct TreeNode *root, int *returnSize, int **returnColumnSizes) {
// 一层最多元素个数
const int size = 1002;
// 最多层数
const int leverMax = 2000;
// 返回的二维数组,第一维表示所在层,第二维表示该层的所有元素
int **res = (int **) malloc(sizeof(int *) * leverMax);
// 一维的维度(多少层)
*returnSize = 0;
// 每个二维的维度(每层多少元素)
*returnColumnSizes = (int *) malloc(sizeof(int) * leverMax);
if (root == NULL) return res;
// 循环队列
struct TreeNode *queue[size];
int lever = 0;
// 保存每层元素个数,下标就是所在层,从0开始
int *columnSize = (int *) calloc(leverMax, sizeof(int));
int front = 0, rear = 0;
queue[rear++] = root;
while (front != rear) {
// 当前层元素数
int count = (rear - front + size) % size;
res[lever] = (int *) malloc(sizeof(int) * count);
int temp = 0;
while (count-- > 0) {
root = queue[(front++) % size];
// 记录当前层的元素
res[lever][temp++] = root->val;
// 当前层元素总数加一
columnSize[lever]++;
if (root->left != NULL) queue[(rear++) % size] = root->left;
if (root->right != NULL) queue[(rear++) % size] = root->right;
}
// 加一层
lever++;
}
*returnSize = lever;
for (int i = 0; i < lever; ++i)
(*returnColumnSizes)[i] = columnSize[i];
return res;
}
108. 将有序数组转换为二叉搜索树
// 递归生成
struct TreeNode *generate(int *nums, int left, int right) {
if (left > right) return NULL;
// 向下取整的中间元素
int mid = (right - left) / 2 + left;
struct TreeNode *node = (struct TreeNode *) malloc(sizeof(struct TreeNode));
node->val = nums[mid];
node->left = generate(nums, left, mid - 1);
node->right = generate(nums, mid + 1, right);
return node;
}
98. 验证二叉搜索树
// 中序遍历
bool inorder(struct TreeNode *root) {
if (root == NULL) return true;
// 左
if (!inorder(root->left)) return false;
// 根
if (pre != NULL && pre->val >= root->val) return false;
pre = root;
// 右
return inorder(root->right);
}
bool isValidBST(struct TreeNode *root) {
pre = NULL;
return inorder(root);
}
// 判断子树是否在min到max的开区间内
bool dfs(struct TreeNode *root, long long min, long long max) {
if (root == NULL) return true;
if (root->val <= min || root->val >= max)return false;
return dfs(root->left, min, root->val) && dfs(root->right, root->val, max);
}
bool isValidBST(struct TreeNode *root) {
return dfs(root, 0x8000000000000000, 0x7fffffffffffffff);
}
230. 二叉搜索树中第K小的元素
int res;
int count;
// 右根左,倒着中序遍历
// 倒数第k个变成找正数第k个
void inorder(struct TreeNode *root) {
if (root == NULL || res != -1) return;
inorder(root->left);
count--;
if (count == 0) {
res = root->val;
return;
}
inorder(root->right);
}
int kthSmallest(struct TreeNode *root, int k) {
res = -1;
count = k;
inorder(root);
return res;
}
199. 二叉树的右视图
int *rightSideView(struct TreeNode *root, int *returnSize) {
*returnSize = 0;
if (root == NULL) return NULL;
int *res = (int *) malloc(sizeof(int) * 100);
const int size = 52;
struct TreeNode **queue = (struct TreeNode **) malloc(sizeof(struct TreeNode *) * size);
int front = 0, rear = 0;
queue[rear++] = root;
// 层序遍历找一层的最后一个节点
while (rear != front) {
int count = (rear - front + size) % size;
struct TreeNode *node;
while (count-- > 0) {
node = queue[(front++) % size];
if (node->left != NULL) queue[(rear++) % size] = node->left;
if (node->right != NULL) queue[(rear++) % size] = node->right;
}
res[(*returnSize)++] = node->val;
}
return res;
}
int *res;
int size;
void dfs(struct TreeNode *root, int depth, int *returnSize) {
if (root == NULL) return;
// 根
if (depth == size) {
// 如果是新的一层的第一个节点,就加入到结果中
// 由于是根右左顺序,新一层的第一个节点一定是该层最右边的节点
res[(*returnSize)++] = root->val;
size++;
}
// 右
dfs(root->right, depth + 1, returnSize);
// 左
dfs(root->left, depth + 1, returnSize);
}
int *rightSideView(struct TreeNode *root, int *returnSize) {
*returnSize = 0;
if (root == NULL) return NULL;
res = (int *) malloc(sizeof(int) * 100);
size = 0;
dfs(root, 0, returnSize);
return res;
}
114. 二叉树展开为链表
// 保存左右子树到栈中,再修改左右指针
void flatten(struct TreeNode *root) {
if (root == NULL) return;
struct TreeNode *stack[2000];
int top = 0;
struct TreeNode *temp, *pre = NULL;
stack[top++] = root;
while (top != 0) {
root = stack[--top];
temp = root;
// 先压右,后压左
if (root->right != NULL) stack[top++] = root->right;
if (root->left != NULL) stack[top++] = root->left;
temp->left = NULL;
if (pre != NULL) pre->right = temp;
pre = temp;
}
}
// todo
// 神似morris
void flatten(struct TreeNode *root) {
while (root != NULL) {
if (root->left != NULL) {
struct TreeNode *rightMost = root->left;
while (rightMost->right != NULL)
rightMost = rightMost->right;
// 把右子树接到左子树的最右边的节点上
rightMost->right = root->right;
// 把追加过的左子树移到右子树的位置
// 下一步访问的其实还是左节点,保证了先序
root->right = root->left;
root->left = NULL;
}
root = root->right;
}
}
struct TreeNode *pre;
// 先序序列倒过来访问的递归写法(后序遍历递归写法的改写)
void dfs(struct TreeNode *root) {
if (root == NULL) return;
dfs(root->right);
dfs(root->left);
root->left = NULL;
root->right = pre;
pre = root;
}
void flatten(struct TreeNode *root) {
pre = NULL;
dfs(root);
}
// 先序序列倒过来访问的迭代写法(后序遍历迭代写法的改写)
void flatten(struct TreeNode *root) {
if (root == NULL) return;
struct TreeNode *stack[2000];
int top = 0;
struct TreeNode *pre = NULL;
while (top != 0 || root != NULL) {
while (root != NULL) {
stack[top++] = root;
root = root->right;
}
root = stack[--top];
if (root->left != NULL && pre != root->left) {
// 左子树不空且未被访问过
stack[top++] = root;
root = root->left;
} else {
// 左子树已经访问,可以处理当前节点
root->left = NULL;
root->right = pre;
pre = root;
root = NULL;
}
}
}
// 保存先序遍历的节点,再遍历一遍节点并同时修改
105. 从前序与中序遍历序列构造二叉树
// 递归
struct TreeNode *generate(int *preorder, int start, int preorderSize, int *inorder, int left, int right) {
if (start > preorderSize || left > right) return NULL;
struct TreeNode *root = (struct TreeNode *) malloc(sizeof(struct TreeNode));
// start是前序遍历中当前正在处理的节点
root->val = preorder[start];
// todo 可以用散列快速定位
// 定位root在中序遍历中的位置,left到pos-1的元素用于构造左子树,pos+1到right的元素用于构造右子树
int pos = left;
for (int i = left; i <= right; ++i) {
if (inorder[i] == preorder[start]) {
pos = i;
break;
}
}
// 先序: {preorder[start]}
// {左子树(一共leftCount个元素)}
// {右子树,第一个元素为preorder[start+1+leftCount]}
// 中序: {left到pos-1(一共leftCount个元素),用于构造左子树}
// {preorder[start]也就是inorder[pos]}
// {pos+1到right用于构造右子树}
// 左子树元素个数
int leftCount = pos - left;
// 构造左子树,左子树第一个节点的值是preorder[start+1]
root->left = generate(preorder, start + 1, preorderSize, inorder, left, pos - 1);
// 构造右子树,右子树第一个节点的值是preorder[start+1+leftCount]
// 因为前序遍历中的start+1到start+leftCount一共leftCount个元素是用来构造左子树的
root->right = generate(preorder, start + 1 + leftCount, preorderSize, inorder, pos + 1, right);
return root;
}
struct TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
return generate(preorder, 0, preorderSize, inorder, 0, inorderSize - 1);
}
// todo 看不懂
int pre;
int in;
struct TreeNode *generate(int *preorder, int preorderSize, int *inorder, int inorderSize, int stop) {
if (pre == preorderSize) return NULL;
if (inorder[in] == stop) {
in++;
return NULL;
}
int rootVal = preorder[pre++];
struct TreeNode *root = (struct TreeNode *) malloc(sizeof(struct TreeNode));
root->val = rootVal;
root->left = generate(preorder, preorderSize, inorder, inorderSize, rootVal);
root->right = generate(preorder, preorderSize, inorder, inorderSize, stop);
return root;
}
struct TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
pre = 0;
in = 0;
return generate(preorder, preorderSize, inorder, inorderSize, 0x7fffffff);
}
// todo 迭代
struct TreeNode *buildTree(int *preorder, int preorderSize, int *inorder, int inorderSize) {
if (preorderSize == 0) return NULL;
struct TreeNode **stack = (struct TreeNode **) malloc(sizeof(struct TreeNode *) * 3000);
int top = 0;
int pre = 0;
int in = 0;
// 先序遍历的第一个值作为根节点
struct TreeNode *curRoot = (struct TreeNode *) malloc(sizeof(struct TreeNode));
curRoot->val = preorder[pre++];
curRoot->left = NULL;
curRoot->right = NULL;
stack[top++] = curRoot;
// 作为最终根节点返回
struct TreeNode *root = curRoot;
// 遍历前序遍历的数组
while (pre < preorderSize) {
// 出现了当前节点的值和中序遍历数组的值相等,寻找是谁的右子树
if (curRoot->val == inorder[in]) {
// 每次进行出栈,实现倒着遍历
while (top != 0 && stack[top - 1]->val == inorder[in]) {
curRoot = stack[--top];
in++;
}
// 设为当前的右孩子
struct TreeNode *node = (struct TreeNode *) malloc(sizeof(struct TreeNode));
node->val = preorder[pre++];
node->left = NULL;
node->right = NULL;
curRoot->right = node;
curRoot = curRoot->right;
stack[top++] = curRoot;
} else {
// 否则 作为左子树
struct TreeNode *node = (struct TreeNode *) malloc(sizeof(struct TreeNode));
node->val = preorder[pre++];
node->left = NULL;
node->right = NULL;
curRoot->left = node;
curRoot = curRoot->left;
stack[top++] = curRoot;
}
}
return root;
}
437. 路径总和 III
// 返回从root开始往下的路径中和为targetSum的情况总数
int dfsCount(struct TreeNode *root, int targetSum, long long tempSum) {
if (root == NULL) return 0;
int tempRes = 0;
tempSum += root->val;
if (tempSum == targetSum) tempRes++;
tempRes += dfsCount(root->left, targetSum, tempSum);
tempRes += dfsCount(root->right, targetSum, tempSum);
return tempRes;
}
// 累加从每个节点出发的情况总数
int dfs(struct TreeNode *root, long long targetSum) {
if (root == NULL) return 0;
return dfsCount(root, targetSum, 0) + dfs(root->left, targetSum) + dfs(root->right, targetSum);
}
// 暴力递归
int pathSum(struct TreeNode *root, int targetSum) {
return dfs(root, targetSum);
}
// java版暴力递归
class Solution {
int dfsCount(TreeNode root, int targetSum, long tempSum) {
if (root == null) return 0;
int tempRes = 0;
tempSum += root.val;
if (tempSum == targetSum) tempRes++;
tempRes += dfsCount(root.left, targetSum, tempSum);
tempRes += dfsCount(root.right, targetSum, tempSum);
return tempRes;
}
int dfs(TreeNode root, int targetSum) {
if (root == null) return 0;
return dfsCount(root, targetSum, 0) + dfs(root.left, targetSum) + dfs(root.right, targetSum);
}
public int pathSum(TreeNode root, int targetSum) {
return dfs(root, targetSum);
}
}
// todo *树的前缀和+回溯
class Solution {
// 保存前缀树,key为前缀和,value为前缀和出现的次数
Map<Long, Integer> hashMap = new HashMap<Long, Integer>();
public int pathSum(TreeNode root, int targetSum) {
// 前缀树为0的个数至少是一个
hashMap.put(0L, 1);
return dfs(root, 0, targetSum);
}
public int dfs(TreeNode root, long prefixSum, int targetSum) {
if (root == null) return 0;
// 计算前缀和
prefixSum += root.val;
// 若是存在前缀和为prefixSum - target的节点,则该节点到当前节点的路径就是符合题意的
int cur = hashMap.getOrDefault(prefixSum - targetSum, 0);
// 保存前缀和
hashMap.put(prefixSum, hashMap.getOrDefault(prefixSum, 0) + 1);
// 计算左右子树符合题意的个数
int left = dfs(root.left, prefixSum, targetSum);
int right = dfs(root.right, prefixSum, targetSum);
// 从map中去掉当前节点的前缀和,使得兄弟结点无法使用当前结点的前缀和
hashMap.put(prefixSum, hashMap.get(prefixSum) - 1);
return cur + left + right;
}
}
236. 二叉树的最近公共祖先
// 前提:节点的值唯一,p、q都在二叉树中
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q){
if(root == NULL)
// 如果树为空,直接返回null
return NULL;
if(root == p || root == q)
// 如果p和q中有等于root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
return root;
// 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
struct TreeNode *left = lowestCommonAncestor(root->left, p, q);
// 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
struct TreeNode *right = lowestCommonAncestor(root->right, p, q);
if(left == NULL)
// 如果在左子树中p和q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
return right;
else if(right == NULL)
// 否则,如果left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况。如果在右子树中,p和q都找不到,则p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
return left;
else
//否则,当left和right均不为空时,说明p、q节点分别在 root异侧, 最近公共祖先即为 root
return root;
}
// 方法二:记录跟节点到p、q的路径。从p、q往上找到第一个公共的节点
124. 二叉树中的最大路径和
class Solution {
public:
int res = INT_MIN;
// 返回向父节点能提供的最长单侧路径和(只能来自左子树或者右子树一边)
int dfs(TreeNode *root) {
if (root == nullptr) return 0;
int left = dfs(root->left);
int right = dfs(root->right);
// 不向父节点提供时,路径会经过当前的根节点,最大路径和会包括两侧能提供的最长单侧路径和
res = max(res, left + right + root->val);
// 返回较大的单侧路径和,较大的还小于0的话就返回0,表示不向父节点提供
return max(max(left, right) + root->val, 0);
}
int maxPathSum(TreeNode *root) {
dfs(root);
return res;
}
};
图
200. 岛屿数量
struct Coordinate {
int x;
int y;
};
int res;
// 暂存字符为1坐标
struct Coordinate *stack;
// 栈顶指针
int top;
// 访问标记数组,0表示尚未访问,1表示访问过。可以省略,直接在原来的grid上标记
int **visited;
// 方向
int directions[4][2] = {{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
// 迭代写法
void dfs(char **grid, int rowSize, int columnSize, int x, int y) {
// 标记访问
visited[x][y] = 1;
if (grid[x][y] == '0') return;
// 否则就是字符1,把坐标(x, y)入栈
stack[top].x = x;
stack[top].y = y;
top++;
while (top > 0) {
int tempX = stack[top - 1].x;
int tempY = stack[top - 1].y;
// 表示node坐标四周是否还有未被访问过的1
int flag = false;
// 按上下左右的顺序找
for (int i = 0; i < 4; ++i) {
int newX = tempX + directions[i][0];
int newY = tempY + directions[i][1];
// 新坐标未越界,且尚未访问过
if ((newX >= 0 && newX < rowSize && newY >= 0 && newY < columnSize)
&& visited[newX][newY] == 0) {
// 标记访问
visited[newX][newY] = 1;
// 是1就入栈
if (grid[newX][newY] == '1') {
flag = true;
stack[top].x = newX;
stack[top].y = newY;
top++;
}
}
}
// 都被访问过了就把当前坐标出栈,回溯上个坐标
if (!flag) top--;
}
// 当前岛屿的处理完,边界要么是字符0要么是越界
res++;
}
int numIslands(char **grid, int gridSize, int *gridColSize) {
res = 0;
stack = (struct Coordinate *) malloc(sizeof(struct Coordinate) * gridSize * (*gridColSize));
top = 0;
visited = (int **) malloc(sizeof(int *) * gridSize);
for (int i = 0; i < gridSize; ++i)
visited[i] = (int *) calloc(*gridColSize, sizeof(int));
for (int i = 0; i < gridSize; ++i)
for (int j = 0; j < *gridColSize; ++j)
// 从没访问过的坐标开始遍历
if (visited[i][j] == 0)
dfs(grid, gridSize, *gridColSize, i, j);
return res;
}
// 方向
int directions[4][2] = {{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
// 判断坐标是否越界
bool isCoordinateLegal(int rowSize, int columnSize, int x, int y) {
return x >= 0 && x < rowSize && y >= 0 && y < columnSize;
}
// 判断是否访问过,0表示海洋,1表示陆地,2表示访问过
bool isVisited(char **grid, int x, int y) {
return grid[x][y] == '2';
}
void markVisited(char **grid, int x, int y) {
grid[x][y] = '2';
}
void dfs(char **grid, int rowSize, int columnSize, int x, int y) {
// 标记访问
markVisited(grid, x, y);
// 按上下左右的顺序找
for (int i = 0; i < 4; ++i) {
int newX = x + directions[i][0];
int newY = y + directions[i][1];
// 新坐标未越界,且尚未访问过
if (isCoordinateLegal(rowSize, columnSize, newX, newY) && !isVisited(grid, newX, newY)) {
if (grid[newX][newY] == '1')
dfs(grid, rowSize, columnSize, newX, newY);
}
}
}
int numIslands(char **grid, int gridSize, int *gridColSize) {
int res = 0;
for (int i = 0; i < gridSize; ++i) {
for (int j = 0; j < *gridColSize; ++j) {
// 从没访问过的且字符是1的坐标开始遍历
if (!isVisited(grid, i, j)) {
if (grid[i][j] == '1') {
res++;
dfs(grid, gridSize, *gridColSize, i, j);
}
markVisited(grid, i, j);
}
}
}
return res;
}
// BFS
// 并查集
994. 腐烂的橘子
struct Coordinate {
int x;
int y;
};
int directions[4][2] = {{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
int rowSize;
int columnSize;
int sizeOfQueue;
int front;
int rear;
int freshNum;
// 烂橘子左边队列
struct Coordinate *queue;
bool isCoordinateLegal(int x, int y) {
return x >= 0 && x < rowSize && y >= 0 && y < columnSize;
}
// 感染四周的新鲜橘子,并将其入队
void infect(int **grid, int x, int y) {
for (int i = 0; i < 4; ++i) {
int newX = x + directions[i][0];
int newY = y + directions[i][1];
if (isCoordinateLegal(newX, newY) && grid[newX][newY] == 1) {
// 被感染的新鲜橘子入队
grid[newX][newY] = 2;
queue[rear].x = newX;
queue[rear].y = newY;
rear = (rear + 1) % sizeOfQueue;
// 少了个新鲜橘子
freshNum--;
}
}
}
int orangesRotting(int **grid, int gridSize, int *gridColSize) {
rowSize = gridSize;
columnSize = *gridColSize;
sizeOfQueue = rowSize * columnSize + 1;
queue = (struct Coordinate *) malloc(sizeof(struct Coordinate) * sizeOfQueue);
front = 0;
rear = 0;
freshNum = 0;
for (int i = 0; i < rowSize; ++i) {
for (int j = 0; j < columnSize; ++j) {
if (grid[i][j] == 2) {
// 把烂橘子入队
queue[rear].x = i;
queue[rear].y = j;
rear = (rear + 1) % sizeOfQueue;
} else if (grid[i][j] == 1) {
// 记录新鲜橘子数
freshNum++;
}
}
}
// 感染轮数
int res = 0;
while (front != rear) {
int tempRear = rear;
int count = (rear - front + sizeOfQueue) % sizeOfQueue;
while (count-- > 0) {
// 出队
int tempX = queue[front].x;
int tempY = queue[front].y;
front = (front + 1) % sizeOfQueue;
// 感染四周的新鲜橘子,并将其入队
infect(grid, tempX, tempY);
}
// 有新的烂橘子入队,表明感染了一轮
if (rear != tempRear) res++;
}
// 无法感染时,检查是否还有新鲜橘子
return freshNum == 0 ? res : -1;
}
207. 课程表
// BFS:kahn算法找入度为0的顶点
// prerequisites[i][0]为弧头节点,prerequisites[i][1]为弧尾节点
bool canFinish(int numCourses, int **prerequisites, int prerequisitesSize, int *prerequisitesColSize) {
// 记录各个顶点的入度
int *inDegrees = (int *) calloc(numCourses, sizeof(int));
// 记录入度为0的顶点
int stack[numCourses];
int top = 0;
// 遍历每条弧,记录入度
for (int i = 0; i < prerequisitesSize; ++i)
inDegrees[prerequisites[i][0]]++;
// 遍历每个顶点,记录入度为0的顶点
for (int i = 0; i < numCourses; ++i)
if (inDegrees[i] == 0)
stack[top++] = i;
// 剩余未删除的弧的个数
int leftEdgeCount = prerequisitesSize;
while (top != 0) {
int k = stack[--top];
// 将所有以k为弧尾节点的弧断开
for (int i = 0; i < prerequisitesSize; ++i) {
if (prerequisites[i][1] == k) {
// 删除弧
leftEdgeCount--;
// 弧头顶点入度减一
inDegrees[prerequisites[i][0]]--;
// 若弧头节点的入度减小成0,则入栈
if (inDegrees[prerequisites[i][0]] == 0)
stack[top++] = prerequisites[i][0];
}
}
}
return leftEdgeCount == 0;
}
struct EdgeNode {
int vertex;
struct EdgeNode *next;
};
struct VertexNode {
int vertex;
struct EdgeNode *dummyHead;
};
struct Graph {
struct VertexNode *adjList;
int vertexNum;
int edgeNum;
};
struct Graph *createGraph(int numCourses, int **prerequisites, int prerequisitesSize) {
struct Graph *graph = (struct Graph *) malloc(sizeof(struct Graph));
graph->vertexNum = numCourses;
graph->edgeNum = prerequisitesSize;
graph->adjList = (struct VertexNode *) malloc(sizeof(struct VertexNode) * numCourses);
for (int i = 0; i < numCourses; ++i) {
graph->adjList[i].dummyHead = (struct EdgeNode *) malloc(sizeof(struct EdgeNode));
graph->adjList[i].vertex = i;
graph->adjList[i].dummyHead->next = NULL;
}
// 建立邻接表
for (int i = 0; i < prerequisitesSize; ++i) {
struct EdgeNode *node = (struct EdgeNode *) malloc(sizeof(struct EdgeNode));
node->vertex = prerequisites[i][0];
struct EdgeNode *dummyHead = graph->adjList[prerequisites[i][1]].dummyHead;
// 头插法
node->next = dummyHead->next;
dummyHead->next = node;
}
return graph;
}
// 访问标记数组,0表示未访问,1表示正在访问,2表示访问结束
int *visited;
struct Graph *graph;
bool hasCircle;
void dfs(int **prerequisites, int vertex) {
if (hasCircle) return;
// 正在访问当前节点
visited[vertex] = 1;
struct EdgeNode *cur = graph->adjList[vertex].dummyHead->next;
while (cur != NULL) {
if (visited[cur->vertex] == 1) {
// 同一个递归中访问到正在访问的顶点,表示出现环
hasCircle = true;
return;
}
if (visited[cur->vertex] == 0)
// 尚未访问就进行访问,访问完毕的已经退出他自己的递归了,不需要处理
dfs(prerequisites, cur->vertex);
cur = cur->next;
}
// 当前节点访问完毕
visited[vertex] = 2;
// 如果需要输出拓扑序列的话,可以在此处将节点压入栈中,最后输出栈
}
// DFS:逆拓扑排序,找出度为0的顶点
bool canFinish(int numCourses, int **prerequisites, int prerequisitesSize, int *prerequisitesColSize) {
visited = (int *) calloc(numCourses, sizeof(int));
graph = createGraph(numCourses, prerequisites, prerequisitesSize);
hasCircle = false;
for (int i = 0; i < numCourses; ++i)
if (visited[i] == 0)
dfs(prerequisites, i);
return !hasCircle;
}
208. 实现 Trie (前缀树)
// 结构体定义中不能直接赋值
typedef struct TrieNode {
bool isEnd;
// 指针数组
struct TrieNode *children[26];
} Trie;
Trie *trieCreate() {
Trie *root = (Trie *) malloc(sizeof(Trie));
root->isEnd = false;
memset(root->children, 0, sizeof(root->children));
return root;
}
void trieInsert(Trie *obj, char *word) {
Trie *cur = obj;
int len = strlen(word);
for (int i = 0; i < len; ++i) {
char ch = word[i];
if (cur->children[ch - 'a'] == NULL) {
Trie *node = (Trie *) malloc(sizeof(Trie));
node->isEnd = false;
memset(node->children, 0, sizeof(node->children));
// 给cur增加一个孩子节点
cur->children[ch - 'a'] = node;
}
// 移到对应孩子节点
cur = cur->children[ch - 'a'];
}
cur->isEnd = true;
}
bool trieSearch(Trie *obj, char *word) {
Trie *cur = obj;
int len = strlen(word);
for (int i = 0; i < len; ++i) {
char ch = word[i];
// 没有往下的路径了
if (cur->children[ch - 'a'] == NULL) return false;
// 移到对应孩子节点
cur = cur->children[ch - 'a'];
}
return cur->isEnd;
}
bool trieStartsWith(Trie *obj, char *prefix) {
Trie *cur = obj;
int len = strlen(prefix);
for (int i = 0; i < len; ++i) {
char ch = prefix[i];
// 没有往下的路径了
if (cur->children[ch - 'a'] == NULL) return false;
// 移到对应孩子节点
cur = cur->children[ch - 'a'];
}
return true;
}
void trieFree(Trie *obj) {
free(obj);
obj = NULL;
}
回溯
46. 全排列
int **res;
int *hashMap;
int *rtSize;
// temp中0到curIndex已经放入数据,现在往curIndex处放入所有可能
void generate(int *nums, int numsSize, int *temp, int curIndex) {
// temp已经放满,把当前排列添加到结果中
if (curIndex == numsSize) {
for (int i = 0; i < numsSize; ++i) {
res[(*rtSize)][i] = temp[i];
}
(*rtSize)++;
return;
}
for (int i = 0; i < numsSize; ++i) {
// nums[i]还没放入,就放入到curIndex位置
if (hashMap[nums[i] + 10] == 0) {
temp[curIndex] = nums[i];
// 标记nums[i]已经放入
hashMap[nums[i] + 10] = 1;
// 递归处理子问题,尝试curIndex+1处所有的放入可能
generate(nums, numsSize, temp, curIndex + 1);
// 取消标记,再尝试在curIndex处放入其他还没使用过的数据
hashMap[nums[i] + 10] = 0;
}
}
}
// 按字典序输出
int **permute(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
// 最多的组合数
const int maxSize = 720;
*returnSize = 0;
res = (int **) malloc(sizeof(int *) * maxSize);
*returnColumnSizes = (int *) malloc(sizeof(int) * maxSize);
for (int i = 0; i < 720; ++i) {
res[i] = (int *) malloc(sizeof(int) * numsSize);
(*returnColumnSizes)[i] = numsSize;
}
rtSize = returnSize;
// 标记数据是否已经使用过(即放入temp数组)
hashMap = (int *) calloc(21, sizeof(int));
// 暂存当前的排列
int *temp = (int *) malloc(sizeof(int) * numsSize);
generate(nums, numsSize, temp, 0);
return res;
}
int **res;
int *rtSize;
void swap(int *array, int left, int right) {
if (left == right) return;
int temp = array[left];
array[left] = array[right];
array[right] = temp;
}
// temp中0到curIndex已经放入数据,现在往curIndex处放入所有可能
void generate(int *nums, int numsSize, int *temp, int curIndex) {
// temp已经放满,把当前排列添加到结果中
if (curIndex == numsSize) {
for (int i = 0; i < numsSize; ++i) {
res[(*rtSize)][i] = temp[i];
}
(*rtSize)++;
return;
}
// nums[left]开始到末尾都是尚未使用过的元素,从中挑出一个使用,并且在nums中和nums[left]交换位置
// 这样以来nums从开头到nums[left]就是已经使用过的元素
int left = curIndex;
for (int right = left; right < numsSize; ++right) {
temp[curIndex] = nums[right];
// 标记nums[i]已经放入
swap(nums, left, right);
// 递归处理子问题,尝试curIndex+1处所有的放入可能
generate(nums, numsSize, temp, curIndex + 1);
// 取消标记,再尝试在curIndex处放入其他还没使用过的数据
swap(nums, left, right);
}
}
// 不按字典序输出,不使用hashMap标记元素是否使用过
int **permute(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
// 最多的组合数
const int maxSize = 720;
*returnSize = 0;
res = (int **) malloc(sizeof(int *) * maxSize);
*returnColumnSizes = (int *) malloc(sizeof(int) * maxSize);
for (int i = 0; i < 720; ++i) {
res[i] = (int *) malloc(sizeof(int) * numsSize);
(*returnColumnSizes)[i] = numsSize;
}
rtSize = returnSize;
// 暂存当前的排列
int *temp = (int *) malloc(sizeof(int) * numsSize);
generate(nums, numsSize, temp, 0);
return res;
}
// 以 curIndex 作为标记,nums[0, curIndex)是已经排好的,也就是使用过的元素
class Solution {
public:
vector<vector<int>> res;
void backtrack(vector<int> &nums, int curIndex) {
if (curIndex == nums.size()) {
res.emplace_back(nums);
return;
}
// nums[0, curIndex)是已经排好的,从 nums[curIndex, nums.size()-1]中选一个 nums[i] 放到 nums[curIndex]
for (int i = curIndex; i < nums.size(); ++i) {
// nums[i] 放到 nums[curIndex]
swap(nums[i], nums[curIndex]);
// 继续递归填下一个数
backtrack(nums, curIndex + 1);
// 撤销操作
swap(nums[i], nums[curIndex]);
}
}
// 不按字典序输出
vector<vector<int>> permute(vector<int> &nums) {
backtrack(nums, 0);
return res;
}
};
// 把 nums 数组当作标记数组
class Solution {
public:
vector<vector<int>> res;
vector<int> output;
// output 中 0 到 curIndex 已经放入数据,现在往 curIndex 处放入所有可能
void backtrack(vector<int> &nums, int curIndex) {
// output 已经放满,把当前排列添加到结果中
if (curIndex == nums.size()) {
res.emplace_back(output);
return;
}
for (int i = 0; i < nums.size(); ++i) {
// 已经被使用过的就跳过
if (nums[i] == INT_MIN) continue;
int temp = nums[i];
// 标记
nums[i] = INT_MIN;
output.emplace_back(temp);
// 继续递归填下一个数
backtrack(nums, curIndex + 1);
// 撤销操作
output.pop_back();
nums[i] = temp;
}
}
// 不按字典序输出
vector<vector<int>> permute(vector<int> &nums) {
backtrack(nums, 0);
return res;
}
};
78. 子集
int **res;
int *rtSize;
int **rtColumnSize;
void generate(int *nums, int numsSize, int *temp, int len, int curIndex, int nextStart) {
if (curIndex == len) {
// 将长度为len的子集加入结果
res[*rtSize] = (int *) malloc(sizeof(int) * len);
(*rtColumnSize)[*rtSize] = len;
for (int i = 0; i < len; ++i)
res[*rtSize][i] = temp[i];
(*rtSize)++;
return;
}
// 当前位置选nums[i],那么后面位置选的元素是从nums[i+1]开始选择的,避免选重复了
// nextStart之前的已经被考虑过了
for (int i = nextStart; i < numsSize; ++i) {
// 从curIndex到结尾挑一个放在curIndex
temp[curIndex] = nums[i];
// 在curIndex已经放入nums[i]的条件下,考虑curIndex+1放入i后面的元素中的哪一个
generate(nums, numsSize, temp, len, curIndex + 1, i + 1);
}
}
int **subsets(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
const int maxSize = 1024;
res = (int **) malloc(sizeof(int *) * maxSize);
rtSize = returnSize;
*rtSize = 0;
rtColumnSize = returnColumnSizes;
*rtColumnSize = (int *) malloc(sizeof(int) * maxSize);
int *temp = (int *) malloc(sizeof(int) * numsSize);
res[0] = NULL;
(*rtColumnSize)[0] = 0;
(*rtSize)++;
// 生成的长度逐渐加一
for (int len = 1; len <= numsSize; ++len)
generate(nums, numsSize, temp, len, 0, 0);
return res;
}
// todo 01序列表示对应位置的元素是否选中
int **subsets(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
int **res = (int **) malloc(sizeof(int *) * (1 << numsSize));
*returnColumnSizes = (int *) malloc(sizeof(int) * (1 << numsSize));
*returnSize = 1 << numsSize;
int temp[numsSize];
// mask中1的个数代表了子集中元素个数
for (int mask = 0; mask < (1 << numsSize); ++mask) {
// 记录子集中元素个数
int len = 0;
for (int i = 0; i < numsSize; ++i) {
// 根据mask中1的位置判断nums[i]是否被选中
// mask & (1 << i) != 0 说明被选中了
if (mask & (1 << i)) {
temp[len++] = nums[i];
}
}
int *tempRes = (int *) malloc(sizeof(int) * len);
memcpy(tempRes, temp, sizeof(int) * len);
(*returnColumnSizes)[mask] = len;
res[mask] = tempRes;
}
return res;
}
int **res;
int *rtSize;
int **rtColumnSize;
void generate(int *nums, int numsSize, int *temp, int len, int cur) {
// cur==numsSize为true时,表示temp中已经在长度为len的情况下,已经尝试放过所有的元素
if (cur == numsSize) {
int *tempRes = (int *) malloc(sizeof(int) * len);
memcpy(tempRes, temp, sizeof(int) * len);
(*rtColumnSize)[*rtSize] = len;
res[*rtSize] = tempRes;
(*rtSize)++;
return;
}
// 1.在temp[len]处放nums[cur],然后考虑在temp[len+1]处放nums数组中从cur+1到结尾中的哪个元素
temp[len] = nums[cur];
generate(nums, numsSize, temp, len + 1, cur + 1);
// 2.不在temp[len]处放nums[cur],而是考虑在temp[len]处放nums数组中从cur+1到结尾中的哪个元素
generate(nums, numsSize, temp, len, cur + 1);
}
int **subsets(int *nums, int numsSize, int *returnSize, int **returnColumnSizes) {
res = (int **) malloc(sizeof(int *) * (1 << numsSize));
rtSize = returnSize;
*rtSize = 0;
rtColumnSize = returnColumnSizes;
*rtColumnSize = (int *) malloc(sizeof(int) * (1 << numsSize));
int *temp = (int *) malloc(sizeof(int) * numsSize);
generate(nums, numsSize, temp, 0, 0);
return res;
}
17. 电话号码的字母组合
char **res;
int *rtSize;
char *temp;
char phoneMap[10][5] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
int digitsLen;
void generate(char *digits, int curIndex) {
// digits已经处理完,curIndex表示正在处理digits中下标为curIndex的数字
if (curIndex == digitsLen) {
temp[curIndex] = '\0';
char *tempRes = (char *) malloc(sizeof(char) * (curIndex + 1));
memcpy(tempRes, temp, sizeof(char) * (curIndex + 1));
res[*rtSize] = tempRes;
(*rtSize)++;
return;
}
char *phoneStr = phoneMap[digits[curIndex] - '0'];
for (int i = 0; i < strlen(phoneStr); ++i) {
// temp中curIndex处放入phoneStr中的一个字符
temp[curIndex] = phoneStr[i];
// 在放入phoneStr[i]的情况下,递归处理子问题在curIndex+1处放什么字符
generate(digits, curIndex + 1);
}
}
char **letterCombinations(char *digits, int *returnSize) {
*returnSize = 0;
digitsLen = strlen(digits);
if (digits == NULL || digitsLen == 0) return NULL;
rtSize = returnSize;
res = (char **) malloc(sizeof(char *) * 256);
temp = (char *) malloc(sizeof(char) * 5);
generate(digits, 0);
return res;
}
39. 组合总和
int *rtSize;
int **rtColumnSize;
int targetSum;
int **res;
int *temp;
void generate(int *candidates, int candidateSize, int start, int curIndex, int tempSum) {
if (tempSum == targetSum) {
int *tempRes = (int *) malloc(sizeof(int) * curIndex);
memcpy(tempRes, temp, sizeof(int) * curIndex);
res[*rtSize] = tempRes;
(*rtColumnSize)[*rtSize] = curIndex;
(*rtSize)++;
return;
}
// 如果curIndex放的时candidates[i],那么curIndex+1处不能取下标i之前的元素,防止重复
// 但curIndex+1处可以继续取candidates[i]
// 从start开始取,防止重复
for (int i = start; i < candidateSize; ++i) {
// 超过目标和就舍弃
if (tempSum + candidates[i] > targetSum) continue;
// 1.在curIndex放candidates[i]
tempSum += candidates[i];
temp[curIndex] = candidates[i];
// 递归处理子问题:在curIndex+1处放入什么元素
generate(candidates, candidateSize, i, curIndex + 1, tempSum);
// 2.回溯,取消在curIndex放candidates[i],尝试放入其他元素
tempSum -= candidates[i];
}
}
int **combinationSum(int *candidates, int candidatesSize, int target, int *returnSize, int **returnColumnSizes) {
*returnSize = 0;
rtSize = returnSize;
*returnColumnSizes = (int *) malloc(sizeof(int) * 150);
rtColumnSize = returnColumnSizes;
res = (int **) malloc(sizeof(int *) * 150);
temp = (int *) malloc(sizeof(int) * 20);
targetSum = target;
generate(candidates, candidatesSize, 0, 0, 0);
return res;
}
22. 括号生成
char **res;
int *rtSize;
char *temp;
void generate(int n, int curIndex, int leftBracketNum, int rightBracketNum) {
// 左右括号都用完
if (leftBracketNum == 0 && rightBracketNum == 0) {
temp[curIndex] = '\0';
char *tempRes = (char *) malloc(sizeof(char) * ((n << 1) + 1));
memcpy(tempRes, temp, sizeof(char) * ((n << 1) + 1));
res[(*rtSize)++] = tempRes;
return;
}
// 放入(的条件:剩余(的数量大于0
if (leftBracketNum > 0) {
// 1.curIndex处放(
temp[curIndex] = '(';
// 处理子问题curIndex+1处放什么
generate(n, curIndex + 1, leftBracketNum - 1, rightBracketNum);
}
// 放入)的条件:剩余(的数量小于剩余)的数量
if (leftBracketNum < rightBracketNum) {
// 2.curIndex处放)
temp[curIndex] = ')';
// 处理子问题curIndex+1处放什么
generate(n, curIndex + 1, leftBracketNum, rightBracketNum - 1);
}
}
char **generateParenthesis(int n, int *returnSize) {
*returnSize = 0;
rtSize = returnSize;
res = (char **) malloc(sizeof(char *) * (1 << (n << 1)));
temp = (char *) malloc(sizeof(char) * ((n << 1) + 1));
generate(n, 0, n, n);
return res;
}
// todo bfs
79. 单词搜索
bool res;
int len;
int rowSize;
int columnSize;
void recursive(char **board, char *word, int curIndex, int curRow, int curColumn) {
// 已经找到就不找了
if (res) return;
// 找到了
if (curIndex == len) {
res = true;
return;
}
// 坐标越界、board[curRow][curColumn]已经和之前的某一位匹配过了、或者和当前位不匹配都直接返回
if ((curRow < 0 || curRow >= rowSize || curColumn < 0 || curColumn >= columnSize)
|| (board[curRow][curColumn] == '0')
|| (board[curRow][curColumn] != word[curIndex]))
return;
// 匹配的情况下,置为0,标记已经匹配过
char tempChar = board[curRow][curColumn];
board[curRow][curColumn] = '0';
// 再找下一个和word[curIndex+1]匹配的
// 1.上
recursive(board, word, curIndex + 1, curRow - 1, curColumn);
// 2.下
recursive(board, word, curIndex + 1, curRow + 1, curColumn);
// 3.左
recursive(board, word, curIndex + 1, curRow, curColumn - 1);
// 4.右
recursive(board, word, curIndex + 1, curRow, curColumn + 1);
// 取消标记,恢复原始状态
board[curRow][curColumn] = tempChar;
}
bool exist(char **board, int boardSize, int *boardColSize, char *word) {
res = false;
len = strlen(word);
rowSize = boardSize;
columnSize = *boardColSize;
for (int i = 0; i < rowSize; ++i)
for (int j = 0; j < columnSize; ++j)
// 从每个位置作为起点开始
recursive(board, word, 0, i, j);
return res;
}
131. 分割回文串
class Solution {
public:
vector<vector<string>> res;
vector<string> output;
int len;
// 判断回文
bool judge(string &str, int left, int right) {
while (left < right) {
if (str[left] != str[right]) return false;
left++;
right--;
}
return true;
}
// curIndex 左边是已经分割好的字符串
void backtrack(string s, int curIndex) {
if (curIndex == len) {
res.emplace_back(output);
return;
}
for (int right = curIndex; right < len; ++right) {
// [curIndex, right] 构不成回文,就跳过
if (!judge(s, curIndex, right)) continue;
// 如果能构成回文,就临时加入 output
output.emplace_back(s.substr(curIndex, right - curIndex + 1));
// 然后递归处理后面的序列
backtrack(s, right + 1);
// 回溯
output.pop_back();
}
}
vector<vector<string>> partition(string s) {
len = s.length();
backtrack(s, 0);
return res;
}
};
51. N 皇后
class Solution {
public:
vector<vector<string>> res;
// 分别标记列和两个方向的斜线上是否已经存在皇后
unordered_set<int> columns;
unordered_set<int> diagonals1;
unordered_set<int> diagonals2;
vector<vector<string>> solveNQueens(int n) {
// 记录每一行的皇后所在的列
vector<int> queens(n, -1);
backtrack(queens, n, 0);
return res;
}
void backtrack(vector<int> &queens, int n, int row) {
if (row == n) {
vector<string> board = generateBoard(queens, n);
res.push_back(board);
} else {
for (int i = 0; i < n; i++) {
// 当前列已有皇后
if (columns.find(i) != columns.end()) continue;
// 主斜线已有
int diagonal1 = row - i;
if (diagonals1.find(diagonal1) != diagonals1.end()) continue;
// 副斜线已有
int diagonal2 = row + i;
if (diagonals2.find(diagonal2) != diagonals2.end()) continue;
// 把 row 行的皇后放在 i 列
queens[row] = i;
// 标记列和两个方向的斜线上已经存在皇后
columns.insert(i);
diagonals1.insert(diagonal1);
diagonals2.insert(diagonal2);
// 递归处理下一行
backtrack(queens, n, row + 1);
// 取消标记
queens[row] = -1;
columns.erase(i);
diagonals1.erase(diagonal1);
diagonals2.erase(diagonal2);
}
}
}
vector<string> generateBoard(vector<int> &queens, int n) {
vector<string> board;
for (int i = 0; i < n; i++) {
string row = string(n, '.');
row[queens[i]] = 'Q';
board.push_back(row);
}
return board;
}
};
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<string>> res;
// 记录皇后放的位置,queens[i] 二进制位为 1 的地方才是放皇后的位置
// 也可以直接记录具体列号,这样生成结果时快些
vector<int> queens;
int limit;
vector<vector<string>> solveNQueens(int n) {
// 把低 n 位变成 1
limit = (1 << n) - 1;
// -1 的位置表示没有皇后
queens.resize(n, -1);
backtrack(n, 0, 0, 0, 0);
return res;
}
void backtrack(int n, int row, int columns, int diagonals1, int diagonals2) {
if (columns == limit) {
// 生成结果
res.emplace_back(generateBoard(n));
return;
}
// 0 的位置能放,1 的位置不能放
int ban = columns | diagonals1 | diagonals2;
// candidate 为 1 的地方都是可以放皇后的
int candidate = limit & (~ban);
// 尝试每个位置
while (candidate != 0) {
// 最右侧的 1
int place = candidate & (-candidate);
queens[row] = place;
// 累计上当前皇后的影响,(diagonals1 | place) >> 1 的意思是当前 place 位置放皇后的情况下,主斜线对下一行的影响
backtrack(n, row + 1, columns | place, (diagonals1 | place) >> 1, (diagonals2 | place) << 1);
// 删掉最右侧的 1
candidate ^= place;
}
}
vector<string> generateBoard(int n) {
vector<string> board;
for (int i = 0; i < n; i++) {
string str;
for (int j = 0; j < n; ++j) {
if ((queens[i] & (1 << j)) != 0) {
str += 'Q';
} else {
str += '.';
}
}
board.emplace_back(str);
}
return board;
}
};
二分查找
35. 搜索插入位置
// 左边界(大于等于target的第一个位置)
int searchInsert(int *nums, int numsSize, int target) {
int left = 0, right = numsSize - 1;
int mid;
while (left <= right) {
mid = ((right - left) >> 1) + left;
if (nums[mid] >= target)
// 往左
right = mid - 1;
else
// 往右
left = mid + 1;
}
// 结束时,left=right+1
// right右边全都大于等于target,left左边全都小于target
return left;
}
74. 搜索二维矩阵
int rowSize;
int columnSize;
// 当成一维数组进行二分查找
bool binarySearch(int **matrix, int target, int left, int right) {
if (left > right) return false;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
// 计算具体坐标
int row = mid / columnSize;
int column = mid % columnSize;
int cur = matrix[row][column];
if (cur == target) {
return true;
} else if (cur > target) {
// 往左
right = mid - 1;
} else {
// 往右
left = mid + 1;
}
}
return false;
}
bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) {
rowSize = matrixSize;
columnSize = *matrixColSize;
return binarySearch(matrix, target, 0, rowSize * columnSize - 1);
}
bool searchMatrix(int **matrix, int matrixSize, int *matrixColSize, int target) {
int row = 0, column = *matrixColSize - 1;
// 从右上角往左或往下
while (row < matrixSize && column >= 0) {
int cur = matrix[row][column];
if (cur == target) {
return true;
} else if (cur < target) {
// 往下找更大的
row++;
} else {
// 往左找更小的
column--;
}
}
return false;
}
34. 在排序数组中查找元素的第一个和最后一个位置
// 左边界
int binarySearch1(int *array, int size, int target) {
int left = 0, right = size - 1;
int mid;
while (left <= right) {
mid = ((right - left) >> 1) + left;
if (array[mid] >= target)
right = mid - 1;
else
left = mid + 1;
}
return left;
}
// 右边界
int binarySearch2(int *array, int size, int target) {
int left = 0, right = size - 1;
int mid;
while (left <= right) {
mid = ((right - left) >> 1) + left;
if (array[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
return right;
}
int *searchRange(int *nums, int numsSize, int target, int *returnSize) {
int *res = (int *) malloc(sizeof(int) * 2);
*returnSize = 2;
int left = binarySearch1(nums, numsSize, target);
int right = binarySearch2(nums, numsSize, target);
if (left >= numsSize || nums[left] != target) {
res[0] = -1;
res[1] = -1;
} else {
res[0] = left;
res[1] = right;
}
return res;
}
33. 搜索旋转排序数组
// 递归
int binarySearchNums(int *nums, int target, int left, int right) {
if (left > right) return -1;
int mid = left + ((right - left) >> 1);
if (nums[mid] == target) return mid;
if (nums[mid] >= nums[left]) {
// [left, mid-1]是有序的,并且target在范围内
if ((left <= mid - 1) && nums[left] <= target && nums[mid - 1] >= target)
return binarySearchNums(nums, target, left, mid - 1);
else
return binarySearchNums(nums, target, mid + 1, right);
} else {
// [mid+1, right]是有序的,并且target在范围内
if ((mid + 1 <= right) && nums[mid + 1] <= target && nums[right] >= target)
return binarySearchNums(nums, target, mid + 1, right);
else
return binarySearchNums(nums, target, left, mid - 1);
}
}
int search(int *nums, int numsSize, int target) {
return binarySearchNums(nums, target, 0, numsSize - 1);
}
// 迭代
int binarySearchNums(int *nums, int target, int left, int right) {
if (left > right) return -1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (nums[mid] == target) return mid;
if (nums[mid] >= nums[left]) {
// [left, mid-1]是有序的,并且target在范围内
if ((left <= mid - 1) && nums[left] <= target && nums[mid - 1] >= target)
right = mid - 1;
else
left = mid + 1;
} else {
// [mid+1, right]是有序的,并且target在范围内
if ((mid + 1 <= right) && nums[mid + 1] <= target && nums[right] >= target)
left = mid + 1;
else
right = mid - 1;
}
}
return -1;
}
int search(int *nums, int numsSize, int target) {
return binarySearchNums(nums, target, 0, numsSize - 1);
}
153. 寻找旋转排序数组中的最小值
// 循环右移的nums
int findMin(int *nums, int numsSize) {
int left = 0, right = numsSize - 1;
int min = nums[0];
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (nums[mid] < min) min = nums[mid];
if (nums[mid] >= nums[left]) {
// [left, mid-1]是顺序区间
if (nums[left] < min) min = nums[left];
// 在右侧区间找
left = mid + 1;
} else {
// [mid+1, right]是顺序区间
if ((mid + 1 <= right) && nums[mid + 1] < min) min = nums[mid + 1];
// 在左侧区间找
right = mid - 1;
}
}
return min;
}
// todo
// 循环右移的nums
int findMin(int *nums, int numsSize) {
int left = 0;
int right = numsSize - 1;
int mid;
// 规律:最小值下标x,[0,x)值都大于等于末尾元素nums[x],[x,numsSize-1]都小于等于末尾元素nums[x]
while (left < right) {
mid = left + ((right - left) >> 1);
if (nums[mid] > nums[right]) {
// [left, mid]都大于nums[right],都排除,在右侧区间[mid+1, right]中找
left = mid + 1;
} else if (nums[mid] < nums[right]) {
// nums[mid]是[mid,right]上最小的,忽略(mid,right]上的,在[left, mid]中找
right = mid;
}
}
// 循环结束时,left等于right,且left左边全都大于nums[left],nums[right]又大于等于其右边的
// 所以最小值就是nums[left]
return nums[left];
}
// todo
// 循环右移的nums
int findMin(int *nums, int numsSize) {
int left = 0;
int right = numsSize - 1;
int mid;
// 规律:最小值下标x,[0,x)值都大于等于末尾元素nums[x],[x,numsSize-1]都小于等于末尾元素nums[x]
while (left < right) {
mid = left + ((right - left) >> 1);
if (nums[mid] > nums[right]) {
// [left, mid]都大于nums[right],都排除,在右侧区间[mid+1, right]中找
left = mid + 1;
} else if (nums[mid] < nums[right]) {
// nums[mid]是[mid,right]上最小的,忽略(mid,right]上的,在[left, mid]中找
right = mid;
} else {
// 忽略末尾,新的末尾nums[right-1]也符合规律
right--;
}
}
return nums[left];
}
bool judgeMin(int *nums, int numsSize, int index) {
// 同时比左右两个元素小的就是最小值
if (nums[index] < nums[(index - 1 + numsSize) % numsSize]
&& nums[index] < nums[(index + 1) % numsSize])
return true;
return false;
}
// 对每个最小值可能出现的地方进行判断
int findMin(int *nums, int numsSize) {
int left = 0;
int right = numsSize - 1;
int mid;
while (left < right) {
mid = left + ((right - left) >> 1);
// left可能等于mid,所以要加上等号,表示一个元素nums[left]也有序
if (nums[left] <= nums[mid]) {
// [left, mid]有序
if (judgeMin(nums, numsSize, left)) return nums[left];
left = mid + 1;
} else if (nums[left] > nums[mid]) {
// [mid, right]有序
if (judgeMin(nums, numsSize, mid)) return nums[mid];
right = mid;
}
}
return nums[left];
}
4. 寻找两个正序数组的中位数
class Solution {
public:
int getKth(vector<int> &ary1, int start1, int end1, vector<int> &ary2, int start2, int end2, int k) {
// 记录两个数组的长度
int len1 = end1 - start1 + 1;
int len2 = end2 - start2 + 1;
// 其中一个数组空了,返回另一个数组中第 k 个小的
if (len1 == 0) return ary2[start2 + k - 1];
if (len2 == 0) return ary1[start1 + k - 1];
// 返回最小的
if (k == 1) return min(ary1[start1], ary2[start2]);
// 找第 k/2 小的元素所在的下标,如果数组个数比 k/2 还小,就找末尾元素
int i = start1 + min(len1, k / 2) - 1;
int j = start2 + min(len2, k / 2) - 1;
if (ary1[i] < ary2[j]) {
// 排除 ary1[start1]~ary1[i] 位置的所有元素,一共 excluded 个数,这些都不可能是第 k 小的
int excluded = i - start1 + 1;
// 继续在剩余的数组元素中找第 k - excluded 小的
return getKth(ary1, i + 1, end1, ary2, start2, end2, k - excluded);
} else {
// 排除 ary2[start2]~ary2[j] 位置的所有元素
int excluded = j - start2 + 1;
return getKth(ary1, start1, end1, ary2, j + 1, end2, k - excluded);
}
}
// 递增序列A和B,从A和B中找第k小的数字,A[1]~A[k/2],B[1]~B[k/2],一共才k个数,
// 在A[k/2] < B[k/2]的情况下,A[k/2]最多是第k-1小的,在找第k小的数字时就可以将A[1]~A[k/2]排除
double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size();
int n = nums2.size();
// 靠左的中位数是第 (m + n + 1) / 2 小的数
int left = (m + n + 1) / 2;
// 靠右的中位数是第 (m + n + 2) / 2 小的数
int right = (m + n + 2) / 2;
// 将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
return (getKth(nums1, 0, m - 1, nums2, 0, n - 1, left) + getKth(nums1, 0, m - 1, nums2, 0, n - 1, right)) * 0.5;
}
};
class Solution {
public:
// todo
double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size();
int n = nums2.size();
// 保证 m <= n
if (m > n) return findMedianSortedArrays(nums2, nums1);
int iMin = 0, iMax = m;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = (m + n + 1) / 2 - i;
if (j != 0 && i != m && nums2[j - 1] > nums1[i]) {
// i 需要增大
iMin = i + 1;
} else if (i != 0 && j != n && nums1[i - 1] > nums2[j]) {
// i 需要减小
iMax = i - 1;
} else {
// 达到要求,并且将边界条件列出来单独考虑
int maxLeft = 0;
if (i == 0)
maxLeft = nums2[j - 1];
else if (j == 0)
maxLeft = nums1[i - 1];
else
maxLeft = max(nums1[i - 1], nums2[j - 1]);
// 奇数的话不需要考虑右半部分
if ((m + n) % 2 == 1)
return maxLeft;
int minRight;
if (i == m)
minRight = nums2[j];
else if (j == n)
minRight = nums1[i];
else
minRight = min(nums2[j], nums1[i]);
// 如果是偶数的话返回结果
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
};
Top100(中)的更多相关文章
- 整理requests和正则表达式爬取猫眼Top100中遇到的问题及解决方案
最近看崔庆才老师的爬虫课程,第一个实战课程是requests和正则表达式爬取猫眼电影Top100榜单.虽然理解崔老师每一步代码的实现过程,但自己敲代码的时候还是遇到了不少问题: 问题1:获取respo ...
- 微信小程序 TOP100 榜单
8 月 12 日,阿拉丁数据统计平台发布了国内第一份小程序 TOP100 榜单,摩拜单车成为全榜第一! 该榜单数据来源于阿拉丁小程序统计平台检测.合作.如有赞等,并经过企业电话调研和实地走访企业等校准 ...
- LeetCode刷题笔记和想法(C++)
主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...
- 亚马逊DRKG使用体验
基于文章:探索「老药新用」最短路径:亚马逊AI Lab开源大规模药物重定位知识图谱DRKG,记录了该项目的实际部署与探索过程,供参考. 1. DRKG介绍 大规模药物重定位知识图谱 Drug Repu ...
- Google服务器架构图解简析
无疑是互联网时代最闪亮的明星.截止到今天为止,Google美国主站在Alexa排名已经连续3年第一,Alexa Top100中,各国的Google分站竟然霸占了超过20多个名额,不得不令人感叹Goog ...
- 学习GlusterFS(三)
glusterfs,GNU cluster file system,创始人Anand Babu Periasamy,目标:代替开源Lustre和商业产品GPFS,glusterfs是什么: cloud ...
- Python开源框架
info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...
- 多线程爬取猫眼电影TOP100并保存到mongo数据库中
import requests import re import json from requests.exceptions import RequestException from multipro ...
- 《zw版·Halcon-delphi系列原创教程》 zw版-Halcon常用函数Top100中文速查手册
<zw版·Halcon-delphi系列原创教程> zw版-Halcon常用函数Top100中文速查手册 Halcon函数库非常庞大,v11版有1900多个算子(函数). 这个Top版,对 ...
- 参加2015年TOP100会议的零散笔记
2015年出差很少,感到整个技术都已经荒废了,收到12月份TOP100的会议通知后,还是去充点电吧,不然心慌啊.对于软件大会这种大杂烩式的会议已经没有多少兴趣了,看看这个TOP100组织得有何不同? ...
随机推荐
- 【安装部署】Apache SeaTunnel 和 Web快速安装详解
版本说明 由于作者目前接触当前最新版本为2.3.4 但是官方提供的web版本未1.0.0,不兼容2.3.4,因此这里仍然使用2.3.3版本. 可以自定义兼容处理,官方提供了文档:https://mp. ...
- 在IIS上部署ASP.NET Core Web API和Blazor Wasm详细教程
前言 前段时间我们完成了七天.NET 8 操作 SQLite 入门到实战的开发系列教程,有不少同学留言问如何将项目发布部署到IIS上面运行.本篇文章我们就一起来讲讲在IIS上部署ASP.NET Cor ...
- [KEYENCE Programming Contest 2023 Autumn(AtCoder Beginner Contest 325) E
KEYENCE Programming Contest 2023 Autumn(AtCoder Beginner Contest 325) - AtCoder E E - Our clients, p ...
- csv导入导出组件jcsv
jcsv 介绍 jcsv一个简单的.轻量级的csv导入.导出库,相对于opencsv与javacsv,jcsv侧重于导入导出,包括导入校验.导出模板等. 源代码地址:https://gitee.com ...
- kali常用配置
用户须知 1.免责声明:本教程作者及相关参与人员对于任何直接或间接使用本教程内容而导致的任何形式的损失或损害,包括但不限于数据丢失.系统损坏.个人隐私泄露或经济损失等,不承担任何责任.所有使用本教程内 ...
- Game on Sum--组合数学--DP
\(Codeforces-Round 767\) (Div. 2) F2. \(Game \ on \ Sum\) \(HERE\) 题意 \(QZS\) 和 \(HANGRY\) 玩游戏. 游戏共有 ...
- [golang]查询ssl证书剩余有效天数并邮件提醒
前言 自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案.但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,如果证书即将过 ...
- 工作 6 年,@Transactional 注解用的一塌糊涂
接手新项目一言难尽,别的不说单单就一个 @Transactional 注解用的一塌糊涂,五花八门的用法,很大部分还失效无法回滚. 有意识的在涉及事务相关方法上加@Transactional注解,是个好 ...
- 神经网络之卷积篇:详解简单卷积网络示例(A simple convolution network example)
详解简单卷积网络示例 假设有一张图片,想做图片分类或图片识别,把这张图片输入定义为\(x\),然后辨别图片中有没有猫,用0或1表示,这是一个分类问题,来构建适用于这项任务的卷积神经网络.针对这个示例, ...
- 浅谈 C# 中的顶级语句
前言 在C# 9版本中引入了一项新特性:顶级语句,这一特性允许在不显式定义 Main 方法的情况下直接编写代码. 传统的写法 namespace TestStatements{ internal ...