普通视图

发现新文章,点击刷新页面。
今天 — 2025年11月20日LeetCode 每日一题题解

每日一题-设置交集大小至少为2🔴

2025年11月20日 00:00

给你一个二维整数数组 intervals ,其中 intervals[i] = [starti, endi] 表示从 startiendi 的所有整数,包括 startiendi

包含集合 是一个名为 nums 的数组,并满足 intervals 中的每个区间都 至少两个 整数在 nums 中。

  • 例如,如果 intervals = [[1,3], [3,7], [8,9]] ,那么 [1,2,4,7,8,9][2,3,4,8,9] 都符合 包含集合 的定义。

返回包含集合可能的最小大小。

 

示例 1:

输入:intervals = [[1,3],[3,7],[8,9]]
输出:5
解释:nums = [2, 3, 4, 8, 9].
可以证明不存在元素数量为 4 的包含集合。

示例 2:

输入:intervals = [[1,3],[1,4],[2,5],[3,5]]
输出:3
解释:nums = [2, 3, 4].
可以证明不存在元素数量为 2 的包含集合。 

示例 3:

输入:intervals = [[1,2],[2,3],[2,4],[4,5]]
输出:5
解释:nums = [1, 2, 3, 4, 5].
可以证明不存在元素数量为 4 的包含集合。 

 

提示:

  • 1 <= intervals.length <= 3000
  • intervals[i].length == 2
  • 0 <= starti < endi <= 108

【宫水三叶の相信科学系列】贪心运用题

作者 AC_OIer
2022年7月22日 10:07

贪心

不要被样例数据误导了,题目要我们求最小点集的数量,并不规定点集 S 是连续段。

为了方便,我们令 intervalsins

当只有一个线段时,我们可以在线段内取任意两点作为 S 成员,而当只有两个线段时,我们可以两个线段重合情况进行决策:

  1. 当两个线段完全不重合时,为满足题意,我们需要从两个线段中各取两个点,此时这四个点都可以任意取;
  2. 当两个线段仅有一个点重合,为满足 S 最小化的题意,我们可以先取重合点,然后再两个线段中各取一个;
  3. 当两个线段有两个及以上的点重合,此时在重合点中任选两个即可。

不难发现,当出现重合的所有情况中,必然可以归纳某个线段的边缘点上。即不存在两个线段存在重合点,仅发生在两线段的中间部分:

image.png

因此我们可以从边缘点决策进行入手。

具体的,我们可以按照「右端点从小到大,左端点从大到小」的双关键字排序,然后从前往后处理每个区间,处理过程中不断往 S 中添加元素,由于我们已对所有区间排序且从前往后处理,因此我们往 S 中增加元素的过程中必然是单调递增,同时在对新的后续区间考虑是否需要往 S 中添加元素来满足题意时,也是与 S 中的最大/次大值(点集中的边缘元素)做比较,因此我们可以使用两个变量 ab 分别代指当前集合 S 中的次大值和最大值(ab 初始化为足够小的值 $-1$),而无需将整个 S 存下来。

不失一般性的考虑,当我们处理到 $ins[i]$ 时,该如何决策:

  1. 若 $ins[i][0] > b$(当前区间的左端点大于当前点集 S 的最大值),说明 $ins[i]$ 完全不被 S 所覆盖,为满足题意,我们要在 $ins[i]$ 中选两个点,此时直观思路是选择 $ins[i]$ 最靠右的两个点(即 $ins[i][1] - 1$ 和 $ins[i][1]$);
  2. 若 $ins[i][0] > a$(即当前区间与点集 S 存在一个重合点 b,由于次大值 a 和 最大值 b 不总是相差 $1$,我们不能写成 $ins[i][0] == b$),此时为了满足 $ins[i]$ 至少被 $2$ 个点覆盖,我们需要在 $ins[i]$ 中额外选择一个点,此时直观思路是选择 $ins[i]$ 最靠右的点(即$ins[i][1]$);
  3. 其余情况,说明当前区间 $ins[i]$ 与点集 S 至少存在两个点 ab,此时无须额外引入其余点来覆盖 $ins[i]$。

上述情况是对「右端点从小到大」的必要性说明,而「左端点从大到小」目的是为了方便我们处理边界情况而引入的:若在右端点相同的情况下,如果「左端点从小到大」处理的话,会有重复的边缘点被加入 S

有同学对重复边缘点加入 S 不理解,假设 S 当前的次大值和最大值分别为 jk(其中 $k - j > 1$),如果后面有两个区间分别为 $[k - 1, d]$ 和 $[k + 1, d]$ 时,就会出现问题(其中 d 为满足条件的任意右端点)
更具体的:假设当前次大值和最大值分别为 $2$ 和 $4$,后续两个区间分别为 $[3, 8]$ 和 $[5, 8]$,你会发现先处理 $[3, 8]$ 的话,数值 $8$ 会被重复添加

上述决策存在直观判断,需要证明不存在比该做法取得的点集 S 更小的合法解

若存在更小的合法集合方案 A(最优解),根据我们最前面对两个线段的重合分析知道,由于存在任意选点均能满足覆盖要求的情况,因此最优解 A 的具体方案可能并不唯一。

因此首先我们先在不影响 A 的集合大小的前提下,对具体方案 A 中的非关键点(即那些被选择,但既不是某个具体区间的边缘点,也不是边缘点的相邻点)进行调整(修改为区间边缘点或边缘点的相邻点)

这样我们能够得到一个唯一的最优解具体方案,该方案既能取到最小点集大小,同时与贪心解 S 的选点有较大重合度。

此时如果贪心解并不是最优解的话,意味着贪心解中存在某些不必要的点(可去掉,同时不会影响覆盖要求)。

然后我们在回顾下,我们什么情况下会往 S 中进行加点,根据上述「不失一般性」的分析:

  1. 当 $ins[i][0] > b$ 时,我们会往 S 中添加两个点,若这个不必要的点是在这个分支中被添加的话,意味着当前 $ins[i]$ 可以不在此时被覆盖,而在后续其他区间 $ins[j]$ 被覆盖时被同步覆盖(其中 $j > i$),此时必然对应了我们重合分析中的后两种情况,可以将原本在 $ins[j]$ 中被选择的点,调整为 $ins[i]$ 的两个边缘点,结果不会变差(覆盖情况不变,点数不会变多):

image.png

即此时原本在最优解 A 中不存在,在贪心解 S 中存在的「不必要点」会变成「必要点」。

  1. 当 $ins[i] > a$ 时,我们会往 S 中添加一个点,若这个不必要的点是在这个分支被添加的话,分析方式同理,且情况 $1$ 不会发生,如果 $ins[i]$ 和 $ins[j]$ 只有一个重合点的话,起始 $ins[i][1]$ 不会是不必要点:

image.png

综上,我们可以经过两步的“调整”,将贪心解变为最优解:第一步调整是在最优解的任意具体方案 A 中发生,通过将所有非边缘点调整为边缘点,来得到一个唯一的最优解具体方案;然后通过反证法证明,贪心解 S 中并不存在所谓的可去掉的「不必要点」,从而证明「贪心解大小必然不会大于最优解的大小」,即 $S > A$ 不成立,$S \leq A$ 恒成立,再结合 A 是最优解的前提($A \leq S$),可得 $S = A$。

代码:

###Java

class Solution {
    public int intersectionSizeTwo(int[][] ins) {
        Arrays.sort(ins, (a, b)->{
            return a[1] != b[1] ? a[1] - b[1] : b[0] - a[0];
        });
        int a = -1, b = -1, ans = 0;
        for (int[] i : ins) {
            if (i[0] > b) {
                a = i[1] - 1; b = i[1];
                ans += 2;
            } else if (i[0] > a) {
                a = b; b = i[1];
                ans++;
            }
        }
        return ans;
    }
}

###TypeScript

function intersectionSizeTwo(ins: number[][]): number {
    ins = ins.sort((a, b)=> {
        return a[1] != b[1] ? a[1] - b[1] : b[0] - a[0]
    });
    let a = -1, b = -1, ans = 0
    for (const i of ins) {
        if (i[0] > b) {
            a = i[1] - 1; b = i[1]
            ans += 2
        } else if (i[0] > a) {
            a = b; b = i[1]
            ans++
        }
    }
    return ans
};
  • 时间复杂度:$O(n\log{n})$
  • 空间复杂度:$O(\log{n})$

最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

设置交集大小至少为2【贪心】

2022年7月22日 08:07

方法一:贪心

我们对intervals进行排序,intervals[0]升序,inervals[1]降序,然后从后向前进行遍历。
为什么要一个升序一个降序呢?
假设我们有一个intervals = [[2,3],[3,4],[5,10],[5,8]] (已排好序), 只要我们满足了和[5,8]的交集大于等于2,则对于[5,10](左区间相同,右区间降序,保证在左区间相同的情况下让区间范围最小的在最右边)这个区间来说,必定是满足交集大于等于2的,因为小区间满足,大区间必然满足,反过来不一定,在左区间相同的情况下,我们取最小区间的两个元素就可以满足所有左区间相同的区间。因此我们贪心的取interval[n-1][0]interval[n-1][0] + 1做为开始的两个集合元素,设初始两个元素为curnext,则cur = intervals[n - 1][0],next = intervals[n - 1][0] + 1
然后开始分类讨论上一个区间[xi,yi]的情况,根据排序有xi <= cur

  • yi >= next ,则是一个大区间,一定满足交集为2的情况
  • yi < cur,那一定没有交集,我们还是贪心的取cur = xi,next = xi + 1
  • cur <= yi < next,有一个交集,我们贪心的取next = cur,cur = xi
    保证每次都是取左边界或者左边界和左边界+1
    image.png
    最后返回答案即可
    代码如下
class Solution {
    public int intersectionSizeTwo(int[][] intervals) {
        Arrays.sort(intervals, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);
        int n = intervals.length;
        //初始的两个元素
        int cur = intervals[n - 1][0];
        int next = intervals[n - 1][0] + 1;
        int ans = 2;
        //从后向前遍历
        for (int i = n - 2; i >= 0; i--) {
            //开始分类讨论
            if (intervals[i][1] >= next) {
                continue;
            } else if (intervals[i][1] < cur) {
                cur = intervals[i][0];
                next = intervals[i][0] + 1;
                ans = ans + 2;
            } else {
                next = cur;
                cur = intervals[i][0];
                ans++;
            }
        }
        return ans;
    }
}
class Solution:
    def intersectionSizeTwo(self, intervals: List[List[int]]) -> int:
        if not intervals:
            return 0
        intervals.sort(key = lambda x : (x[0], -x[1]))
        ans = 2
        cur, next = intervals[-1][0], intervals[-1][0]+1
        for xi, yi in reversed(intervals[:-1]):
            if yi >= next:
                continue
            elif yi < cur:
                cur = xi
                next = xi + 1
                ans += 2
            elif cur <= yi < next:
                next = cur
                cur = xi
                ans += 1
        return ans
class Solution {
public:
    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), [](vector<int>& a, vector<int>& b){
            if(a[0] == b[0]){
                return a[1] > b[1];
            }
            return a[0] < b[0];
        });  // 左边界升序,若相同,右边界降序
        int res = 2, ls = intervals.back()[0], rs = ls + 1;
        for(int i = intervals.size() - 2; i >= 0; i--){
            if(intervals[i][1] >= rs){  // 有两个及以上的交点
                continue;
            }else if(intervals[i][1] < ls){  // 没有交点
                ls = intervals[i][0];
                rs = intervals[i][0] + 1;
                res += 2;
            }else{  // 一个交点
                rs = ls;
                ls = intervals[i][0];
                res++;
            }
        }
        return res;
    }
};

感谢@fergus-peng小伙伴的py代码
感谢@handsomechar小伙伴的c++代码
image.png
给大家写了一个打印结果的代码,方便大家理解
代码如下

    public int intersectionSizeTwo(int[][] intervals) {
        Arrays.sort(intervals, (o1, o2) -> o1[0] == o2[0] ? o2[1] - o1[1] : o1[0] - o2[0]);
        System.out.println("排序后intervals:" + Arrays.deepToString(intervals));
        int n = intervals.length;
        int cur = intervals[n - 1][0];
        int next = intervals[n - 1][0] + 1;
        int ans = 2;
        List<Integer> list = new ArrayList<>();
        list.add(cur);
        list.add(next);
        for (int i = n - 2; i >= 0; i--) {
            System.out.println("待比较区间:" + Arrays.toString(intervals[i]) + "\t当前集合S:" + list);
            if (intervals[i][1] >= next) {
                continue;
            } else if (intervals[i][1] < cur) {
                cur = intervals[i][0];
                next = intervals[i][0] + 1;
                ans = ans + 2;
                list.add(0, next);
                list.add(0, cur);
            } else {
                next = cur;
                cur = intervals[i][0];
                ans++;
                list.add(0, cur);
            }
        }
        return ans;
    }

示例:

int[][] intervals = {{2, 10}, {3, 7}, {3, 15}, {4, 11}, {6, 12}, {6, 16}, {7, 8}, {7, 11}, {7, 15}, {11, 12}};

//结果
排序后intervals:[[2, 10], [3, 15], [3, 7], [4, 11], [6, 16], [6, 12], [7, 15], [7, 11], [7, 8], [11, 12]]
待比较区间:[7, 8]当前集合S:[11, 12]
待比较区间:[7, 11]当前集合S:[7, 8, 11, 12]
待比较区间:[7, 15]当前集合S:[7, 8, 11, 12]
待比较区间:[6, 12]当前集合S:[7, 8, 11, 12]
待比较区间:[6, 16]当前集合S:[7, 8, 11, 12]
待比较区间:[4, 11]当前集合S:[7, 8, 11, 12]
待比较区间:[3, 7]当前集合S:[7, 8, 11, 12]
待比较区间:[3, 15]当前集合S:[3, 7, 8, 11, 12]
待比较区间:[2, 10]当前集合S:[3, 7, 8, 11, 12]

写题解不易,如果对您有帮助,记得关注 + 点赞 + 收藏呦!!!
每天都会更新每日一题题解,大家加油!!

设置交集大小至少为2

2022年7月21日 12:00

方法一:贪心

思路与算法

首先我们从稍简化的情况开始分析:如果题目条件为设置交集大小为 $1$,为了更好的分析我们将 $\textit{intervals}$ 按照 $[s,e]$ 序对进行升序排序,其中 $\textit{intervals}$ 为题目给出的区间集合,$s,e$ 为区间的左右边界。设排序后的 $\textit{intervals} = {[s_1,e_1,],\dots,[s_n,e_n]}$,其中 $n$ 为区间集合的大小,并满足 $\forall i,j \in [1,n],i < j$ 有 $s_i \leq s_j$ 成立。然后对于最后一个区间 $[s_n,e_n]$ 来说一定是要提供一个最后交集集合中的元素,那么我们思考我们选择该区间中哪个元素是最优的——最优的元素应该尽可能的把之前的区间尽可能的覆盖。那么我们选择该区间的开头元素 $s_n$ 一定是最优的,因为对于前面的某一个区间 $[s_i,s_j]$:

  • 如果 $s_j < s_n$:那么无论选择最后一个区间中的哪个数字都不会在区间 $[s_i,s_j]$ 中。
  • 否则 $s_j \geq s_n$:因为 $s_n \geq s_i$ 所以此时选择 $s_n$ 一定会在区间 $[s_i,s_j]$ 中。

即对于最后一个区间 $[s_n,e_n]$ 来说选择区间的开头元素 $s_n$ 一定是最优的。那么贪心的思路就出来了:排序后从后往前进行遍历,每次选取与当前交集集合相交为空的区间的最左边的元素即可,然后往前判断前面是否有区间能因此产生交集,产生交集的直接跳过即可。此时算法的时间复杂度为:$O(n \log n)$ 主要为排序的时间复杂度。对于这种情况具体也可以见本站题目 452. 用最少数量的箭引爆气球

那么我们用同样的思路来扩展到需要交集为 $m~,~m > 1$ 的情况:此时同样首先对 $\textit{intervals}$ 按照 $[s,e]$ 序对进行升序排序,然后我们需要额外记录每一个区间与最后交集集合中相交的元素(只记录到 $m$ 个为止)。同样我们从最后一个区间往前进行处理,如果该区间与交集集合相交元素个数小于 $m$ 个时,我们从该区间左边界开始往后添加不在交集集合中的元素,并往前进行更新需要更新的区间,重复该过程直至该区间与交集元素集合有 $m$ 个元素相交。到此就可以解决问题了,不过我们也可以来修改排序的规则——我们将区间 $[s,e]$ 序对按照 $s$ 升序,当 $s$ 相同时按照 $e$ 降序来进行排序,这样可以实现在处理与交集集合相交元素个数小于 $m$ 个的区间 $[s_i,e_i]$ 时,保证不足的元素都是在区间的开始部分,即我们可以直接从区间开始部分进行往交集集合中添加元素。

而对于本题来说,我们只需要取 $m = 2$ 的情况即可。

代码

###Python

class Solution:
    def intersectionSizeTwo(self, intervals: List[List[int]]) -> int:
        intervals.sort(key=lambda x: (x[0], -x[1]))
        ans, n, m = 0, len(intervals), 2
        vals = [[] for _ in range(n)]
        for i in range(n - 1, -1, -1):
            j = intervals[i][0]
            for k in range(len(vals[i]), m):
                ans += 1
                for p in range(i - 1, -1, -1):
                    if intervals[p][1] < j:
                        break
                    vals[p].append(j)
                j += 1
        return ans

###C++

class Solution {
public:
    void help(vector<vector<int>>& intervals, vector<vector<int>>& temp, int pos, int num) {
        for (int i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].push_back(num);
        }
    }

    int intersectionSizeTwo(vector<vector<int>>& intervals) {
        int n = intervals.size();
        int res = 0;
        int m = 2;
        sort(intervals.begin(), intervals.end(), [&](vector<int>& a, vector<int>& b) {
            if (a[0] == b[0]) {
                return a[1] > b[1];
            }
            return a[0] < b[0];
        });
        vector<vector<int>> temp(n);
        for (int i = n - 1; i >= 0; i--) {
            for (int j = intervals[i][0], k = temp[i].size(); k < m; j++, k++) {
                res++;
                help(intervals, temp, i - 1, j);
            }
        }
        return res;
    }
};

###Java

class Solution {
    public int intersectionSizeTwo(int[][] intervals) {
        int n = intervals.length;
        int res = 0;
        int m = 2;
        Arrays.sort(intervals, (a, b) -> {
            if (a[0] == b[0]) {
                return b[1] - a[1];
            }
            return a[0] - b[0];
        });
        List<Integer>[] temp = new List[n];
        for (int i = 0; i < n; i++) {
            temp[i] = new ArrayList<Integer>();
        }
        for (int i = n - 1; i >= 0; i--) {
            for (int j = intervals[i][0], k = temp[i].size(); k < m; j++, k++) {
                res++;
                help(intervals, temp, i - 1, j);
            }
        }
        return res;
    }

    public void help(int[][] intervals, List<Integer>[] temp, int pos, int num) {
        for (int i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].add(num);
        }
    }
}

###C#

public class Solution {
    public int IntersectionSizeTwo(int[][] intervals) {
        int n = intervals.Length;
        int res = 0;
        int m = 2;
        Array.Sort(intervals, (a, b) => {
            if (a[0] == b[0]) {
                return b[1] - a[1];
            }
            return a[0] - b[0];
        });
        IList<int>[] temp = new IList<int>[n];
        for (int i = 0; i < n; i++) {
            temp[i] = new List<int>();
        }
        for (int i = n - 1; i >= 0; i--) {
            for (int j = intervals[i][0], k = temp[i].Count; k < m; j++, k++) {
                res++;
                Help(intervals, temp, i - 1, j);
            }
        }
        return res;
    }

    public void Help(int[][] intervals, IList<int>[] temp, int pos, int num) {
        for (int i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].Add(num);
        }
    }
}

###C

static void help(int** intervals, int** temp, int *colSize, int pos, int num) {
    for (int i = pos; i >= 0; i --) {
        if (intervals[i][1] < num) {
            break;
        }
        temp[i][colSize[i]++] = num;
    }
}

static inline int cmp(const void* pa, const void* pb) {
    if ((*(int **)pa)[0] == (*(int **)pb)[0]) {
        return (*(int **)pb)[1] - (*(int **)pa)[1];
    }
    return (*(int **)pa)[0] - (*(int **)pb)[0];
}

int intersectionSizeTwo(int** intervals, int intervalsSize, int* intervalsColSize){
    int res = 0;
    int m = 2;
    qsort(intervals, intervalsSize, sizeof(int *), cmp);
    int **temp = (int **)malloc(sizeof(int *) * intervalsSize);
    for (int i = 0; i < intervalsSize; i++) {
        temp[i] = (int *)malloc(sizeof(int) * 2);
    }
    int *colSize = (int *)malloc(sizeof(int) * intervalsSize);
    memset(colSize, 0, sizeof(int) * intervalsSize);
    for (int i = intervalsSize - 1; i >= 0; i --) {
        for (int j = intervals[i][0], k = colSize[i]; k < m; j++, k++) {
            res++;
            help(intervals, temp, colSize, i - 1, j);
        }
    }
    for (int i = 0; i < intervalsSize; i++) {
        free(temp[i]);
    }
    free(colSize);
    return res;
}

###go

func intersectionSizeTwo(intervals [][]int) (ans int) {
    sort.Slice(intervals, func(i, j int) bool {
        a, b := intervals[i], intervals[j]
        return a[0] < b[0] || a[0] == b[0] && a[1] > b[1]
    })
    n, m := len(intervals), 2
    vals := make([][]int, n)
    for i := n - 1; i >= 0; i-- {
        for j, k := intervals[i][0], len(vals[i]); k < m; k++ {
            ans++
            for p := i - 1; p >= 0 && intervals[p][1] >= j; p-- {
                vals[p] = append(vals[p], j)
            }
            j++
        }
    }
    return
}

###JavaScript

var intersectionSizeTwo = function(intervals) {
    const n = intervals.length;
    let res = 0;
    let m = 2;
    intervals.sort((a, b) => {
        if (a[0] === b[0]) {
            return b[1] - a[1];
        }
        return a[0] - b[0];
    });
    const temp = new Array(n).fill(0);
    for (let i = 0; i < n; i++) {
        temp[i] = [];
    }

    const help = (intervals, temp, pos, num) => {
        for (let i = pos; i >= 0; i--) {
            if (intervals[i][1] < num) {
                break;
            }
            temp[i].push(num);
        }
    }

    for (let i = n - 1; i >= 0; i--) {
        for (let j = intervals[i][0], k = temp[i].length; k < m; j++, k++) {
            res++;
            help(intervals, temp, i - 1, j);
        }
    }
    return res;
};

复杂度分析

  • 时间复杂度:$O(n \log n + nm)$,其中 $n$ 为给定区间集合 $\textit{intervals}$ 的大小,$m$ 为设置交集大小,本题为 $2$。

  • 空间复杂度:$O(nm)$,其中 $n$ 为给定区间集合 $\textit{intervals}$ 的大小,$m$ 为设置交集的大小,本题为 $2$。主要开销为存储每一个区间与交集集合的相交的元素的开销。

昨天 — 2025年11月19日LeetCode 每日一题题解

将找到的值乘以 2

2022年2月7日 10:28

方法一:排序

思路与算法

如果我们不对数组 $\textit{nums}$ 进行任何操作,那么每次更新 $\textit{original}$ 后,都需要 $O(n)$ 的时间完整遍历一遍。最终时间复杂度为 $O(n^2)$。

我们可以对这一过程进行优化。具体而言,每次在数组中找到 $\textit{original}$ 后,$\textit{original}$ 的数值都会比更新前更大,因此我们可以先将数组 $\textit{nums}$ 升序排序,这样每次更新后的 $\textit{original}$ 数值在数组中的位置(如有)只可能位于更新前的后面,我们只需要一边从左至右遍历排序后的 $\textit{nums}$ 数组一边尝试更新 $\textit{original}$ 即可。

代码

###C++

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        sort(nums.begin(), nums.end());
        for (int num: nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
};

###Python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        nums.sort()
        for num in nums:
            if num == original:
                original *= 2
        return original

###Java

class Solution {
    public int findFinalValue(int[] nums, int original) {
        Arrays.sort(nums);
        for (int num : nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
}

###C#

public class Solution {
    public int FindFinalValue(int[] nums, int original) {
        Array.Sort(nums);
        foreach (int num in nums) {
            if (original == num) {
                original *= 2;
            }
        }
        return original;
    }
}

###Go

func findFinalValue(nums []int, original int) int {
    sort.Ints(nums)
    for _, num := range nums {
        if original == num {
            original *= 2
        }
    }
    return original
}

###C

int cmp(const void* a, const void* b) {
    return (*(int*)a - *(int*)b);
}

int findFinalValue(int* nums, int numsSize, int original) {
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize; i++) {
        if (original == nums[i]) {
            original *= 2;
        }
    }
    return original;
}

###JavaScript

var findFinalValue = function(nums, original) {
    nums.sort((a, b) => a - b);
    for (const num of nums) {
        if (original === num) {
            original *= 2;
        }
    }
    return original;
};

###TypeScript

function findFinalValue(nums: number[], original: number): number {
    nums.sort((a, b) => a - b);
    for (const num of nums) {
        if (original === num) {
            original *= 2;
        }
    }
    return original;
}

###Rust

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let mut nums = nums;
        nums.sort();
        let mut original = original;
        for &num in nums.iter() {
            if original == num {
                original *= 2;
            }
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 为 $\textit{nums}$ 的长度。排序的时间复杂度为 $O(n \log n)$,遍历更新 $\textit{original}$ 的时间复杂度最多为 $O(n)$。

  • 空间复杂度:$O(\log n)$,即为排序的栈空间开销。

方法二:哈希表

思路与算法

我们还可以采用更加直接地利用空间换取时间的方法:利用哈希集合存储数组 $\textit{nums}$ 中的元素,然后我们只需要每次判断 $\textit{original}$ 是否位于该哈希集合中即可。具体地:

  • 如果 $\textit{original}$ 位于哈希集合中,我们将 $\textit{original}$ 乘以 $2$,然后再次判断;

  • 如果 $\textit{original}$ 不位于哈希集合中,那么循环结束,我们返回当前的 $\textit{original}$ 作为答案。

代码

###C++

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> s(nums.begin(), nums.end());
        while (s.count(original)) {
            original *= 2;
        }
        return original;
    }
};

###Python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        s = set(nums)
        while original in s:
            original *= 2
        return original

###Java

class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
        while (set.contains(original)) {
            original *= 2;
        }
        return original;
    }
}

###C#

public class Solution {
    public int FindFinalValue(int[] nums, int original) {
        HashSet<int> set = new HashSet<int>(nums);
        while (set.Contains(original)) {
            original *= 2;
        }
        return original;
    }
}

###Go

func findFinalValue(nums []int, original int) int {
    set := make(map[int]bool)
    for _, num := range nums {
        set[num] = true
    }
    for set[original] {
        original *= 2
    }
    return original
}

###C

typedef struct {
    int key;
    UT_hash_handle hh;
} HashItem; 

HashItem *hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}

bool hashAddItem(HashItem **obj, int key) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem *)malloc(sizeof(HashItem));
    pEntry->key = key;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}

void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);  
        free(curr);
    }
}

int findFinalValue(int* nums, int numsSize, int original) {
    HashItem *set = NULL;
    for (int i = 0; i < numsSize; i++) {
        hashAddItem(&set, nums[i]);
    }
    while (hashFindItem(&set, original)) {
        original *= 2;
    }
    hashFree(&set);
    return original;
}

###JavaScript

var findFinalValue = function(nums, original) {
    const set = new Set(nums);
    while (set.has(original)) {
        original *= 2;
    }
    return original;
};

###TypeScript

function findFinalValue(nums: number[], original: number): number {
    const set = new Set(nums);
    while (set.has(original)) {
        original *= 2;
    }
    return original;
}

###Rust

use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let set: HashSet<_> = nums.into_iter().collect();
        let mut original = original;
        while set.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为 $\textit{nums}$ 的长度。遍历数组维护元素哈希集合的时间复杂度为 $O(n)$,遍历更新 $\textit{original}$ 的时间复杂度最多为 $O(n)$。

  • 空间复杂度:$O(n)$,即为元素哈希集合的空间开销。

[Python3/Java/C++/Go/TypeScript] 一题一解:哈希表(清晰题解)

作者 lcbin
2025年11月19日 07:38

方法一:哈希表

我们用一个哈希表 $\textit{s}$ 记录数组 $\textit{nums}$ 中的所有数字。

接下来,我们从 $\textit{original}$ 开始,如果 $\textit{original}$ 在 $\textit{s}$ 中,我们将 $\textit{original}$ 乘以 $2$,直到 $\textit{original}$ 不在 $\textit{s}$ 中,返回 $\textit{original}$。

###python

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        s = set(nums)
        while original in s:
            original <<= 1
        return original

###java

class Solution {

    public int findFinalValue(int[] nums, int original) {
        Set<Integer> s = new HashSet<>();
        for (int num : nums) {
            s.add(num);
        }
        while (s.contains(original)) {
            original <<= 1;
        }
        return original;
    }
}

###cpp

class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> s(nums.begin(), nums.end());
        while (s.contains(original)) {
            original <<= 1;
        }
        return original;
    }
};

###go

func findFinalValue(nums []int, original int) int {
s := map[int]bool{}
for _, x := range nums {
s[x] = true
}
for s[original] {
original <<= 1
}
return original
}

###ts

function findFinalValue(nums: number[], original: number): number {
    const s: Set<number> = new Set([...nums]);
    while (s.has(original)) {
        original <<= 1;
    }
    return original;
}

###rust

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        use std::collections::HashSet;
        let s: HashSet<i32> = nums.into_iter().collect();
        let mut original = original;
        while s.contains(&original) {
            original <<= 1;
        }
        original
    }
}

时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-将找到的值乘以 2🟢

2025年11月19日 00:00

给你一个整数数组 nums ,另给你一个整数 original ,这是需要在 nums 中搜索的第一个数字。

接下来,你需要按下述步骤操作:

  1. 如果在 nums 中找到 original ,将 original 乘以 2 ,得到新 original(即,令 original = 2 * original)。
  2. 否则,停止这一过程。
  3. 只要能在数组中找到新 original ,就对新 original 继续 重复 这一过程

返回 original最终 值。

 

示例 1:

输入:nums = [5,3,6,1,12], original = 3
输出:24
解释: 
- 3 能在 nums 中找到。3 * 2 = 6 。
- 6 能在 nums 中找到。6 * 2 = 12 。
- 12 能在 nums 中找到。12 * 2 = 24 。
- 24 不能在 nums 中找到。因此,返回 24 。

示例 2:

输入:nums = [2,7,9], original = 4
输出:4
解释:
- 4 不能在 nums 中找到。因此,返回 4 。

 

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i], original <= 1000

【track & traning】思路简单,性能高效接近100

作者 uint32
2022年3月30日 23:11

方便快速学习算法与理解~

🌇 点赞 👍 收藏 ⭐留言 📝 一键三连 ~关注Jam,从你我做起!

兄弟会背叛你,女人会离开你,金钱会诱惑你,生活会刁难你,只有数学不会,不会就是不会
天才与否,取决于最终达到的高度。真正的天才是那些脚踏实地的人
静下心来好好做自己,走稳脚下每一步,就是最好的路,强者都是孤独的

推荐 python 算法的书籍,体系化学习算法与数据结构,用正确的方式成为offer收割机
leetcode —— 系统化快速学习算法,这不是内卷,这只是悄悄地努力,然后惊艳所有的人
image.png


求解思路

暴力破解

代码

###python3

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        for _ in range(len(nums)+1):
            if original in set(nums):
                original *= 2
            else:
                return original

巧妙利用位运算优化空间复杂度:从 O(n) 到 O(log U) 到 O(1)

作者 endlesscheng
2022年1月30日 12:08

题意

返回不在 $\textit{nums}$ 中的最小的 $\textit{original}\cdot 2^k$,其中 $k$ 是非负整数。

方法一:枚举

枚举 $k=0,1,2,\ldots$ 判断 $\textit{original}\cdot 2^k$ 是否在 $\textit{nums}$ 中。

用哈希集合记录 $\textit{nums}$ 的每个元素可以加快判断。

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        st = set(nums)
        while original in st:
            original *= 2
        return original
class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> st = new HashSet<>();
        for (int x : nums) {
            st.add(x);
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> st(nums.begin(), nums.end());
        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
};
func findFinalValue(nums []int, original int) int {
has := map[int]bool{}
for _, x := range nums {
has[x] = true
}

for has[original] {
original *= 2
}
return original
}
var findFinalValue = function(nums, original) {
    const st = new Set(nums);
    while (st.has(original)) {
        original *= 2;
    }
    return original;
};
use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, mut original: i32) -> i32 {
        let st = nums.into_iter().collect::<HashSet<_>>();
        while st.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}\left(n+\log\dfrac{U}{\textit{original}}\right)$,其中 $n$ 是 $\textit{nums}$ 的长度,$U=\max(\textit{nums})$。循环次数为 $\mathcal{O}\left(\log\dfrac{U}{\textit{original}}\right)$。
  • 空间复杂度:$\mathcal{O}(n)$。

方法二:只记录所有可能值

哈希集合记录的元素可以更少,只需要记录符合 $\textit{original}\cdot 2^k$ 的元素。

设 $x = \textit{nums}[i]$,如果 $x = \textit{original}\cdot 2^k$,那么:

  • $x$ 是 $\textit{original}$ 的倍数。
  • $\dfrac{x}{\textit{original}}$ 是 2 的幂,见 我的题解
class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        st = set()
        for x in nums:
            k, r = divmod(x, original)
            if r == 0 and k & (k - 1) == 0:
                st.add(x)

        while original in st:
            original *= 2
        return original
class Solution {
    public int findFinalValue(int[] nums, int original) {
        Set<Integer> st = new HashSet<>();
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                st.add(x);
            }
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        unordered_set<int> st;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                st.insert(x);
            }
        }

        while (st.contains(original)) {
            original *= 2;
        }
        return original;
    }
};
func findFinalValue(nums []int, original int) int {
has := map[int]bool{}
for _, x := range nums {
k := x / original
if x%original == 0 && k&(k-1) == 0 {
has[x] = true
}
}
for has[original] {
original *= 2
}
return original
}
var findFinalValue = function(nums, original) {
    const st = new Set();
    for (const x of nums) {
        const k = x / original;
        if (x % original === 0 && (k & (k - 1)) === 0) {
            st.add(x);
        }
    }

    while (st.has(original)) {
        original *= 2;
    }
    return original;
};
use std::collections::HashSet;

impl Solution {
    pub fn find_final_value(nums: Vec<i32>, mut original: i32) -> i32 {
        let st = nums.into_iter()
            .filter(|x| x % original == 0 && ((x / original) & (x / original - 1)) == 0)
            .collect::<HashSet<_>>();
        while st.contains(&original) {
            original *= 2;
        }
        original
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}\left(n+\log\dfrac{U}{\textit{original}}\right)$,其中 $n$ 是 $\textit{nums}$ 的长度,$U=\max(\textit{nums})$。
  • 空间复杂度:$\mathcal{O}\left(\log\dfrac{U}{\textit{original}}\right)$。

方法三:位运算

改成记录 $\textit{original}\cdot 2^k$ 中的 $k$。

由于 $k$ 很小,我们可以把 $k$ 保存到一个二进制数 $\textit{mask}$ 中,具体请看 从集合论到位运算,常见位运算技巧分类总结!

答案中的 $k$ 就是 $\textit{mask}$ 的最低 $0$ 的位置。

这可以通过位运算 $\mathcal{O}(1)$ 地计算出来:把 $\textit{mask}$ 取反,找最低位的 $1$,其对应的二进制数 $\textit{lowbit}$ 即为答案中的 $2^k$。再乘以 $\textit{original}$,得到最终答案。

:找最低 $0$ 对应的 $2^k$,也可以直接计算 (mask + 1) & ~mask

class Solution:
    def findFinalValue(self, nums: List[int], original: int) -> int:
        mask = 0
        for x in nums:
            k, r = divmod(x, original)
            if r == 0 and k & (k - 1) == 0:
                mask |= k

        # 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask
        return original * (mask & -mask)
class Solution {
    public int findFinalValue(int[] nums, int original) {
        int mask = 0;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask;
        return original * (mask & -mask);
    }
}
class Solution {
public:
    int findFinalValue(vector<int>& nums, int original) {
        int mask = 0;
        for (int x : nums) {
            int k = x / original;
            if (x % original == 0 && (k & (k - 1)) == 0) {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = ~mask;
        return original * (mask & -mask);
    }
};
func findFinalValue(nums []int, original int) int {
mask := 0
for _, x := range nums {
k := x / original
if x%original == 0 && k&(k-1) == 0 {
mask |= k
}
}

// 找最低的 0,等价于取反后,找最低的 1(lowbit)
mask = ^mask
return original * (mask & -mask)
}
var findFinalValue = function(nums, original) {
    let mask = 0;
    for (const x of nums) {
        const k = x / original;
        if (x % original === 0 && (k & (k - 1)) === 0) {
            mask |= k;
        }
    }

    // 找最低的 0,等价于取反后,找最低的 1(lowbit)
    mask = ~mask;
    return original * (mask & -mask);
};
impl Solution {
    pub fn find_final_value(nums: Vec<i32>, original: i32) -> i32 {
        let mut mask = 0;
        for x in nums {
            let k = x / original;
            if x % original == 0 && (k & (k - 1)) == 0 {
                mask |= k;
            }
        }

        // 找最低的 0,等价于取反后,找最低的 1(lowbit)
        mask = !mask;
        original * (mask & -mask)
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

相似题目

2568. 最小无法得到的或值

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

昨天以前LeetCode 每日一题题解

题意解读,简洁写法(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2025年11月18日 07:24

题意

有一个包含 $\texttt{a}$ 和 $\texttt{b}$ 的字符串 $s$,把其中的 $\texttt{a}$ 替换成 $0$,$\texttt{b}$ 替换成 $1,0$,得到一个 $01$ 序列 $\textit{bits}$。

你需要把 $\textit{bits}$ 还原回字符串 $s$,判断 $s$ 的最后一个字符是不是 $\texttt{a}$。

思路

根据题意,两种字符替换后的第一个数字一定是不同的,一个是 $0$,另一个是 $1$。

换句话说,我们可以通过 $\textit{bits}[0]$ 确定字符串的第一个字符:

  • 如果 $\textit{bits}[0]=0$,那么是 $\texttt{a}$,把 $\textit{bits}[0]$ 去掉。
  • 如果 $\textit{bits}[0]=1$,那么是 $\texttt{b}$,把 $\textit{bits}[0]$ 和 $\textit{bits}[1]$ 去掉。

重复该过程,直到 $\textit{bits}$ 剩下至多一个数字。

分类讨论:

  • 如果最后剩下一个数字,由于题目保证 $\textit{bits}[n-1] = 0$,所以字符串的最后一个字符是 $\texttt{a}$,返回 $\texttt{true}$。
  • 如果最后没有剩下数字,说明字符串的最后一个字符是 $\texttt{b}$,返回 $\texttt{false}$。

示例 1 是 $[1,0] + [0]$,满足要求。

示例 2 是 $[1,1] + [1,0]$,不满足要求。

class Solution:
    def isOneBitCharacter(self, bits: List[int]) -> bool:
        n = len(bits)
        i = 0
        while i < n - 1:  # 循环直到剩下至多一个数字
            i += bits[i] + 1  # 如果 bits[i] == 1 则跳过下一位
        return i == n - 1  # 注意题目保证 bits[n-1] == 0,无需判断
class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int n = bits.length;
        int i = 0;
        while (i < n - 1) { // 循环直到剩下至多一个数字
            i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
        }
        return i == n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
    }
}
class Solution {
public:
    bool isOneBitCharacter(vector<int>& bits) {
        int n = bits.size();
        int i = 0;
        while (i < n - 1) { // 循环直到剩下至多一个数字
            i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
        }
        return i == n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
    }
};
bool isOneBitCharacter(int* bits, int bitsSize) {
    int i = 0;
    while (i < bitsSize - 1) { // 循环直到剩下至多一个数字
        i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
    }
    return i == bitsSize - 1; // 注意题目保证 bits[n-1] == 0,无需判断
}
func isOneBitCharacter(bits []int) bool {
n := len(bits)
i := 0
for i < n-1 { // 循环直到剩下至多一个数字
i += bits[i] + 1 // 如果 bits[i] == 1 则跳过下一位
}
return i == n-1 // 注意题目保证 bits[n-1] == 0,无需判断
}
var isOneBitCharacter = function(bits) {
    const n = bits.length;
    let i = 0;
    while (i < n - 1) { // 循环直到剩下至多一个数字
        i += bits[i] + 1; // 如果 bits[i] == 1 则跳过下一位
    }
    return i === n - 1; // 注意题目保证 bits[n-1] == 0,无需判断
};
impl Solution {
    pub fn is_one_bit_character(bits: Vec<i32>) -> bool {
        let n = bits.len();
        let mut i = 0;
        while i < n - 1 { // 循环直到剩下至多一个数字
            i += (bits[i] + 1) as usize; // 如果 bits[i] == 1 则跳过下一位
        }
        i == n - 1 // 注意题目保证 bits[n-1] == 0,无需判断
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{bits}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

每日一题-1 比特与 2 比特字符🟢

2025年11月18日 00:00

有两种特殊字符:

  • 第一种字符可以用一比特 0 表示
  • 第二种字符可以用两比特(10 或 11)表示

给你一个以 0 结尾的二进制数组 bits ,如果最后一个字符必须是一个一比特字符,则返回 true

 

示例 1:

输入: bits = [1, 0, 0]
输出: true
解释: 唯一的解码方式是将其解析为一个两比特字符和一个一比特字符。
所以最后一个字符是一比特字符。

示例 2:

输入:bits = [1,1,1,0]
输出:false
解释:唯一的解码方式是将其解析为两比特字符和两比特字符。
所以最后一个字符不是一比特字符。

 

提示:

  • 1 <= bits.length <= 1000
  • bits[i]01

【负雪明烛】图解算法:走一步 or 走两步

作者 fuxuemingzhu
2022年2月20日 09:55

大家好,我是 @负雪明烛。点击右上方的「+关注,优质题解不间断!

题目大意

有两种字符,一种是 0,一种是10或者11

给出了一个由这两种字符组成的数组,判断最后一位的数字是否一定是单个的 0

解题方法

遍历

有两种字符串,一种是 0,一种是 1011。即一种长度是1,一种长度是2.

所以找个指针然后遍历一遍:

  • 遇到 0 走一步;
  • 遇到 1走两步。

题目告诉了数组的最后一个元素一定是 0,所以最后如果恰好到达len-1,说明最后一个数字的长度为 1 ,也就是 0,就满足题意了。

717. 1比特与2比特字符.001.png

代码如下:

###Java

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int N = bits.length;
        int pos = 0;
        while (pos < N - 1) {
            pos += bits[pos] == 1 ? 2 : 1;
        }
        return pos == N - 1;
    }
}

###Python

class Solution(object):
    def isOneBitCharacter(self, bits):
        """
        :type bits: List[int]
        :rtype: bool
        """
        pos = 0
        while pos < len(bits) - 1:
            pos += 2 if bits[pos] == 1 else 1
        return pos == len(bits) - 1

###C++

class Solution {
public:
    bool isOneBitCharacter(vector<int>& bits) {
        const int N = bits.size();
        int pos = 0;
        while (pos < N - 1) {
            pos += bits[pos] == 1 ? 2 : 1;
        }
        return pos == N - 1;
    }
};

时间复杂度

  • 时间复杂度:$O(N)$,其中 $N$ 是数组长度;
  • 空间复杂度:$O(1)$。

总结

  1. 单指针根据题目要求进行移动,最重要的还是理解题意哇!!

image.png


我是 @负雪明烛 ,刷算法题 1000 多道,写了 1000 多篇算法题解,收获阅读量 300 万。
关注我,你将不会错过我的精彩动画题解、面试题分享、组队刷题活动,进入主页 @负雪明烛 右侧有刷题组织,从此刷题不再孤单。

【宫水三叶】简单模拟题

作者 AC_OIer
2022年2月20日 08:39

模拟

根据题意进行模拟即可,使用 $n$ 代指 bits 的长度,$idx$ 为当前「比特字」的开头,从前往后扫描每个「比特字」,如果最后一个「比特字」的开头为 $n - 1$ 返回 True,否则返回 False

代码:

###Java

class Solution {
    public boolean isOneBitCharacter(int[] bits) {
        int n = bits.length, idx = 0;
        while (idx < n - 1) {
            if (bits[idx] == 0) idx++;
            else idx += 2;
        }
        return idx == n - 1;
    }
}
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$

加练「滑动窗口/模拟」内容

题太简单?不如来学习热乎的 一道结合众多知识点的滑窗综合题 🎉🎉

考虑加练如下「模拟」相关内容 🍭🍭🍭

题目 题解 难度 推荐指数
2. 两数相加 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
5. 最长回文子串 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
6. Z 字形变换 LeetCode 题解链接 中等 🤩🤩🤩
8. 字符串转换整数 (atoi) LeetCode 题解链接 中等 🤩🤩🤩
12. 整数转罗马数字 LeetCode 题解链接 中等 🤩🤩
31. 下一个排列 LeetCode 题解链接 中等 🤩🤩🤩
43. 字符串相乘 LeetCode 题解链接 中等 🤩🤩🤩🤩
58. 最后一个单词的长度 LeetCode 题解链接 中等 🤩🤩🤩🤩
59. 螺旋矩阵 II LeetCode 题解链接 中等 🤩🤩🤩🤩
68. 文本左右对齐 LeetCode 题解链接 困难 🤩🤩🤩
71. 简化路径 LeetCode 题解链接 中等 🤩🤩🤩🤩
73. 矩阵置零 LeetCode 题解链接 中等 🤩🤩🤩🤩
89. 格雷编码 LeetCode 题解链接 中等 🤩🤩🤩🤩
165. 比较版本号 LeetCode 题解链接 中等 🤩🤩🤩🤩
166. 分数到小数 LeetCode 题解链接 中等 🤩🤩🤩🤩
233. 数字 1 的个数 LeetCode 题解链接 困难 🤩🤩🤩🤩
260. 只出现一次的数字 III LeetCode 题解链接 中等 🤩🤩🤩🤩
273. 整数转换英文表示 LeetCode 题解链接 困难 🤩🤩🤩🤩
284. 顶端迭代器 LeetCode 题解链接 中等 🤩🤩🤩🤩
299. 猜数字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
335. 路径交叉 LeetCode 题解链接 困难 🤩🤩🤩🤩
382. 链表随机节点 LeetCode 题解链接 中等 🤩🤩🤩🤩🤩
400. 第 N 位数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
413. 等差数列划分 LeetCode 题解链接 中等 🤩🤩🤩🤩
414. 第三大的数 LeetCode 题解链接 中等 🤩🤩🤩🤩
419. 甲板上的战舰 LeetCode 题解链接 中等 🤩🤩🤩🤩
423. 从英文中重建数字 LeetCode 题解链接 中等 🤩🤩🤩🤩
443. 压缩字符串 LeetCode 题解链接 中等 🤩🤩🤩🤩
451. 根据字符出现频率排序 LeetCode 题解链接 中等 🤩🤩🤩🤩
457. 环形数组是否存在循环 LeetCode 题解链接 中等 🤩🤩🤩🤩
528. 按权重随机选择 LeetCode 题解链接 中等 🤩🤩🤩🤩
539. 最小时间差 LeetCode 题解链接 中等 🤩🤩🤩🤩
726. 原子的数量 LeetCode 题解链接 困难 🤩🤩🤩🤩
794. 有效的井字游戏 LeetCode 题解链接 中等 🤩🤩🤩🤩
846. 一手顺子 LeetCode 题解链接 中等 🤩🤩🤩
859. 亲密字符串 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1001. 网格照明 LeetCode 题解链接 困难 🤩🤩🤩🤩
1005. K 次取反后最大化的数组和 LeetCode 题解链接 简单 🤩🤩🤩🤩
1436. 旅行终点站 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1446. 连续字符 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1480. 一维数组的动态和 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1583. 统计不开心的朋友 LeetCode 题解链接 中等 🤩🤩🤩🤩
1614. 括号的最大嵌套深度 LeetCode 题解链接 简单 🤩🤩🤩🤩🤩
1743. 从相邻元素对还原数组 LeetCode 题解链接 中等 🤩🤩🤩🤩
1834. 单线程 CPU LeetCode 题解链接 中等 🤩🤩🤩🤩
1894. 找到需要补充粉笔的学生编号 LeetCode 题解链接 中等 🤩🤩🤩🤩
面试题 10.02. 变位词组 LeetCode 题解链接 中等 🤩🤩🤩🤩

注:以上目录整理来自 wiki,任何形式的转载引用请保留出处。


最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

[Python3/Java/C++/Go/TypeScript] 一题一解:模拟(清晰题解)

作者 lcbin
2025年11月17日 07:40

方法一:模拟

我们可以遍历数组 $\textit{nums}$,用变量 $j$ 记录上一个 $1$ 的下标,那么当前位置 $i$ 的元素为 $1$ 时,只需要判断 $i - j - 1$ 是否小于 $k$ 即可。如果小于 $k$,则说明存在两个 $1$ 之间的 $0$ 的个数小于 $k$,返回 $\text{false}$;否则,将 $j$ 更新为 $i$,继续遍历数组。

遍历结束后,返回 $\text{true}$。

###python

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        j = -inf
        for i, x in enumerate(nums):
            if x:
                if i - j - 1 < k:
                    return False
                j = i
        return True

###java

class Solution {
    public boolean kLengthApart(int[] nums, int k) {
        int j = -(k + 1);
        for (int i = 0; i < nums.length; ++i) {
            if (nums[i] == 1) {
                if (i - j - 1 < k) {
                    return false;
                }
                j = i;
            }
        }
        return true;
    }
}

###cpp

class Solution {
public:
    bool kLengthApart(vector<int>& nums, int k) {
        int j = -(k + 1);
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] == 1) {
                if (i - j - 1 < k) {
                    return false;
                }
                j = i;
            }
        }
        return true;
    }
};

###go

func kLengthApart(nums []int, k int) bool {
j := -(k + 1)
for i, x := range nums {
if x == 1 {
if i-j-1 < k {
return false
}
j = i
}
}
return true
}

###ts

function kLengthApart(nums: number[], k: number): boolean {
    let j = -(k + 1);
    for (let i = 0; i < nums.length; ++i) {
        if (nums[i] === 1) {
            if (i - j - 1 < k) {
                return false;
            }
            j = i;
        }
    }
    return true;
}

###rust

impl Solution {
    pub fn k_length_apart(nums: Vec<i32>, k: i32) -> bool {
        let mut j = -(k + 1);
        for (i, &x) in nums.iter().enumerate() {
            if x == 1 {
                if (i as i32) - j - 1 < k {
                    return false;
                }
                j = i as i32;
            }
        }
        true
    }
}

时间复杂度 $O(n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。空间复杂度 $O(1)$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

每日一题-是否所有 1 都至少相隔 k 个元素🟢

2025年11月17日 00:00

给你一个由若干 01 组成的数组 nums 以及整数 k。如果所有 1 都至少相隔 k 个元素,则返回 true ;否则,返回 false

 

示例 1:

输入:nums = [1,0,0,0,1,0,0,1], k = 2
输出:true
解释:每个 1 都至少相隔 2 个元素。

示例 2:

输入:nums = [1,0,0,1,0,1], k = 2
输出:false
解释:第二个 1 和第三个 1 之间只隔了 1 个元素。

 

提示:

  • 1 <= nums.length <= 105
  • 0 <= k <= nums.length
  • nums[i] 的值为 01

简单题,简单做(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2025年11月7日 10:39

如果所有相邻的 $1$ 都至少相隔 $k$ 个元素,那么所有 $1$ 都至少相隔 $k$ 个元素。

所以只需检查相邻的 $1$。

记录上一个 $1$ 的位置 $\textit{last}_1$。

如果 $\textit{nums}[i] = 1$ 且 $i - \textit{last}_1 - 1 < k$,即 $i - \textit{last}_1 \le k$,则不满足要求。

为了兼容 $\textit{nums}$ 的第一个 $1$,可以初始化 $\textit{last}_1 = -k-1$。

###py

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        last1 = -inf
        for i, x in enumerate(nums):
            if x != 1:
                continue
            if i - last1 <= k:
                return False
            last1 = i
        return True

###java

class Solution {
    public boolean kLengthApart(int[] nums, int k) {
        int last1 = -k - 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != 1) {
                continue;
            }
            if (i - last1 <= k) {
                return false;
            }
            last1 = i;
        }
        return true;
    }
}

###cpp

class Solution {
public:
    bool kLengthApart(vector<int>& nums, int k) {
        int last1 = -k - 1;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] != 1) {
                continue;
            }
            if (i - last1 <= k) {
                return false;
            }
            last1 = i;
        }
        return true;
    }
};

###c

bool kLengthApart(int* nums, int numsSize, int k) {
    int last1 = -k - 1;
    for (int i = 0; i < numsSize; i++) {
        if (nums[i] != 1) {
            continue;
        }
        if (i - last1 <= k) {
            return false;
        }
        last1 = i;
    }
    return true;
}

###go

func kLengthApart(nums []int, k int) bool {
last1 := -k - 1
for i, x := range nums {
if x != 1 {
continue
}
if i-last1 <= k {
return false
}
last1 = i
}
return true
}

###js

var kLengthApart = function(nums, k) {
    let last1 = -Infinity;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] !== 1) {
            continue;
        }
        if (i - last1 <= k) {
            return false;
        }
        last1 = i;
    }
    return true;
};

###rust

impl Solution {
    pub fn k_length_apart(nums: Vec<i32>, k: i32) -> bool {
        let mut last1 = -k - 1;
        for (i, x) in nums.into_iter().enumerate() {
            if x != 1 {
                continue;
            }
            if i as i32 - last1 <= k {
                return false;
            }
            last1 = i as i32;
        }
        true
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{nums}$ 的长度。
  • 空间复杂度:$\mathcal{O}(1)$。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

思路简单,性能高效

作者 uint32
2021年1月31日 22:40

解题思路

此处撰写解题思路
image.png

代码

###python3

class Solution:
    def kLengthApart(self, nums: List[int], k: int) -> bool:
        if 1 not in nums:
            return True

        min_len = n = len(nums)
        start = nums.index(1)
        for idx in range(start+1,n):
            if nums[idx] == 1:
                min_len = min(min_len, idx - start - 1)
                start = idx
                if min_len < k:
                    return False
        return True



[Python3/Java/C++/Go/TypeScript] 一题一解:遍历计数(清晰题解)

作者 lcbin
2025年11月16日 11:20

方法一:遍历计数

我们遍历字符串 $s$,用一个变量 $\textit{cur}$ 记录当前连续的 1 的个数,用变量 $\textit{ans}$ 记录答案。当遍历到字符 $s[i]$ 时,如果 $s[i] = 0$,则 $\textit{cur}$ 置 0,否则 $\textit{cur}$ 自增 1,然后 $\textit{ans}$ 自增 $\textit{cur}$,并对 $10^9 + 7$ 取模。

遍历结束,返回 $\textit{ans}$ 即可。

###python

class Solution:
    def numSub(self, s: str) -> int:
        mod = 10**9 + 7
        ans = cur = 0
        for c in s:
            if c == "0":
                cur = 0
            else:
                cur += 1
                ans = (ans + cur) % mod
        return ans

###java

class Solution {
    public int numSub(String s) {
        final int mod = 1_000_000_007;
        int ans = 0, cur = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) == '0') {
                cur = 0;
            } else {
                cur++;
                ans = (ans + cur) % mod;
            }
        }
        return ans;
    }
}

###cpp

class Solution {
public:
    int numSub(string s) {
        const int mod = 1e9 + 7;
        int ans = 0, cur = 0;
        for (char c : s) {
            if (c == '0') {
                cur = 0;
            } else {
                cur++;
                ans = (ans + cur) % mod;
            }
        }
        return ans;
    }
};

###go

func numSub(s string) (ans int) {
const mod int = 1e9 + 7
cur := 0
for _, c := range s {
if c == '0' {
cur = 0
} else {
cur++
ans = (ans + cur) % mod
}
}
return
}

###ts

function numSub(s: string): number {
    const mod = 1_000_000_007;
    let [ans, cur] = [0, 0];
    for (const c of s) {
        if (c === '0') {
            cur = 0;
        } else {
            cur++;
            ans = (ans + cur) % mod;
        }
    }
    return ans;
}

###rust

impl Solution {
    pub fn num_sub(s: String) -> i32 {
        const MOD: i32 = 1_000_000_007;
        let mut ans: i32 = 0;
        let mut cur: i32 = 0;
        for c in s.chars() {
            if c == '0' {
                cur = 0;
            } else {
                cur += 1;
                ans = (ans + cur) % MOD;
            }
        }
        ans
    }
}

时间复杂度 $O(n)$,其中 $n$ 为字符串 $s$ 的长度。空间复杂度 $O(1)$。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

❌
❌