本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是LeetCode专题第55篇文章,我们一起来看看LeetCode中的第89题 Gray Code(格雷码)。

这题的官方难度是Medium,通过率是48.9%,点赞639,反对1545。又是一道反对比点赞多得多的题目,我个人发现其实这些反对很多的题目都有一个特点,就是题意比较晦涩,出题人的意图不太容易get到。不知道是不是老外理解能力不太行,所以都给出了这么多的反对。

我们就来看看这道题的真面目吧。

题意

题目中说gray code,格雷码是一连串n位二进制表示的数字。这一串的数字有一个特点就是第一个数字是0,从0开始后面的每一个数字和前一个数字只有一个二进制位不同。

题目会给定我们一个非负整数n,要求我们生产n位的灰色代码,也就是产生这些数字。并且这些数字是以10进制存储的。

不知道大家看明白没有,我们来看一个样例。

样例

Input: 2
Output: [0,1,3,2]

在上面这个例子当中,输入是2,表示这些数字是两位二进制位构成的,输出是[0, 1, 3, 2]。我们把0,1,3,2翻译成二进制,0是00,1是01,3是11,2是10。排列在一起的话就是00, 01, 11, 10。我们可以发现每一个数和前一个数相差的都是一个二进制位。

题目当中相关的描述就这么多,但其实有很多隐藏的信息没有给,要我们自己猜测。比如说每一个数字只能出现一次,不然的话这个序列就是无穷无尽的。另外一个隐藏信息是,这样的序列应该也不是唯一的,但是题目并没有说是否所有合法的序列都可以通过测试,还是说一定要返回字典序最小的结果。

题目比较晦涩也就算了,这些隐藏信息没有交代清楚,也难怪大家会费解。

题解

当然以上的问题其实也不是事,我们不确定试一次也就知道了,核心还是怎么想出解法来。

干想是没有结果的,还是要先分析搜集一些信息。首先,题目给定的n,限制了每个数能够使用的二进制位的数量。n个二进制位一共能表示的数字有$2^n$种,我们无法得知是否这么多数字都能串联起来。假设可行的话,那么这个问题其实就是这$2^n$个数如何摆放的问题。

所以问题的关键就是要寻找这样一个序列,根据我们之前解全排列以及各种排列的方法,可以联想得到,这大概率是一个搜索问题。

顺着搜索的思路继续往下,剩下的事情就容易了,我们的起始搜索点是0。题目中要求了每两个相邻的数的二进制位只相差一个,那么我们可以遍历这些二进制位,寻找0的后继节点。同样对于每一个后继节点来说,我们都可以用同样的方法寻找它的后继们。再加上gray code不能包含重复的元素,我们可以在搜索的时候加上剪枝。

这一套其实是一个经典的搜索问题的流程。

如果我们换个思路,虽然也能得到一样的解法,但是思考的过程会不太一样。怎么换思路呢,其实也简单,我们把它想象成一个图论问题。也就是说,每一个数字都是图中的一个节点。如果两个数字之间满足只相差了一个二进制位,那么说明它们之间有一条边相连。整个问题就转变成了我们从0这个点出发,找出所有连通的节点。

对于图上的遍历问题,方法就很固定了就是搜索。也就是说从这个角度思考的话,更加容易想到搜索上面了, 整个思考的链路会更短。这也是为什么很多大神建模的时候喜欢从往图上考虑的原因。

这些都想明白了再来写代码真的就水到渠成了,整个核心代码真的不长:

class Solution:
def grayCode(self, n: int) -> List[int]:
ret = [0]
elements = {0} def dfs(cur):
# 遍历与cur唯一不同的二进制位
for i in range(n):
# 针对这一维做亦或,将0变1,1变0
nxt = cur ^ (1 << i)
if nxt in elements:
continue
# 记录答案,继续往下遍历
elements.add(nxt)
ret.append(nxt)
dfs(nxt)
dfs(0)
return ret

总结

单纯从思路以及最后的AC代码来看的话,这道题难度应该是很低的,实际上也的确如此,这题的通过率接近50%,已经是Medium难度的下届了。但是相比于做对这题而言,更加重要的是思路。以图论的思维来抽象建模是算法题当中一个非常常见的手段,这是比题目本身更加宝贵的东西。

如果你读过昨天的文章的话,会发现昨天的87题,本质上也是用的一个图论建模的方法。但是从表现形式上来说,这两题真的可以说是完全不一样。建议大家能好好做做这两题,体会一下其中思维和解法的闪光点。

今天的文章到这里就结束了,如果喜欢本文的话,请来一波素质三连,给我一点支持吧(关注、转发、点赞)。

本文使用 mdnice 排版

LeetCode 89,因为题目晦涩而被点了1500+反对的搜索问题的更多相关文章

  1. leetcode - 位运算题目汇总(下)

    接上文leetcode - 位运算题目汇总(上),继续来切leetcode中Bit Manipulation下的题目. Bitwise AND of Numbers Range 给出一个范围,[m, ...

  2. leetcode top 100 题目汇总

    首先表达我对leetcode网站的感谢,与高校的OJ系统相比,leetcode上面的题目更贴近工作的需要,而且支持的语言广泛.对于一些比较困难的题目,可以从讨论区中学习别人的思路,这一点很方便. 经过 ...

  3. [LeetCode] 89. Gray Code 格雷码

    The gray code is a binary numeral system where two successive values differ in only one bit. Given a ...

  4. leetcode tree相关题目总结

    leetcode tree相关题目小结 所使用的方法不外乎递归,DFS,BFS. 1. 题100 Same Tree Given two binary trees, write a function ...

  5. All LeetCode Questions List 题目汇总

    All LeetCode Questions List(Part of Answers, still updating) 题目汇总及部分答案(持续更新中) Leetcode problems clas ...

  6. [LeetCode] 二叉树相关题目(不完全)

    最近在做LeetCode上面有关二叉树的题目,这篇博客仅用来记录这些题目的代码. 二叉树的题目,一般都是利用递归来解决的,因此这一类题目对理解递归很有帮助. 1.Symmetric Tree(http ...

  7. [LeetCode] 动态规划入门题目

    最近接触了动态规划这个厉害的方法,还在慢慢地试着去了解这种思想,因此就在LeetCode上面找了几道比较简单的题目练了练手. 首先,动态规划是什么呢?很多人认为把它称作一种"算法" ...

  8. 关于LeetCode上链表题目的一些trick

    最近在刷leetcode上关于链表的一些高频题,在写代码的过程中总结了链表的一些解题技巧和常见题型. 结点的删除 指定链表中的某个结点,将其从链表中删除. 由于在链表中删除某个结点需要找到该结点的前一 ...

  9. LeetCode - 排列相关题目

    1.获取全排列 https://leetcode.com/problems/permutations/submissions/ 按字典序输出: 这里用的是vector<int>,不是引用. ...

随机推荐

  1. __FILE__和__DIR__的用法

    dirname(__FILE__)由两部分组成, __FILE__ 魔术变量和 dirname() 函数.__FILE__是当前脚本的绝对路径,通过 dirname() 函数后,就可以获得脚本所在目录 ...

  2. MYSQL 之 JDBC(九):增删改查(七)DAO的补充和重构

    DAO重构后的代码 package com.litian.jdbc; import org.apache.commons.beanutils.BeanUtils; import java.sql.*; ...

  3. python 面向对象专题(九):特殊方法 (二)__get__、__set__、__delete__ 描述符(二)覆盖型与非覆盖型描述符对比

    前言 根据是否定义__set__ 方法,描述符可分为两大类. 实现 __set__ 方法的描述符属于覆盖型描述符,因为虽然描述符是类属性,但是实现 __set__ 方法的话,会覆盖对实例属性的赋值操作 ...

  4. Python之爬虫(二十一) Scrapy爬取所有知乎用户信息(下)

    在上一篇文章中主要写了关于爬虫过程的分析,下面是代码的实现,完整代码在:https://github.com/pythonsite/spider items中的代码主要是我们要爬取的字段的定义 cla ...

  5. Python面向对象01 /面向对象初识、面向对象结构、类、self、实例化对象

    Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 目录 Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 1. 面向对象初识 2. 面向 ...

  6. 精通java并发-synchronized关键字和锁

    目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages synchronized关键字和锁 示例代码 public class MyThreadTest2 { public ...

  7. 命令模式(c++实现)

    命令模式 目录 命令模式 模式定义 模式动机 UML类图 源码实现 优点 缺点 模式定义 命令模式(Facade),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请 ...

  8. GPO - General GPO Settings(3)

    WMI filtering Setting - Differentiating Installation Between Operations and Architecture. WMI SQL Ge ...

  9. Python Ethical Hacking - WEB PENETRATION TESTING(5)

    Guessing Login Information on Login Pages Our target website: http://10.0.0.45/dvwa/login.php #!/usr ...

  10. twitch游戏直播(【国外】平台)如何绑定二次验证码_虚拟MFA?

    一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 twitch游戏直播([国外]平台)如何绑定二次验证码_虚拟MFA? 二次验证码小程序于谷歌身份验证器APP的优势(更多见官网 ...