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;
    }
};
posted @ 2024-01-17 01:31  n1ce2cv  阅读(4)  评论(0编辑  收藏  举报