07 2021 档案
摘要:只有当链表 headA 和 headB 都不为空时,两个链表才可能相交。因此首先判断链表 headA 和 headB 是否为空,如果其中至少有一个链表为空,则两个链表一定不相交,返回 null。 当链表 headA 和 headB 都不为空时,创建两个指针 pA 和 pB,初始时分别指向两个链表的头
阅读全文
摘要:class Solution { public: vector<int> temp; int res = 0; int reversePairs(vector<int>& nums) { int n = nums.size(); temp.resize(n); mergeSort(nums, 0,
阅读全文
摘要:方法一:使用哈希表存储频数 我们可以对字符串进行两次遍历。 在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回该字符,否则在遍历结束后返回空格。 class Solution { public: char firstUn
阅读全文
摘要:首先我们来通过一个例子理解一下这里「翻译」的过程:我们来尝试翻译「14021402」。 分成两种情况: 首先我们可以把每一位单独翻译,即 [1, 4, 0, 2],翻译的结果是 beac 然后我们考虑组合某些连续的两位: [14, 0, 2],翻译的结果是 oac。 [1, 40, 2],这种情况是
阅读全文
摘要:class Solution { public: static bool cmp(int& a, int& b) { string sa = to_string(a), sb = to_string(b); return sa + sb < sb + sa; } string minNumber(v
阅读全文
摘要:class Solution { public: int digitAtIndex(int n) { int digit = 1; int start = 0; long long cnt = 10; while (n >= cnt) { n -= cnt; digit++; if (start =
阅读全文
摘要:标准的中序遍历采用 左 → 根 → 右 的顺序,其中 左 和 右 的部分调用递归。 本题的处理在于将前一个结点与当前结点链接,因此,必须跟踪最后一个结点,该结点在新的双向链表中是当前最大的。 另外一个细节:我们也需要保留第一个,也就是最小的结点,以完成闭环。 下面是具体算法: 将 first 和 l
阅读全文
摘要:由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。由于链表的头节点可能会被删除,因此我们需要额外使用一个哑节点(dummy node)指向链表的头节点。 具体地,我们从指针 p 指向链表的哑节点,随后开始对链表进行遍历。如果当
阅读全文
摘要:由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。 具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与 cur.next 对应的元素相同,那么我们就将 cur.next 从链表中移除;否
阅读全文
摘要:法一 我们首先将该链表中每一个节点拆分为两个相连的节点,例如对于链表 ,我们可以将其拆分为 \(A \rightarrow A' \rightarrow B \rightarrow B' \rightarrow C \rightarr
阅读全文
摘要:对于给定的某个排列,我们想求出比它大的最小的排列。 可以从后往前遍历这个排列,找到第一个可以让排列的字典序变大的位置。 只有当序列单调下降时,它才不存在更大的排列,因此我们要找的位置就是第一-次出现 的位置。 那么此时将 变成比它大的最小数
阅读全文
摘要:序列中包含了重复的数字,要求我们返回不重复的全排列,那么我们依然可以选择使用搜索回溯的方法来做。 我们将这个问题看作有 n 个排列成一行的空格,我们需要从左往右依次填入题目给定的 n 个数,每个数只能使用一次。那么很直接的可以想到一种穷举的算法,即从左往右每一个位置都依此尝试填入一个数,看能不能填完
阅读全文
摘要:为了动态维护中位数,我们可以建立两个二叉堆:一个小根堆、一个大根堆。 在依次读入这个整数序列的过程中,设当前序列长度为M,我们始终保持: 序列中从小到大排名为1 ~ M/2的整数存储在大根堆中; 序列中从小到大排名为M/2+1 ~ M的整数存储在小根堆中, 大根堆允许存储的元素最多比小根堆多一个。
阅读全文
摘要:法一 我们用一个大根堆实时维护数组的前 k 小值。首先将前 k 个数插入大根堆中,随后从第 k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。 class Solution { public: vector<i
阅读全文
摘要:根据二叉搜索树的定义,可以通过递归,判断所有子树的正确性 (即其后序遍历是否满足二叉搜索树的定义) ,若所有子树都正确,则此序列为二叉搜索树的后序遍历。 终止条件: 当 ,说明此子树节点数量小于等于,返回 true ; 划分左右子树: 遍历后序遍历的 \([l, r]
阅读全文
摘要:将根节点插入队列中; 创建一个新队列,用来按顺序保存下一层的所有子节点; 对于当前队列中的所有节点,按顺序依次将儿子插入新队列; 按从左到右、从右到左的顺序交替保存队列中节点的值; 重复步骤2-4,直到队列为空为止。 /** * Definition for a binary tree node.
阅读全文
摘要:我们可以用广度优先搜索解决这个问题。 我们可以想到最朴素的方法是用一个二元组 (node, level) 来表示状态,它表示某个节点和它所在的层数,每个新进队列的节点的 level 值都是父亲节点的 level 值加一。最后根据每个点的 level 对点进行分类,分类的时候我们可以利用哈希表,维护一
阅读全文
摘要:BFS。 /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NUL
阅读全文
摘要:思路 所有的元素一定是按顺序 push 进去的,重要的是怎么 pop 出来? 假设当前栈顶元素值为 2,同时对应的 popped 序列中下一个要 pop 的值也为 2,那就必须立刻把这个值 pop 出来。因为之后的 push 都会让栈顶元素变成不同于 2 的其他值,这样再 pop 出来的数 popp
阅读全文
摘要:单调栈。 class MinStack { public: stack<int> stk, minstk; /** initialize your data structure here. */ MinStack() { } void push(int x) { stk.push(x); if (m
阅读全文
摘要:可以模拟螺旋矩阵的路径。初始位置是矩阵的左上角,初始方向是向右,当路径超出界限或者进入之前访问过的位置时,顺时针旋转,进入下一个方向。 判断路径是否进入之前访问过的位置需要使用一个与输入矩阵大小相同的辅助矩阵 visited,其中的每个元素表示该位置是否被访问过。当一个元素被访问时,将 visite
阅读全文
摘要:如果一个树的左子树与右子树镜像对称,那么这个树是对称的。 因此,该问题可以转化为:两个树在什么情况下互为镜像? 如果同时满足下面的条件,两个树互为镜像: 它们的两个根结点具有相同的值 每个树的右子树都与另一个树的左子树镜像对称 我们可以实现这样一个递归函数,通过「同步移动」两个指针的方法来遍历这棵树
阅读全文
摘要:我们从根节点开始,递归地对树进行遍历,并从叶子节点先开始翻转得到镜像。 如果当前遍历到的节点 root 的左右两棵子树都已经翻转得到镜像,那么我们只需要交换两棵子树的位置,即可得到以 root 为根节点的整棵子树的镜像。 /** * Definition for a binary tree node
阅读全文
摘要:代码分为两个部分: 遍历树A中的所有非空节点R; 判断树A中以R为根节点的子树是不是包含和树B一样的结构,且我们从根节点开始匹配; 对于第一部分,我们直接递归遍历树A即可,遇到非空节点后,就进行第二部分的判断。 对于第二部分,我们同时从根节点开始遍历两棵子树: 如果树B中的节点为空,则表示当前分支是
阅读全文
摘要:二路归并。 /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ clas
阅读全文
摘要:快慢指针。 /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ clas
阅读全文
摘要:双指针。 class Solution { public: vector<int> exchange(vector<int>& nums) { int n = nums.size(); int i = 0, j = n - 1; while (i < j) { while (i < j && num
阅读全文
摘要:快速幂。 class Solution { public: double qpow(double x, long long n) { double res = 1; while (n) { if (n & 1) res *= x; x = x * x; n >>= 1; } return res;
阅读全文
摘要:方法一:动态规划 对于的正整数 ,当 时,可以拆分成至少两个正整数的和。令 是拆分出的第一个正整数,则剩下的部分是 , 可以不继续拆分,或者继续拆分成至少两个正整数的和。由于每个正整数对应的最大乘积取决于比它小的正整数对应的最
阅读全文
摘要:DFS。 class Solution { public: int n, m; int single_sum(int x) { int res = 0; while (x) { res += x % 10; x /= 10; } return res; } int sum(int x, int y)
阅读全文
摘要:回溯法。 class Solution { public: int n, m; int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; bool check(int x, int y) { return x >= 0 && x < n && y >= 0
阅读全文
摘要:一个包含重复元素的升序数组在经过旋转之后,可以得到下面可视化的折线图: 其中横轴表示数组元素的下标,纵轴表示数组元素的值。图中标出了最小值的位置,是我们需要查找的目标。 我们考虑数组中的最后一个元素 x:在最小值右侧的元素,它们的值一定都小于等于 x;而在最小值左侧的元素,它们的值一定都大于等于 x
阅读全文
摘要:一个不包含重复元素的升序数组在经过旋转之后,可以得到下面可视化的折线图: 其中横轴表示数组元素的下标,纵轴表示数组元素的值。图中标出了最小值的位置,是我们需要查找的目标。 我们考虑数组中的最后一个元素 x:在最小值右侧的元素(不包括最后一个元素本身),它们的值一定都严格小于 x;而在最小值左侧的元素
阅读全文
摘要:维护两个栈,第一个栈支持插入操作,第二个栈支持删除操作。 根据栈先进后出的特性,我们每次往第一个栈里插入元素后,第一个栈的顶部元素是最后插入的元素,第一个栈的底部元素是下一个待删除的元素。 为了维护队列先进先出的特性,我们引入第二个栈,用第二个栈维护待删除的元素,在执行删除操作的时候我们首先看下第二
阅读全文
摘要:如果一个节点有右子树,那么它的下一个节点就是它的右子树中的最左子节点。 接着我们分析一个节点没有右子树的情形: 如果结点是它父节点的左子节点,那么它的下一个节点就是它的父节点。 如果一个节点没有右子树,并且它还是它父节点的右子节点。我们可以沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左
阅读全文
摘要:/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), r
阅读全文
摘要:/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solu
阅读全文
摘要:在部分编程语言中,我们可以动态地将原数组长度扩大,此时我们就可以使用双指针算法,来降低空间的使用: 首先遍历一遍原数组,求出最终答案的长度length; 将原数组resize成length大小; 使用两个指针,指针i指向原字符串的末尾,指针j指向length的位置; 两个指针分别从后往前遍历,如果s
阅读全文
摘要:由于给定的二维数组具备每行从左到右递增以及每列从上到下递增的特点,当访问到一个元素时,可以排除数组中的部分元素。 从二维数组的右上角开始查找。如果当前元素等于目标值,则返回 true。如果当前元素大于目标值,则移到左边一列。如果当前元素小于目标值,则移到下边一行。 可以证明这种方法不会错过目标值。如
阅读全文
摘要:二分出重复出现的数。 一共有 n+1 个数,每个数的取值范围是1到n,所以至少会有一个数出现两次。 然后我们采用分治的思想,将每个数的取值的区间[1, n]划分成[1, n/2]和[n/2+1, n]两个子区间,然后分别统计两个区间中数的个数。 注意这里的区间是指 数的取值范围,而不是 数组下标。
阅读全文
摘要:一个数 是 的幂,当且仅当 是正整数,并且 的二进制表示中仅包含 1 个 1。 因此我们可以考虑使用位运算,将 的二进制表示中最低位的那个 提取出来,再判断剩余的数值是否为 即可。下面介绍两种常见的与「二进制表示中最
阅读全文
摘要:二分图的最大匹配,匈牙利算法裸题。 const int N = 210; vector<int> g[N]; bool vis[N]; int match[N]; int n, m; int find(int x) { for(int i = 0; i < g[x].size(); i++) { i
阅读全文
摘要:题目中的匹配是一个「逐步匹配」的过程:我们每次从字符串 p 中取出一个字符或者「字符 + 星号」的组合,并在 s 中进行匹配。对于 p 中一个字符而言,它只能在 s 中匹配一个字符,匹配的方法具有唯一性;而对于 p 中字符 + 星号的组合而言,它可以在 s 中匹配任意自然数个字符,并不具有唯一性。因
阅读全文