力扣每日一题-08.22

2024-08-23 08:36
文章标签 力扣 一题 每日 08.22

本文主要是介绍力扣每日一题-08.22,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述

给定两个整数 nx,要求构造一个长度为 n 的正整数数组 nums,其中:

  • 数组中的所有元素是严格递增的。

  • 数组中的所有元素按位与的结果等于 x

返回数组 nums 中最后一个元素的最小值。


题目分析与思路

1. 初始思路

  • 题目要求构造一个严格递增的数组,同时满足数组中的所有元素按位与的结果为 x

    如果所有数组元素的按位与结果要等于 x,那么每个元素的二进制形式中的那些与 x 的二进制表示匹配的位必须和 x 一致。这意味着:

    • x 的二进制中为 1 的位置上,数组中每个元素的对应位置也必须为 1

    • 对于 x 中为 0 的位,这些位可能在数组的某些元素中出现不同的值。

2. 分析递增的条件

  • 要构造递增的数组,需要确保每一步都使得数组中的元素变大,而不改变最终的按位与结果。

  • 我们通过逐位更新数组中的元素来确保递增性。

3. 控制操作步数

  • n 表示数组的长度,而 n - 1 表示我们有多少步需要操作。我们可以通过检查 n - 1 的二进制位来控制具体的操作步骤,确保我们在 n-1 步内完成所有必要的递增操作。

4. 位运算的使用

  • 我们通过位运算来控制更新哪些位,以及如何更新。

    • m = n - 1m 的二进制表示告诉我们哪些位可以用于更新。

    • while 循环:我们通过逐步检查 ans 中的位,找到第一个可以安全更新的 0 位。

    • |= 操作:用于将 m 中的位安全地添加到 ans 中的合适位置。


题解步骤
  1. 初始化

    • 我们从 x 开始,将其作为 ans 的初始值,因为我们要求最终的按位与结果必须等于 x

    • m = n - 1:表示我们有 n-1 次步骤来调整 ans 的值。

  2. 逐步更新 ans

    • 使用位运算逐步检查 m 的每一位,并决定是否对 ans 进行更新。

    • while 循环用于确保我们找到 ans 中第一个可以更新的位。

    • |= 操作用于将 m 的当前位合并到 ans 中,确保 ans 递增。

  3. 返回结果

    • 最终更新后的 ans 即为满足条件的最小值。


代码实现
class Solution {public long minEnd(int n, int x) {long ans = x;  // 初始化 ans 为 xint m = n - 1;  // m 表示剩余的操作步数for (long i = 1, offset = 0; i <= m; i <<= 1) {// 找到 ans 中的第一个 0 位while ((ans & (i << offset)) > 0) {offset++;}// 更新 ans 的值ans |= (m & i) << offset;}return ans;  // 返回最终的结果}
}

题解分析
  1. 位运算的使用

    • 位运算在这道题中至关重要。我们通过 m & i 来检查是否可以对 ans 进行更新,并通过 |= 操作来确保 ans 的递增性。(有点难懂,后面会详细说明)

  2. m 的作用

    • m 控制了我们能够进行的操作步数,m 的每一位决定了当前步骤是否可以更新 ans 的某一位。

  3. 递增的保证

    • while 循环确保我们总是找到 ans 中(从低位向高位)第一个 0,并将其更新为1,从而确保 ans 是递增的。

  4. 复杂度分析

    • 由于我们逐位检查 mans 的位,时间复杂度与 nx 的位数相关,复杂度为 O(log n)。

    • 空间复杂度较低,仅使用了几个辅助变量,因此空间复杂度为 O(1)。


关键知识点
  • 位运算

    • &:按位与,用于逐位检查二进制位。

    • |=:按位或,用于将一个位设置为 1

    • <<:左移运算,用于将位掩码 i 移动到合适的位置。

  • 递增性

    • 通过检查 ans 中的 0 位并逐步更新,确保生成的 ans 是递增的。

  • 控制操作步骤

    • 使用 m = n - 1 的二进制位来控制在 n-1 步内完成所有必要的位更新。


说白了,要明白的是这道题的主要步骤就是找x的二进制中从低位到高位的0然后改为1.

那,如何找0?如何改值?如何判断已经结束需要返回?

1. 如何找 x 的二进制中从低位到高位的 0

在这道题中,我们要从 x 的二进制表示中找到第一个可以更新为 1 的位置(即第一个 0 位),从而确保数组递增。这个过程可以通过以下步骤完成:

  • 位运算的使用:我们通过逐位检查x的每一位,利用&和<<来找到第一个0位。具体操作是:

    • ans & (1 << offset):我们从 offset = 0 开始,检查 ans 的第 offset 位。如果 ans 在该位为 1,则说明这一位已经被占用,我们不能在这一位上进行更新。

    • while 循环:如果当前位已经为 1,我们递增 offset,继续检查下一位,找到 ans 中的第一个为 0 的位。

2. 如何将 0 改为 1

找到 0 位后,我们需要将其更新为 1。这个操作通过以下步骤完成:

  • 按位或 |= 操作:当找到第一个0位时,我们可以使用按位或操作将其更新为1:

    • (m & i) << offset:这里 m & i 的结果是 1(即我们要将某一位更新为 1)。通过左移 offset 位,我们将这一位放到合适的位置(即 0 位)。

    • ans |= (m & i) << offset:这一步将 m 中的某一位合并到 ans 中的第 offset 位,从而将 0 更新为 1

3. 如何判断已经结束,需要返回结果?

结束条件非常简单:当我们完成了所有的 n - 1 步操作,成功找到了所有合适的 0 位并将其更新为 1 后,ans 就是最终的结果。这是因为我们已经确保 ans 是递增的,且满足按位与结果为 x 的条件。

  • 循环控制for (long i = 1, offset = 0; i <= m; i <<= 1) 这个循环控制了我们逐步检查和更新 ans 的过程,直到完成所有的 n - 1 次操作。

  • 返回结果:当循环结束时,ans 已经更新到正确的值,我们直接返回 ans 作为最终结果。

是否检测了 n-1 次操作?

并不是直接检测 n-1 次操作,而是通过 m = n - 1 来间接控制操作次数。m 的二进制位控制了哪些步骤需要执行,而 offset 控制的是在哪些位置进行这些步骤。

更详细的解释:
  • m = n - 1:它的二进制位表示我们在执行 n-1 个操作。通过检测 m 的每一位是否为 1,我们确定是否需要在 ans 的某个位置进行更新。
  • while 循环中 offset 的作用:offset 的递增和检查帮助我们找到 ans 中的某个 0 位。这是我们准备将其更新为 1 的位置,以确保 ans 的递增性。
    • while 循环不断递增 offset,直到找到一个可以安全设置为 1 的位置。

示例:如何通过这些步骤找到并更新 0

让我们通过一个简单的例子来进一步说明如何找到 0 位并将其更新为 1

输入示例:
  • n = 3

  • x = 4

操作步骤:
  1. 初始化

    • ans = x = 4,二进制为 100

    • m = n - 1 = 2,二进制为 10

  2. 第一步操作

    • i = 1(二进制为 1),从最低位开始检查。

    • while 循环:检查 ans 的第 0 位,ans & (1 << 0) = 0。第 0 位为 0,可以更新。

    • 更新ans |= 1 << 0,即将第 0 位从 0 更新为 1

    • 更新后的 ans = 5,二进制为 101

  3. 第二步操作

    • i = 2(二进制为 10),检查 m 的下一位。

    • while 循环:检查 ans 的第 1 位,ans & (1 << 1) = 0。第 1 位为 0,可以更新。

    • 更新ans |= 2 << 1,即将第 1 位从 0 更新为 1

    • 更新后的 ans = 6,二进制为 110

  4. 结束并返回

    • 操作完 n - 1 次后,ans 最终值为 6,这是符合条件的最小递增值。

这篇关于力扣每日一题-08.22的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098878

相关文章

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

每日一练7:简写单词(含链接)

1.链接 简写单词_牛客题霸_牛客网 2.题目 3.代码1(错误经验) #include <iostream>#include <string>using namespace std;int main() {string s;string ret;int count = 0;while(cin >> s)for(auto a : s){if(count == 0){if( a <=

两数之和--力扣1

两数之和 题目思路C++代码 题目 思路 根据题目要求,元素不能重复且不需要排序,我们这里使用哈希表unordered_map。注意题目说了只对应一种答案。 所以我们在循环中,使用目标值减去当前循环的nums[i],得到差值,如果我们在map中能够找到这个差值,就说明存在两个整数的和为目标值。 如果没有找到,就将当前循环的nums[i]以及下标i放入map中,以便后续查

【每日刷题】Day113

【每日刷题】Day113 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 91. 解码方法 - 力扣(LeetCode) 2. LCR 098. 不同路径 - 力扣(LeetCode) 3. 63. 不同路径 II - 力扣(LeetCode) 1. 91. 解码方法 - 力扣(LeetCode) //思路:动态规划。 cl

力扣第347题 前K个高频元素

前言 记录一下刷题历程 力扣第347题 前K个高频元素 前K个高频元素 原题目: 分析 我们首先使用哈希表来统计数字出现的频率,然后我们使用一个桶排序。我们首先定义一个长度为n+1的数组,对于下图这个示例就是长度为7的数组。为什么需要一个长度为n+1的数组呢?假如说总共有三个数字都为1,那么我们需要把这个1放在数组下标为3的位置,假如说数组长度为n,对于这个例子就是长度为3,那么它的

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结,删除链表节点问题使用到列表,哈希表,递归比较容易超时,我觉得使用计数排序比较稳,处理起来也不是很难。 1. 力扣3217:从链表中移除在数组中的节点 1.1 题目: 给你一个整数数组 nums 和一个链表的头节点 head。从链表中移除所有存在于 nums 中的节点后,返回修改后的链表的头节点。 示例 1: 输入: nums = [1,2,3], head = [1,2,3,

力扣 739. 每日温度【经典单调栈题目】

1. 题目 理解题意: 1.1. 给一个温度集合, 要返回一个对应长度的结果集合, 这个结果集合里面的元素 i 是 当前 i 位置的元素的下一个更高温度的元素的位置和当前 i 位置的距离之差, 若是当前元素不存在下一个更高温度的元素, 则这个位置用0代替; 2. 思路 本题用单调栈来求解;单调栈就适用于来求当前元素左边或者右边第一个比当前元素大或者小的元素;【单调栈:让栈中的元素保持单调

力扣接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]输出:6解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 示例 2: 输入:height

每日一题——第八十一题

打印如下图案: #include<stdio.h>int main() {int i, j;char ch = 'A';for (i = 1; i < 5; i++, ch++){for (j = 0; j < 5 - i; j++){printf(" ");//控制空格输出}for (j = 1; j < 2 * i; j++)//条件j < 2 * i{printf("%c", ch