<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>张磊的技术笔记</title><description>记录技术实践、问题求解与阅读思考</description><link>https://zhanglei1949.github.io/</link><language>zh_CN</language><item><title>从零跑通 NeuG：导入数据，完成第一次图查询</title><link>https://zhanglei1949.github.io/posts/neug-code-graph-getting-started/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/neug-code-graph-getting-started/</guid><description>用 NeuG 和 Python 构建一张代码依赖图，从 CSV 导入开始，查询直接调用者、多层依赖与涉及的文件，附可运行示例。</description><pubDate>Wed, 30 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;修改一个公共函数之前，我们常常要先回答两个问题：谁在调用它？改动可能影响哪些上层模块？&lt;/p&gt;
&lt;p&gt;调用点不多时，用编辑器跳转就能找到答案。调用链一长，就得逐层查看函数、记录路径，再整理涉及的文件。有了调用关系数据，这些工作可以交给图查询。&lt;/p&gt;
&lt;p&gt;下面用 NeuG 存储一张代码依赖图：先定义模型、导入 CSV，再查找直接和间接调用者，最后列出它们所在的文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文示例已在 NeuG &lt;strong&gt;0.2.0&lt;/strong&gt;、Python &lt;strong&gt;3.11.9&lt;/strong&gt;、macOS &lt;strong&gt;15.1.1 / ARM64&lt;/strong&gt; 上运行验证。数据为人工构造，不涉及真实项目。文档核对日期为 2026 年 9 月 30 日。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;为什么用 NeuG 做这个例子&lt;/h2&gt;
&lt;p&gt;NeuG 支持图数据存储和 Cypher 查询，既可以嵌入应用进程，也可以通过服务访问。本篇使用 Python 嵌入式接口：在脚本中打开数据库、建立连接，直接执行查询，无需另外启动数据库服务。&lt;a href=&quot;https://neug.io/docs/overview/introduction/&quot;&gt;官方介绍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;A&lt;/code&gt; 调用 &lt;code&gt;B&lt;/code&gt;，可以表示为一条从 &lt;code&gt;A&lt;/code&gt; 指向 &lt;code&gt;B&lt;/code&gt; 的边。函数与源文件之间再加一条“定义于”关系，就能从调用链查到文件。&lt;/p&gt;
&lt;p&gt;这组数据很小，用字典和遍历算法也能处理。用它入门的好处是，每条查询的结果都可以对着图核对。&lt;/p&gt;
&lt;h2&gt;先把调用关系画出来&lt;/h2&gt;
&lt;p&gt;假设项目中有三个文件，共八个函数。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;文件&lt;/th&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;api.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;handle_request&lt;/code&gt;、&lt;code&gt;format_response&lt;/code&gt;、&lt;code&gt;health_check&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;service.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;build_report&lt;/code&gt;、&lt;code&gt;load_user&lt;/code&gt;、&lt;code&gt;load_orders&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storage.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;read_cache&lt;/code&gt;、&lt;code&gt;query_db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;调用关系如下，箭头始终从调用者指向被调用者：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;handle_request ──→ load_user ──→ read_cache
       │               └─────→ query_db
       └────────→ format_response

build_report ───→ load_user
       └────────→ load_orders ──→ query_db

health_check    （没有调用边）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们准备修改的函数是 &lt;code&gt;query_db&lt;/code&gt;。从图上可以看出，&lt;code&gt;load_user&lt;/code&gt; 和 &lt;code&gt;load_orders&lt;/code&gt; 直接调用它；&lt;code&gt;handle_request&lt;/code&gt; 和 &lt;code&gt;build_report&lt;/code&gt; 则通过其他函数间接依赖它。稍后可以用查询核对。&lt;/p&gt;
&lt;p&gt;这张图需要两类节点和两类关系。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Function&lt;/code&gt;：函数，保存编号 &lt;code&gt;id&lt;/code&gt; 和名称 &lt;code&gt;name&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;File&lt;/code&gt;：文件，保存编号 &lt;code&gt;id&lt;/code&gt; 和路径 &lt;code&gt;path&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CALLS&lt;/code&gt;：从函数指向它调用的函数。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DEFINED_IN&lt;/code&gt;：从函数指向定义它的文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;函数名只是用于展示的属性，编号才是本例的主键。实际项目可能有同名函数，不能直接用短名称判断它们是不是同一个实体。&lt;/p&gt;
&lt;h2&gt;安装与创建数据库&lt;/h2&gt;
&lt;p&gt;先创建独立的 Python 环境，再安装本文验证过的版本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3.11 -m venv .venv
source .venv/bin/activate
python -m pip install &quot;neug==0.2.0&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装方式参考&lt;a href=&quot;https://neug.io/docs/installation/installation/&quot;&gt;官方安装指南&lt;/a&gt;。本文只验证了上述 macOS 环境，其他系统的安装要求请以官方文档为准。&lt;/p&gt;
&lt;p&gt;新建一个工作目录，把下面的 Python 代码按顺序写进同一个脚本，并在该目录中运行。脚本先创建数据库目录，再打开连接：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pathlib import Path
import neug

# 首次运行使用新目录，避免重复导入。
db_path = Path(&quot;code_graph_db&quot;)
db_path.mkdir(exist_ok=False)

db = neug.Database(str(db_path))
conn = db.connect()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再次运行完整脚本时，请换一个工作目录；&lt;code&gt;exist_ok=False&lt;/code&gt; 会在数据库目录已存在时报错，避免误用旧数据。如果只想查询已有数据，跳过目录创建、建表和导入，直接打开原目录即可。&lt;a href=&quot;https://neug.io/docs/getting_started/getting_started/&quot;&gt;数据库与连接说明&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;定义模型，再导入数据&lt;/h2&gt;
&lt;p&gt;先创建节点表和关系表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;schema = [
    &quot;CREATE NODE TABLE Function(id INT64, name STRING, PRIMARY KEY(id))&quot;,
    &quot;CREATE NODE TABLE File(id INT64, path STRING, PRIMARY KEY(id))&quot;,
    &quot;CREATE REL TABLE CALLS(FROM Function TO Function)&quot;,
    &quot;CREATE REL TABLE DEFINED_IN(FROM Function TO File)&quot;,
]
for statement in schema:
    conn.execute(statement)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;FROM&lt;/code&gt; 和 &lt;code&gt;TO&lt;/code&gt; 对应关系的起点和终点。例如，&lt;code&gt;DEFINED_IN&lt;/code&gt; 从函数出发，指向文件。节点表主键和关系表的定义方式可查阅 &lt;a href=&quot;https://neug.io/docs/cypher_manual/ddl_clause/&quot;&gt;DDL 文档&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;接着准备四份 CSV。下面直接用 Python 写出文件，也可以替换成其他工具导出的 CSV。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import csv

files = [(1, &quot;api.py&quot;), (2, &quot;service.py&quot;), (3, &quot;storage.py&quot;)]
functions = [
    (1, &quot;handle_request&quot;), (2, &quot;build_report&quot;), (3, &quot;load_user&quot;),
    (4, &quot;load_orders&quot;), (5, &quot;read_cache&quot;), (6, &quot;query_db&quot;),
    (7, &quot;format_response&quot;), (8, &quot;health_check&quot;),
]
calls = [(1, 3), (1, 7), (2, 3), (2, 4), (3, 5), (3, 6), (4, 6)]
defined_in = [(1, 1), (2, 2), (3, 2), (4, 2), (5, 3), (6, 3), (7, 1), (8, 1)]

datasets = [
    (&quot;functions.csv&quot;, [&quot;id&quot;, &quot;name&quot;], functions),
    (&quot;files.csv&quot;, [&quot;id&quot;, &quot;path&quot;], files),
    (&quot;calls.csv&quot;, [&quot;source&quot;, &quot;target&quot;], calls),
    (&quot;defined_in.csv&quot;, [&quot;source&quot;, &quot;target&quot;], defined_in),
]
for filename, header, rows in datasets:
    with open(filename, &quot;w&quot;, newline=&quot;&quot;) as f:
        writer = csv.writer(f)
        writer.writerow(header)
        writer.writerows(rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如，&lt;code&gt;calls.csv&lt;/code&gt; 中的 &lt;code&gt;3,6&lt;/code&gt; 表示编号为 3 的 &lt;code&gt;load_user&lt;/code&gt; 调用编号为 6 的 &lt;code&gt;query_db&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;COPY FROM&lt;/code&gt; 导入数据，先导入节点，再导入关系：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;imports = [
    &quot;COPY Function FROM &apos;functions.csv&apos; (header=true, delimiter=&apos;,&apos;)&quot;,
    &quot;COPY File FROM &apos;files.csv&apos; (header=true, delimiter=&apos;,&apos;)&quot;,
    &quot;&quot;&quot;COPY CALLS FROM &apos;calls.csv&apos;
       (header=true, delimiter=&apos;,&apos;, from=&apos;Function&apos;, to=&apos;Function&apos;)&quot;&quot;&quot;,
    &quot;&quot;&quot;COPY DEFINED_IN FROM &apos;defined_in.csv&apos;
       (header=true, delimiter=&apos;,&apos;, from=&apos;Function&apos;, to=&apos;File&apos;)&quot;&quot;&quot;,
]
for statement in imports:
    conn.execute(statement)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;节点 CSV 的列顺序与表定义保持一致。关系 CSV 的前两列分别是起点和终点的主键值，&lt;code&gt;from&lt;/code&gt;、&lt;code&gt;to&lt;/code&gt; 则指定端点所属的节点表。这里的相对文件路径以运行 Python 时的工作目录为基准。&lt;a href=&quot;https://neug.io/docs/data_io/import_data/&quot;&gt;CSV 导入说明&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要省略本例中的 &lt;code&gt;delimiter=&apos;,&apos;&lt;/code&gt;。&lt;/strong&gt; 在本次验证环境中，省略这个选项后，导入报了“识别出 1 列、预期 2 列”的错误；显式指定逗号后导入成功。&lt;/p&gt;
&lt;p&gt;导入完成后，先核对数量：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;checks = [
    &quot;MATCH (n:Function) RETURN count(n)&quot;,
    &quot;MATCH (n:File) RETURN count(n)&quot;,
    &quot;MATCH (:Function)-[r:CALLS]-&amp;gt;(:Function) RETURN count(r)&quot;,
    &quot;MATCH (:Function)-[r:DEFINED_IN]-&amp;gt;(:File) RETURN count(r)&quot;,
]
for query in checks:
    print(list(conn.execute(query)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;四个结果应依次是 &lt;code&gt;[[8]]&lt;/code&gt;、&lt;code&gt;[[3]]&lt;/code&gt;、&lt;code&gt;[[7]]&lt;/code&gt; 和 &lt;code&gt;[[8]]&lt;/code&gt;，对应函数数、文件数、调用边数和归属边数。如果不一致，先检查导入数据。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/neug-code-graph/import-query-flow.webp&quot; alt=&quot;CSV 数据导入 NeuG，再通过查询得到结果的流程&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;CSV 导入与图查询的流程示意。&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;第一次查询：谁直接调用了 query_db&lt;/h2&gt;
&lt;p&gt;把下面的 Cypher 交给 &lt;code&gt;conn.execute()&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;query = &quot;&quot;&quot;
MATCH (caller:Function)-[:CALLS]-&amp;gt;(target:Function {id: 6})
RETURN caller.name AS name
ORDER BY name
&quot;&quot;&quot;
for row in conn.execute(query):
    print(row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&apos;load_orders&apos;]
[&apos;load_user&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;(caller:Function)&lt;/code&gt; 匹配函数节点，&lt;code&gt;[:CALLS]&lt;/code&gt; 匹配调用关系，右侧的 &lt;code&gt;{id: 6}&lt;/code&gt; 将目标限定为 &lt;code&gt;query_db&lt;/code&gt;。这条查询只查直接调用者。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RETURN&lt;/code&gt; 指定要输出的属性，&lt;code&gt;ORDER BY&lt;/code&gt; 让结果顺序稳定，方便核对。函数在 CSV 中的排列顺序不应被当作查询结果的默认排序。&lt;/p&gt;
&lt;h2&gt;第二次查询：沿调用链找到上层函数&lt;/h2&gt;
&lt;p&gt;直接调用者之外，我们还希望找到 &lt;code&gt;handle_request&lt;/code&gt; 和 &lt;code&gt;build_report&lt;/code&gt;。把一条边扩展为长度在 1 到 3 之间的路径：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MATCH (caller:Function)-[:CALLS*1..3]-&amp;gt;(target:Function {id: 6})
RETURN DISTINCT caller.id AS id, caller.name AS name
ORDER BY id
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将这段 Cypher 替换到上一段 Python 的 &lt;code&gt;query&lt;/code&gt; 字符串中，保持逐行打印的代码不变，输出为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1, &apos;handle_request&apos;]
[2, &apos;build_report&apos;]
[3, &apos;load_user&apos;]
[4, &apos;load_orders&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;*1..3&lt;/code&gt; 表示路径包含 1 到 3 条调用边。这张示例图的相关路径最多只有两条边，因此这个范围覆盖了图中所有上层调用者；如果某个调用者只能通过四条及以上的调用边到达目标，它就不会被返回。&lt;a href=&quot;https://neug.io/docs/cypher_manual/query_clauses/match_clause/&quot;&gt;多跳模式语法&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;build_report&lt;/code&gt; 有两条路径能到达 &lt;code&gt;query_db&lt;/code&gt;：一条经过 &lt;code&gt;load_user&lt;/code&gt;，另一条经过 &lt;code&gt;load_orders&lt;/code&gt;。&lt;code&gt;DISTINCT&lt;/code&gt; 把重复的调用者合并。返回列保留了主键 &lt;code&gt;id&lt;/code&gt;，即使两个函数同名，也不会被误合并。&lt;/p&gt;
&lt;p&gt;真实调用图可能有递归和环。这里保留三层上限，限定本次查询的范围；需要追溯更深的依赖时，再调整上限。&lt;/p&gt;
&lt;h2&gt;第三次查询：这些调用者分布在哪些文件&lt;/h2&gt;
&lt;p&gt;接下来仍然替换 &lt;code&gt;query&lt;/code&gt; 字符串，沿 &lt;code&gt;DEFINED_IN&lt;/code&gt; 关系查出调用者所在的文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MATCH (caller:Function)-[:CALLS*1..3]-&amp;gt;(target:Function {id: 6}),
      (caller)-[:DEFINED_IN]-&amp;gt;(file:File)
RETURN DISTINCT file.path AS path
ORDER BY path
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&apos;api.py&apos;]
[&apos;service.py&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个模式共享变量 &lt;code&gt;caller&lt;/code&gt;：第一个模式找到上层调用者，第二个模式找到这些调用者所在的文件。&lt;/p&gt;
&lt;p&gt;结果只包含&lt;strong&gt;上层调用者所在的文件&lt;/strong&gt;，因此没有 &lt;code&gt;storage.py&lt;/code&gt;。目标函数 &lt;code&gt;query_db&lt;/code&gt; 自身不在这份调用者名单中；同一文件中的 &lt;code&gt;read_cache&lt;/code&gt; 也没有调用它。如果要形成完整的改动检查清单，可以另外加入目标函数所在文件。&lt;/p&gt;
&lt;p&gt;上一条函数查询也没有返回 &lt;code&gt;format_response&lt;/code&gt; 和 &lt;code&gt;health_check&lt;/code&gt;：图中没有从它们到 &lt;code&gt;query_db&lt;/code&gt; 的调用路径。不过，调用图无法涵盖所有影响。例如，共享数据格式发生变化时，没有调用关系的代码也可能需要调整。&lt;/p&gt;
&lt;h2&gt;关闭以后，数据还在吗&lt;/h2&gt;
&lt;p&gt;完成查询后，关闭连接和数据库。随后用同一个目录重新打开：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;conn.close()
db.close()

db = neug.Database(str(db_path))
conn = db.connect()
try:
    result = conn.execute(&quot;MATCH (n:Function) RETURN count(n)&quot;)
    print(list(result))  # [[8]]
finally:
    conn.close()
    db.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里只重新打开数据库，不再执行建表和导入。配套脚本还会在重新打开后再次检查前面的三组查询。&lt;/p&gt;
&lt;p&gt;本次验证覆盖了正常关闭后的重新打开，未进行进程崩溃或机器掉电测试，也没有做性能测试。八个函数的例子适合检查语法和结果，不能用来推断大型代码仓库的查询表现。&lt;/p&gt;
&lt;h2&gt;从示例走向真实代码库&lt;/h2&gt;
&lt;p&gt;接入真实代码库时，可以把这四份 CSV 换成代码分析工具的输出。导入和查询的步骤相同，数据本身则要处理好下面几个问题。&lt;/p&gt;
&lt;p&gt;首先是身份识别。真实函数应有稳定的标识，至少要考虑仓库、文件路径和限定名称；本文的整数编号只是为了让数据容易阅读。&lt;/p&gt;
&lt;p&gt;其次是调用关系的来源。动态分派、反射、函数指针和生成代码，都可能让关系提取出现遗漏或不确定性。将“明确解析的调用”和“推测的调用”区分开，查询结果才容易解释。&lt;/p&gt;
&lt;p&gt;最后是版本一致性。代码更新后，需要同步替换失效的节点和关系，避免把不同版本的函数混在一张图里。&lt;/p&gt;
&lt;p&gt;你可以先做一个小练习：在 &lt;code&gt;calls&lt;/code&gt; 数据中增加 &lt;code&gt;(8, 6)&lt;/code&gt;，表示 &lt;code&gt;health_check&lt;/code&gt; 调用 &lt;code&gt;query_db&lt;/code&gt;，然后在新的数据库中重新导入。直接调用者应增加 &lt;code&gt;health_check&lt;/code&gt;，而文件名单仍然是 &lt;code&gt;api.py&lt;/code&gt; 和 &lt;code&gt;service.py&lt;/code&gt;，因为 &lt;code&gt;api.py&lt;/code&gt; 本来就在结果中。&lt;/p&gt;
&lt;h2&gt;配套示例&lt;/h2&gt;
&lt;p&gt;配套&lt;a href=&quot;/downloads/neug-code-graph.zip&quot;&gt;示例包&lt;/a&gt;中的 &lt;code&gt;demo.py&lt;/code&gt; 自动生成四份 CSV，在临时目录中创建数据库，核对节点和边数量，检查三组查询结果，并在正常关闭后重新打开数据库再次验证。运行结束会清理它创建的临时目录，不修改已有数据库。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python demo.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;脚本中的结果断言全部通过后，会输出 &lt;code&gt;Reopen check: passed&lt;/code&gt;。修改示例数据后，也要相应更新 &lt;code&gt;QUERIES&lt;/code&gt; 中的预期结果和数量断言。&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;p&gt;本文主要参考 NeuG 官方文档，以下链接分别对应安装、建模、导入和查询步骤。文档核对日期：2026 年 9 月 30 日；示例验证版本：NeuG 0.2.0。在线文档可能随版本更新。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/overview/introduction/&quot;&gt;NeuG — Introduction&lt;/a&gt;：了解 NeuG 的定位，以及嵌入式和服务两种使用方式。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/installation/installation/&quot;&gt;NeuG — Installation&lt;/a&gt;：Python 安装方法及运行环境要求。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/getting_started/getting_started/&quot;&gt;NeuG — Getting Started&lt;/a&gt;：创建数据库、建立连接，以及关闭连接和数据库的基本流程。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/cypher_manual/ddl_clause/&quot;&gt;NeuG — DDL Clause&lt;/a&gt;：节点表、主键和关系表的定义语法。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/data_io/import_data/&quot;&gt;NeuG — COPY FROM&lt;/a&gt;：CSV 导入、列顺序、关系端点及导入选项。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/cypher_manual/query_clauses/match_clause/&quot;&gt;NeuG — MATCH Clause&lt;/a&gt;：节点与关系模式匹配，以及指定路径长度的多跳查询。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neug.io/docs/reference/python_api/query_result/&quot;&gt;NeuG — Python Query Result&lt;/a&gt;：Python 查询结果对象的接口说明。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文的代码依赖数据、查询组合和结果校验为独立编写的教学示例；具体输出以配套脚本的运行结果为依据。&lt;/p&gt;
</content:encoded></item><item><title>读《中国通史》有感</title><link>https://zhanglei1949.github.io/posts/chinese-history/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/chinese-history/</guid><pubDate>Fri, 31 Jan 2020 09:08:22 GMT</pubDate><content:encoded>&lt;h1&gt;读《中国通史》有感&lt;/h1&gt;
&lt;p&gt;这个学期末，我接触到了两本好书，《中国通史》和《历代政治得失》。今天我们主要讲吕思勉先生的《中国通史》中先秦部分。&lt;/p&gt;
&lt;p&gt;我们常常讲，中华民族有五千年的悠久历史，其实这种说法是有争议的。国外并不承认华夏三皇五帝的存在，因为这段上古时期的历史并没有文字记载，仅存的信息是来源于民间的各种神话传说，而神话传说的真实性和准确性是有待商榷的。但是同时我们也应当明白，这世界上的任何艺术创造都是有生活来源的，即，人类所创作的文学艺术从来不可能完全脱离社会现实，不可能完全架空与社会现实。一个很典型的例子就是，人类在幻想自己从未谋面的外星人的时候，往往会给他们赋予自己曾见过的生物/事物的外形。从这个角度讲，我们有理由相信，尧舜禹的神话传说是有现实依据的。&lt;/p&gt;
&lt;p&gt;在商朝的时候，汉字的雏形，甲骨文出现了。甲骨文的内容主要是巫师在占卜之后，将占卜的事件以及结果记录在动物的骨头上（多为牛羊的肩胛骨和龟壳）。在公元前1000年左右，位于岐山附近的周部落开始崛起，并最终以替天行道的姿态终结了商朝的统治。在我看来，周朝的最重要的人物当属周公旦，他作为周文王的弟子辅佐了周武王和周成王。他所建立的宗法制，礼乐制度成为了之后数千年中国的政治制度基础，被后世儒家尊为“元圣”。&lt;/p&gt;
&lt;p&gt;然而分封制的弊病慢慢地耗尽了周王朝地气数，从东周开始，中原大地进入春秋战国时期。这一时期，周王室失去了对诸侯国的控制，各诸侯国为了争夺土地和人民，诉诸战争，不断变法自强，最终由秦国一同天下。客观来讲，这一时期，各诸侯过为了增强国力，广拓疆土，改革制度（导致了士阶层的出现），同时，各国之间频繁的人才流动也带动了广泛的文化交流，从而孕育了华夏文明的雏形。&lt;/p&gt;
</content:encoded></item><item><title>LeetCode 题解-42. Trapping Rain Water</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-42/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-42/</guid><pubDate>Fri, 20 Dec 2019 08:38:06 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 42. Trapping Rain Water 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
&lt;img src=&quot;https://assets.leetcode.com/uploads/2018/10/22/rainwatertrap.png&quot; alt=&quot;avatar&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;很经典的一道题目，解决这个问题的关键点在于明白怎么样才能在一个格子里面集到最多的水．不妨假设我们想要知道在第i个格子里面最多可以集聚到多少水．那么，我们很容易发现，取决与height[i+1...n]中最大数和height[0...i-1]中最大数的较小的那个．所以，我们只需要知道左边最大和右边最大即可．那么可以有两种方案．&lt;/p&gt;
&lt;h3&gt;基于动态规划&lt;/h3&gt;
&lt;p&gt;首先从左边遍历一次数组，dp[i] = max(dp[j]) where 0&amp;lt;=j&amp;lt;=i; 同样再从右边向左遍历一次，这样，对于我们就知道左边最大和右边最大的高度了．&lt;/p&gt;
&lt;h3&gt;基于two-pointer.&lt;/h3&gt;
&lt;p&gt;设定两个指针，left, right. 初始时分别设为最左和最右．在每一次迭代中，判断height[left] 和height[right]的大小．为什么要选小的呢？因为其实我们要找的是两个最大值中的最小值，要想找到更大的最小值，自然是要舍弃现在的最小值．只有这样才能找到更大的．&lt;/p&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public static int trap(int[] height) {
    if (height.length == 0) return 0;
    int left = 0;
    int right = height.length - 1;
    int maxLeft = 0;
    int maxRight = 0;
    int sum = 0;
    while (left &amp;lt; right){
        if (height[left] &amp;lt; height[right]){
            if (height[left] &amp;lt;= maxLeft){
                sum += maxLeft - height[left];
            }
            else {
                maxLeft = height[left];
            }
            left ++;
        }
        else {
            if (height[right] &amp;lt;= maxRight){
                sum += maxRight - height[right];
            }
            else {
                maxRight = height[right];
            }
            right --;
        }
    }
    return sum;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public static int trap(int[] height) {
    int dp[] = new int [height.length];
    int sum = 0;
    int max = 0;
    for (int i = 0; i &amp;lt; height.length; ++i){
        if (max &amp;lt; height[i]) max = height[i];
        dp[i] = max;
    }
    max = 0;
    for (int i = height.length-1; i &amp;gt;= 0; --i){
        if (max &amp;lt; height[i]) max = height[i];
        sum += Math.min(max, dp[i]) - height[i];
    }
    return sum;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 3&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;public static int trap(int [] height){
        //using stack
        if (height.length == 0) return 0;
        Stack&amp;lt;Integer&amp;gt; s = new Stack&amp;lt;Integer&amp;gt;();
        int sum = 0;
        for (int i = 0; i &amp;lt; height.length; ){
            if (s.isEmpty()) s.push(i++);
            else if (height[s.peek()] &amp;gt;= height[i]) s.push(i++);
            else {
                int top = s.pop();
                if (!s.isEmpty()){
                    sum += (Math.min(height[s.peek()], height[i]) - height[top]) * (i - s.peek() - 1);
                }
                else continue;
            }
        }
        return sum;
    }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-42. Trapping Rain Water</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-74/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-74/</guid><pubDate>Fri, 20 Dec 2019 08:38:06 GMT</pubDate><content:encoded>&lt;h1&gt;Problem&lt;/h1&gt;
&lt;p&gt;Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it &lt;strong&gt;in-place&lt;/strong&gt;.&lt;/p&gt;
&lt;h1&gt;Example&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;Input: 
[
  [1,1,1],
  [1,0,1],
  [1,1,1]
]
Output: 
[
  [1,0,1],
  [0,0,0],
  [1,0,1]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Input: 
[
  [0,1,2,0],
  [3,4,5,2],
  [1,3,1,5]
]
Output: 
[
  [0,0,0,0],
  [0,4,5,0],
  [0,3,1,0]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Solution 1&lt;/h1&gt;
&lt;p&gt;这道题目特别注重对空间复杂度的控制，最优的解法应当时常数空间。空间开销最大的当属O(mn). 即，遍历一遍矩阵，
得到一个mxn的矩阵，其中每一个元素表示matrix上对应位置上的元素是不是应该被置为0.鉴于其过于简单，我们略去其
实现。&lt;/p&gt;
&lt;h1&gt;Solution 2&lt;/h1&gt;
&lt;p&gt;在O(mxn)复杂度的基础上，我们可以将复杂度减小至O(m+n). 因为从上一种方法的实现中我们发现，其实我们并不需要记录每一个元素
，只需要记录行和列的索引。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public void setZeroes(int[][] matrix) {
        //O(m_n) space
        int [] rows = new int [matrix.length];
        int [] cols = new int [matrix[0].length];
        for (int i = 0; i &amp;lt; matrix.length; ++i){
            for (int j = 0; j &amp;lt; matrix[0].length; ++j){
                if (matrix[i][j] == 0){
                    rows[i] = 1;
                    cols[j] = 1;
                }
            }
        }
        for (int i = 0; i &amp;lt; matrix.length; ++i){
            if (rows[i] == 1){
                for (int j = 0; j &amp;lt; matrix[i].length; ++j){
                    matrix[i][j] = 0;
                }
            }
        }
        for (int j = 0; j &amp;lt; matrix[0].length; ++j){
            if (cols[j] == 1){
                for (int i = 0; i &amp;lt; matrix.length; ++i){
                    matrix[i][j] = 0;
                }
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;Solution 3&lt;/h1&gt;
&lt;p&gt;有没有常数复杂度的实现呢？我很自然地想到了位运算。但是因为最大只能接受64x64地矩阵作为输入，所以失败了。
在看到被人地题解后恍然大悟。O(1)的复杂度自然要求我们把信息存入到矩阵中。我们不妨征用matrix[0][...]和matrix[...][0]作为记录matrix[1..m][1...n]的置零信息。至于第0列行第0行，我们只需要2个单独的变量即可。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public void setZeroes(int[][] matrix) {
        int row = 0;
        int col = 0;
        for (int i = 0; i &amp;lt; matrix.length; ++i){
            for (int j = 0; j &amp;lt; matrix[i].length; ++j){
                if (matrix[i][j] == 0){
                    if (i == 0 &amp;amp;&amp;amp; col == 0) col = 1;
                    if (j == 0 &amp;amp;&amp;amp; row == 0) row = 1;
                    matrix[i][0] = 0;
                    matrix[0][j] = 0;
                }
            }
        }
        for (int i = 1; i &amp;lt; matrix.length; ++i){
            for (int j = 1; j &amp;lt; matrix[i].length; ++j){
                if (matrix[0][j] == 0 | matrix[i][0] == 0) matrix[i][j] = 0;
            }
        }
        if (row == 1){
            for (int i = 0; i &amp;lt; matrix.length; ++i){
                matrix[i][0] = 0;
            }
        }
        if (col == 1){
            for (int i = 0; i &amp;lt; matrix[0].length; ++i){
                matrix[0][i] = 0;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-32. Longest Valid Parentheses</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-32/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-32/</guid><pubDate>Tue, 10 Dec 2019 08:51:00 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 32. Longest Valid Parentheses 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a string containing just the characters &apos;(&apos; and &apos;)&apos;, find the length of the longest valid (well-formed) parentheses substring.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;(()&quot;
Output: 2
Explanation: The longest valid parentheses substring is &quot;()&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;)()())&quot;
Output: 4
Explanation: The longest valid parentheses substring is &quot;()()&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;题目要求我们从一串由左括号和右括号组成的字符串中找出最长的有效子串，有效指well-formed,即闭合的．我们首先想到可以使用栈来判断这个串是不是闭合的，即在扫描完所有字符之后，栈是不是空的．&lt;/p&gt;
&lt;p&gt;但是本题要求我们找出闭合的最长子串．我们可以想象，对于一个不闭合的字符串，使用栈进行扫描之后在栈里面残留的是匹配不了的元素，而可以完成匹配的子串恰恰在他们中间．因此，我们只需要在入栈的时候记录入栈字符的index,就可以计算出成功匹配的子串的长度，进而找到最大值．&lt;/p&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int longestValidParentheses(String s) {
        Stack&amp;lt;Integer&amp;gt; st = new Stack&amp;lt;Integer&amp;gt;();
        for (int i = 0; i &amp;lt; s.length(); ++i){
            if (s.charAt(i) == &apos;(&apos;) st.push(i);
            else{
                if (!st.empty()){
                    if (s.charAt(st.peek()) != &apos;(&apos;) st.push(i);
                    else st.pop();
                }
                else st.push(i);
            }   
        }
        if (st.empty()) return s.length();
        int max = 0;
        int e = s.length();
        while (!st.empty()){
            int start = st.pop();
            max = Math.max(max, e - start - 1);
            e = start;
        }
        return Math.max(max, e);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;p&gt;如果不借助于栈，我们该怎么解决这个问题呢？联想到我们之前曾用dp的方法求解过最长连续子串的问题，与这个题目很类似，因此我们考虑如何把此题套到动态规划里面．如果我们用dp[i]来表示以s[i]结尾的最长子串的第一个字符所在位置的话，我们可以列出以下公式&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;if (s[i] == &apos;(&apos;) , dp[i] = -1;&lt;/li&gt;
&lt;li&gt;if (s[i] == &apos;)&apos;)
&lt;ul&gt;
&lt;li&gt;if (s[i-1] == &apos;(&apos;), dp[i] = dp[i-2];&lt;/li&gt;
&lt;li&gt;if (s[i-1] == &apos;)&apos;)
&lt;ul&gt;
&lt;li&gt;if (s[dp[i-1]-1] == &apos;(&apos;), 说明s[i]可以成功匹配，dp[i] = dp[i-1]-1;&lt;/li&gt;
&lt;li&gt;if (s[dp[i-1]-1] != &apos;(&apos;), 那么dp[i] = -1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int longestValidParentheses(String s) {
        int dp[] = new int [s.length()];
        if (s.length() &amp;lt;= 1) return 0;
        //dp[i] denotes the start position of the valid parentheses ended at i
        dp[0] = -1;
        if (s.charAt(1) == &apos;)&apos; &amp;amp;&amp;amp; s.charAt(0) == &apos;(&apos;) dp[1] = 0;
        else dp[1] = -1;
        int max = 0;
        if (dp[1] &amp;gt;= 0) max = Math.max(0, 2 - dp[1]);

        for (int i = 2; i &amp;lt; s.length(); ++i){
            if (s.charAt(i) == &apos;(&apos;) dp[i] = -1;
            else if (s.charAt(i) == &apos;)&apos; &amp;amp;&amp;amp; s.charAt(i-1) == &apos;(&apos;){
                if (dp[i-2] &amp;gt;= 0) dp[i] = dp[i-2] ;
                else dp[i] = i-1;
            }
            else {
                // ))
                if (dp[i-1] == 0) dp[i] = -1;
                else if (dp[i-1] == -1) dp[i] = -1;
                else if (s.charAt(dp[i-1] - 1) == &apos;(&apos;){
                    if (dp[i-1] - 2 &amp;gt;= 0 &amp;amp;&amp;amp; dp[dp[i-1]-2] == -1) dp[i] = dp[i-1]-1;
                    else if (dp[i-1] - 2 &amp;gt;= 0) dp[i] = dp[dp[i-1]-2];
                    else dp[i] = dp[i-1] - 1;
                }
                else dp[i] = -1;
            }
            if (dp[i] &amp;gt;= 0) max = Math.max(max, i - dp[i] + 1);
        }
        for (int i = 0; i &amp;lt; s.length(); ++i){
            System.out.printf(&quot;%d &quot;, dp[i]);
        }
        System.out.println();
        return max;
    }
}
``&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-15. 3Sum</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-23/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-23/</guid><pubDate>Thu, 05 Dec 2019 06:59:34 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 23. Merge k Sorted Lists 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
[
  1-&amp;gt;4-&amp;gt;5,
  1-&amp;gt;3-&amp;gt;4,
  2-&amp;gt;6
]
Output: 1-&amp;gt;1-&amp;gt;2-&amp;gt;3-&amp;gt;4-&amp;gt;4-&amp;gt;5-&amp;gt;6
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;之前做过将两个linked list 合并的题目，即为归并排序的思路，这道题要求我们将k个排好序的链表做归并．有两种思路&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;将k个list的归并不断分解为两个list的归并，即采用二分的方法&lt;/li&gt;
&lt;li&gt;采用最直观的思路，既然k个列表都是排好序的，那么在任何时刻，表头的元素都是当前最小的元素．可以使用priority queue来找到最小的元素，并且更换表头．&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) return null;
        return part(lists, 0, lists.length - 1);
    }
    public static ListNode part(ListNode[] lists,int s, int t) {
        if (s == t) return lists[s];
        ListNode l1 = part(lists, s, (s+t)/2);
        ListNode l2 = part(lists, (s+t)/2+1, t);
        return merge(l1, l2);
    }
    public static ListNode merge(ListNode l1, ListNode l2){
        ListNode zero = new ListNode(0);
        ListNode tmp = zero;
        while (l1 != null &amp;amp;&amp;amp; l2 != null){
            if (l1.val &amp;lt; l2.val){ tmp.next = l1; l1 = l1.next;}
            else {tmp.next = l2; l2 = l2.next;}
            tmp = tmp.next;
        }
        while (l1 != null) {tmp.next = l1; l1 = l1.next; tmp = tmp.next;}
        while (l2 != null) {tmp.next = l2; l2 = l2.next; tmp = tmp.next;}
        return zero.next;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
        public static ListNode mergeKLists(ListNode[] lists) {
        if (lists.length == 0) return null;
        //priority queue
        PriorityQueue&amp;lt;ListNode&amp;gt; q = new PriorityQueue&amp;lt;ListNode&amp;gt;(lists.length, 
            new Comparator&amp;lt;ListNode&amp;gt;() {
                @Override
                public int compare(ListNode l1, ListNode l2){
                    if (l1.val &amp;lt; l2.val){
                        return -1;
                    }
                    else if (l1.val &amp;gt; l2.val){
                        return 1;
                    }
                    else return 0;
                }
            }
        );
        for (ListNode l : lists){
            if (l != null) q.add(l);
        }
        ListNode zero = new ListNode(0);
        ListNode tmp = zero;
        while (!q.isEmpty()){
            ListNode l = q.poll();
            tmp.next = l;
            tmp = tmp.next;
            if (l.next != null) q.add(l.next);
        }
        return zero.next;

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 3&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static ListNode mergeKLists(ListNode[] lists) {
        if (lists.length ==0) return null;
        ListNode zero = new ListNode(0);
        ListNode tmp = zero;
        while (true){
            ListNode min_node = find_min_node(lists);
            if (min_node == null) break;
            tmp.next = min_node;
            tmp = tmp.next;
        }
        return zero.next;
    }
    public static ListNode find_min_node(ListNode[] lists) {
        ListNode res = help(lists, 0, lists.length-1);
        if (res == null) return null;
        for (int i = 0; i &amp;lt; lists.length; ++i){
            //System.out.print(res);
            //System.out.println(lists[i]);
            if (res.equals(lists[i])){
                lists[i] = res.next;
                break;
            }
        }
        return res;
    }
    public static ListNode help(ListNode[] lists, int s, int t){
        //System.out.printf(&quot;%d %d\n&quot;, s, t);
        if (s == t) return lists[s];
        ListNode l1 = help(lists, s, (t+s)/2);
        ListNode l2 = help(lists, (t+s)/2 + 1, t);
        if (l1 == null &amp;amp;&amp;amp; l2 == null ) return null;
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val &amp;lt; l2.val) return l1;
        else return l2;
    }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-31. Next Permutation</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-31/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-31/</guid><pubDate>Thu, 05 Dec 2019 05:59:11 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 31. Next Permutation 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Implement &lt;strong&gt;next permutation&lt;/strong&gt;, which rearranges numbers into the lexicographically next greater permutation of numbers.&lt;/p&gt;
&lt;p&gt;If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).&lt;/p&gt;
&lt;p&gt;The replacement must be in-place and use only constant extra memory.&lt;/p&gt;
&lt;p&gt;Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;对n个元素的集合我们进行排列，叫permutation. 对于排列的集合，我们可以按照字母序(lexicographically)进行排序．而这道题目就要求我们在给定一个排列的情况下，找到它下一个排列．&lt;/p&gt;
&lt;p&gt;首先我们需要认清一个排列的规律．&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字母序排序的第一个排列是n个元素的升序排列，最后一个排列是n个元素的降序排列．&lt;/li&gt;
&lt;li&gt;所以我们可以把每个排列分成两半，前一半不一定．而后一半一定是降序的，．&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么我们如果得到字典序的下一个排列呢．首先，找出这两个半相交的地方，即index最大的出现顺序的地方．在此之后是降序列．我们要使得排列变得更大，就要排列出更大的降序列，所以要将这个顺序对变成降序．那么要变成怎么样的降序呢？当然是比当前大的排列里最小的．那么，我们就需要找出他之后的元素里比他大的最小的，进行swap,之后将后面的元素进行排序，得到新子集合的最小排列．&lt;/p&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    //1. first find the smallest ascending order(wrong)
    //2, we should find adjacent elements in ascending order.
    for (int i = nums.length-1;  i &amp;gt;= 1; --i){
        if (nums[i] &amp;gt; nums[i-1]){
            for (int j = nums.length - 1; j &amp;gt;= i; --j){
                if (nums[j] &amp;gt; nums[i-1]){
                    int tmp = nums[i-1];
                    nums[i-1] = nums[j];
                    nums[j] = tmp;
                    Arrays.sort(nums, i, nums.length);
                    return ;
                }
            }
        }
    }
    int tmp = 0;
    for (int i = 0; i &amp;lt; nums.length/2; ++i){
        tmp = nums[i];
        nums[i] = nums[nums.length - i - 1];
        nums[nums.length - i - 1] = tmp;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-15. 3Sum</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-15/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-15/</guid><pubDate>Tue, 03 Dec 2019 09:05:50 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 15. 3Sum 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.&lt;/p&gt;
&lt;p&gt;The solution set must not contain duplicate triplets.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这是一道two-pointer的题目．通过O(n)的遍历将自由度为3的搜索题目降到自由度为2.由此我们可以转化为2sum的问题．
至于如何解决2sum的问题，我们首先进行排序，再使用two pointer进行搜索，总的复杂度为O(n^2).&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; threeSum(int[] nums) {
        Arrays.sort(nums);
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = new LinkedList&amp;lt;&amp;gt;();
        int s = 0;
        int t = 0;
        for (int i = 0; i &amp;lt; nums.length - 2; ++i){
            if (i &amp;gt; 0 &amp;amp;&amp;amp; nums[i] == nums[i-1]) continue;
            s = i + 1;
            t = nums.length - 1;
            while ( s &amp;lt; t){
                if (s &amp;gt; i+1 &amp;amp;&amp;amp; t &amp;lt; nums.length - 1 &amp;amp;&amp;amp; nums[s] == nums[s-1] &amp;amp;&amp;amp; nums[t] == nums[t+1]){
                    s ++;
                    t --;
                    continue;
                }
                if (nums[s] + nums[t] == - nums[i]){
                    LinkedList&amp;lt;Integer&amp;gt; l = new LinkedList&amp;lt;&amp;gt;();
                    l.add(nums[i]);
                    l.add(nums[s]);
                    l.add(nums[t]);
                    res.add(l);
                    s ++;
                    t --;
                }
                else if (nums[s] + nums[t] &amp;gt; - nums[i]) t --;
                else s ++;
            }
        }
        return res;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-18. 4Sum</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-18/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-18/</guid><pubDate>Tue, 03 Dec 2019 09:05:50 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 18. 4Sum 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.&lt;/p&gt;
&lt;p&gt;The solution set must not contain duplicate triplets.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is:
[
  [-1, 0, 1],
  [-1, -1, 2]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这是一道two-pointer的题目．通过O(n)的遍历将自由度为3的搜索题目降到自由度为2.由此我们可以转化为2sum的问题．
至于如何解决2sum的问题，我们首先进行排序，再使用two pointer进行搜索，总的复杂度为O(n^2).&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; threeSum(int[] nums) {
        Arrays.sort(nums);
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = new LinkedList&amp;lt;&amp;gt;();
        int s = 0;
        int t = 0;
        for (int i = 0; i &amp;lt; nums.length - 2; ++i){
            if (i &amp;gt; 0 &amp;amp;&amp;amp; nums[i] == nums[i-1]) continue;
            s = i + 1;
            t = nums.length - 1;
            while ( s &amp;lt; t){
                if (s &amp;gt; i+1 &amp;amp;&amp;amp; t &amp;lt; nums.length - 1 &amp;amp;&amp;amp; nums[s] == nums[s-1] &amp;amp;&amp;amp; nums[t] == nums[t+1]){
                    s ++;
                    t --;
                    continue;
                }
                if (nums[s] + nums[t] == - nums[i]){
                    LinkedList&amp;lt;Integer&amp;gt; l = new LinkedList&amp;lt;&amp;gt;();
                    l.add(nums[i]);
                    l.add(nums[s]);
                    l.add(nums[t]);
                    res.add(l);
                    s ++;
                    t --;
                }
                else if (nums[s] + nums[t] &amp;gt; - nums[i]) t --;
                else s ++;
            }
        }
        return res;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-10. Regular Expression Matching</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-10/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-10/</guid><pubDate>Mon, 02 Dec 2019 06:18:49 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 10. Regular Expression Matching 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an input string (s) and a pattern (p), implement regular expression matching with support for &apos;.&apos; and &apos;*&apos;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&apos;.&apos; Matches any single character.&lt;/li&gt;
&lt;li&gt;&apos;*&apos; Matches zero or more of the preceding element.
The matching should cover the entire input string (not partial).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; could be empty and contains only lowercase letters &lt;code&gt;a-z&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p&lt;/code&gt; could be empty and contains only lowercase letters &lt;code&gt;a-z&lt;/code&gt;, and characters like &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input :
s = &quot;aa&quot;
p = &quot;a&quot;
Output: false
Explanation: &quot;a&quot; does not match the entire string &quot;aa&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
s = &quot;aa&quot;
p = &quot;a*&quot;
Output: true
Explanation: &apos;*&apos; means zero or more of the preceding element, &apos;a&apos;. Therefore, by repeating &apos;a&apos; once, it becomes &quot;aa&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 3&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
s = &quot;ab&quot;
p = &quot;.*&quot;
Output: true
Explanation: &quot;.*&quot; means &quot;zero or more (*) of any character (.)&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 4&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
s = &quot;aab&quot;
p = &quot;c*a*b&quot;
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches &quot;aab&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 5&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
s = &quot;mississippi&quot;
p = &quot;mis*is*p*.&quot;
Output: false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题目要求我们实现一个正则匹配的引擎，包括&lt;code&gt;*&lt;/code&gt;和&lt;code&gt;.&lt;/code&gt;．我刚开始想用贪心匹配解决，发现不能解决全串匹配的问题，也就是说，贪心匹配不保证我们考虑到所有可能的情况．根据leetcode discussion提供的思路，我们使用动态规划的方法解决此问题．&lt;/p&gt;
&lt;p&gt;我们开辟二维数组dp[i][j]来表示我们是否可以用p[0...j]的正则表达式成功匹配s[0...i]的字符串．递归公式我们可以这样考虑．假设我们在dp[i][j]之前已经计算好了dp[0...i-1][0..j-1]. 至于dp[i][j],我们考虑三种不同的情况，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;s[i] == p[j]. 那么如果我们就可以继承dp[i-1][j-1].&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;p[j] == &apos;.&apos;,  同样，我们可以继承dp[i-1][j-1].&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;p[j] == &apos;*&apos;. 这意味着p[j-1] 是[a-z]的字符．所以我们需要将讨论p[j-1]与s[i]的关系．&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;p[j-1] == &apos;.&apos; 或 p[j-1] == s[i]. 在这种情况下，我们可以有不同的可能，只要有一种可能能够成功匹配，我们就能成功匹配．　如果我们不适用`[a-z|.]*&apos;进行匹配，结果是dp[i][j] = dp[i][j-2]. 如果我们只匹配一次，dp[i][j] = dp[i][j-1]. (相当于不用*); 如果我们匹配多次，dp[i][j] = dp[i-1][j].&lt;/li&gt;
&lt;li&gt;p[j-1] 不能和s[i]进行匹配，即匹配失败．所以，dp[i][j] = dp[i][j-2];&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最终返回dp[i][j]即为结果．&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;需要注意的是在初始化的时候需要初始化dp[0][0..j].&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static boolean isMatch(String s, String p){
        int dp[][] = new int [s.length()+1][p.length()+1];
        dp[0][0] = 1;
        for (int i = 1; i &amp;lt;= p.length(); ++i){
            if (p.charAt(i-1) == &apos;*&apos;) dp[0][i] = dp[0][i-2];
        }
        for (int i = 1; i &amp;lt;= s.length(); ++i){
            for (int j = 1; j &amp;lt;= p.length(); ++j){
                if (p.charAt(j-1) == s.charAt(i-1)){
                    dp[i][j] = dp[i-1][j-1];
                }
                else if (p.charAt(j-1) == &apos;.&apos;){
                    dp[i][j] = dp[i-1][j-1];
                }
                else if (p.charAt(j-1) == &apos;*&apos;){
                    //in this condition, we have to check dp[i-1][j-1] and dp[i][j-1] and dp[i-1][j]
                    if (p.charAt(j-2) == s.charAt(i-1) || p.charAt(j-2) == &apos;.&apos;) {
                        dp[i][j] = dp[i][j-2] | dp[i-1][j] | dp[i][j-1];
                                // no matches | multiple matches | single match
                    }
                    else dp[i][j] = dp[i][j-2];
                }
                //default 0
            }
        }
        // for (int i = 0; i &amp;lt; s.length(); ++i){
        //     for (int j = 0; j &amp;lt; p.length(); ++j){
        //         System.out.printf(&quot;%d &quot;, dp[i][j]);
        //     }
        //     System.out.println();
        // }
        if (dp[s.length()][p.length()] == 1) return true;
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-494. Target Sum</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-494/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-494/</guid><pubDate>Mon, 02 Dec 2019 06:18:49 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 494. Target Sum 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.&lt;/p&gt;
&lt;p&gt;Find out how many ways to assign symbols to make sum of integers equal to target S.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: nums is [1, 1, 1, 1, 1], S is 3. 
Output: 5
Explanation: 

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

There are 5 ways to assign symbols to make the sum of nums be target 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Note&lt;/h2&gt;
&lt;p&gt;The length of the given array is positive and will not exceed 20.
The sum of elements in the given array will not exceed 1000.
Your output answer is guaranteed to be fitted in a 32-bit integer.&lt;/p&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;第一反应这道题是搜索题，因为题目很明显地提到了一个target sum 和每个元素的两条分支，所以我们可以写出基于深度有限搜索的solution 1, 时间复杂度为O(2^n).&lt;/p&gt;
&lt;h2&gt;Solution 1: bfs with recursive call&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int findTargetSumWays(int[] nums, int S) {
        return help(nums, S, 0);
    }
    public static int help(int[] nums, int S, int start){
        if (start &amp;gt; nums.length - 1) return 0;
        if (start == nums.length - 1 &amp;amp;&amp;amp; nums[start] == 0 &amp;amp;&amp;amp; nums[start] == S) return 2;
        else if (start == nums.length - 1 &amp;amp;&amp;amp; nums[start] == S) return 1;
        else if (start == nums.length - 1 &amp;amp;&amp;amp; nums[start] == -S) return 1;
        return help(nums, S - nums[start], start + 1) + help(nums, S + nums[start], start + 1);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis 2&lt;/h2&gt;
&lt;p&gt;但事实上，这道题是一道背包问题．对于每个元素我们有两种选项，+或-．
暴力破解的方法复杂度太高，我们考虑更高效的方法．我们首先可以把+,-两个符号换一种角度看待，可以看做将数组分为两半，用其中一般取减去另外一半得到target.&lt;/p&gt;
&lt;p&gt;因此&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sum(p) - sum(n) = S
2sum(p) = S + sum(p&amp;amp;n)
sum(p) = (S + sum(p&amp;amp;n))/2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们只需要找到子数组，是的其和为$(S + sum(p&amp;amp;n))/2$即可．
由于题目中已经指出数组中所有元素之和不超过1000,所以我们可以构造dp[1-1000],dp[i]表示有多少种组合可以构成sum为i的子数组．&lt;/p&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int findTargetSumWays(int[] nums, int S){
        int sum = 0;
        for (int num : nums) sum += num;
        if (sum &amp;lt; S) return 0;
        if ((sum+S) % 2 == 1) return 0;
        //because in this case it is not possible
        int target = (sum + S)/2;

        int dp[] = new int [target+1];
        dp[0] = 1;
        for (int i = 0; i &amp;lt; nums.length; ++i){
            for (int j = target; j &amp;gt;= nums[i]; --j){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[target];
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-283. Move Zeroes</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-283/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-283/</guid><pubDate>Sun, 01 Dec 2019 06:03:12 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 283. Move Zeroes 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an array nums, write a function to move all 0&apos;s to the end of it while maintaining the relative order of the non-zero elements.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Note&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You must do this in-place without making a copy of the array.&lt;/li&gt;
&lt;li&gt;Minimize the total number of operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;题目要求我们要in-place,所以我们不能开辟新的空间．这道题我们使用快慢指针．快指针指向当前元素，慢指针指向新数组的当前元素．&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static void moveZeroes(int[] nums) {
        int cur = 0;
        for (int i = 0; i &amp;lt; nums.length; ++i){
            if (nums[i] != 0){
                nums[cur++] = nums[i];
            }
        }
        for (int i = cur; i &amp;lt; nums.length; ++i){
            nums[i] = 0;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-152. Maximum Product Subarray</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-152/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-152/</guid><pubDate>Thu, 28 Nov 2019 08:24:51 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 152. Maximum Product Subarray 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [-2,0,-1]
Output: 0
Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 1: brute force&lt;/h2&gt;
&lt;p&gt;计算出每个可能的subarray的product,选择最大的．时间复杂度为O(n^2).需要注意的是只需要开O(n)的数组即可．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int maxProduct(int[] nums) {
        int dp[] = new int [nums.length];
        int max = -10000;
        for (int i = nums.length - 1; i &amp;gt;= 0; --i){
            for (int j = i; j &amp;lt; nums.length; ++j){
                if (j == i) dp[j] = nums[j];
                else dp[j] = dp[j-1] * nums[j];
                max= Math.max(max, dp[j]);
            }
        }
        return max;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2 : optimized&lt;/h2&gt;
&lt;p&gt;我们想想有没有可能在线性时间内解决问题呢？如果我们使用dp[i]来表示以nums[i]结尾的最大的product呢？然而这样简单的做法是不行的．因为我们的数组中会出现负数．例如[2,-2,-4].最大的乘积是16但是按照简单的dp只能得到8.　深入观察可以发现，我们之所以得不到最大解是因为在中间结果中存在很小的负数再乘以负数变得很大的情况．既然这样，我们就维护两个数组，分别存储最大的数和最小的数．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int maxProduct(int[] nums) {
        if (nums.length == 1) return nums[0];
        int dp_max[] = new int [nums.length+1];
        int dp_min[] = new int [nums.length+1];
        int max = -10000;
        for (int i = 1; i &amp;lt;= nums.length; ++i){
            dp_max[i] = Math.max(Math.max(nums[i-1], dp_max[i-1] * nums[i-1]), dp_min[i-1] * nums[i-1]);
            dp_min[i] = Math.min(Math.min(nums[i-1], dp_max[i-1] * nums[i-1]), dp_min[i-1] * nums[i-1]);
            max=Math.max(max, dp_max[i]);
        }
        return max;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-371. Sum of Two Integers</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-371/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-371/</guid><pubDate>Thu, 28 Nov 2019 08:15:40 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 371. Sum of Two Integers 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: a = 1, b = 2
Output: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: a = -2, b = 3
Output: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;在不使用内置加法运算的情况下求两个数的和，我们需要依赖位运算．在位运算中我们需要关注两种运算&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;异或．两个数异或的结果恰恰是不进位加法的结果．&lt;/li&gt;
&lt;li&gt;与．两个数与运算后的结果恰恰是进位的结果．&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以对于a+b,我们求得异或和与的结果之后，再将两个结果相加，长此以往，直到其中一个为零：没有进位了，或者加法结果恰好为2^n．&lt;/p&gt;
&lt;h2&gt;Solution 1: bfs with recursive call&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int getSum(int a, int b) {
        while (a != 0 &amp;amp;&amp;amp; b != 0){
            int tmp = a ^ b;
            b = (a &amp;amp; b) &amp;lt;&amp;lt; 1;
            a = tmp;
            //System.out.printf(&quot;%d %d\n&quot;, a, b);
        }
        if (a == 0) return b;
        else return a;
    }
｝
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-136. Single Number</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-136/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-136/</guid><pubDate>Thu, 28 Nov 2019 05:39:12 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 136. Single Number 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a &lt;strong&gt;non-empty&lt;/strong&gt; array of integers, every element appears twice except for one. Find that single one.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [2,2,1]
Output: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: [4,1,2,1,2]
Output: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 1: brute force&lt;/h2&gt;
&lt;p&gt;时间复杂度为线性．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int singleNumber(int[] nums) {
        Map&amp;lt;Integer,Integer&amp;gt; m = new HashMap&amp;lt;Integer, Integer&amp;gt;();
        for (int num : nums){
            m.put(num, m.getOrDefault(num, 0)+1);
        }
        for (int num : m.keySet()){
            if (m.get(num) == 2) continue;
            if (m.get(num) == 1) return num;
        }
        return 0;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2: bit operation&lt;/h2&gt;
&lt;p&gt;时间复杂度为线性，利用了相同的数异或为0的特点．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int singleNumber(int[] nums) {
        int res = 0;
        for (int num : nums){
            res = res ^ num;
        }
        return res;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;拓展&lt;/h2&gt;
&lt;p&gt;有一个 n 个元素的数组，除了两个数只出现一次外，其余元素都出现两次，让你找出这两个只出现一次的数分别是几，要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;如果我们按照前面提到的方法对所有元素求异或的话，我们是得不到最后答案的，因为我们得到的结果是这两个数的异或，那么我们想到，再遍历一次数组，对每个元素单独再异或，只可能有三种结果，其中只会出现一次的两个结果即为我们想要的答案．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int singleNumber(int [] nums){
        int res = 0;
        for (int num : nums){
            res = res ^ num;
        }
        Map&amp;lt;Integer,Integer&amp;gt; m = new HashMap&amp;lt;Integer, Integer&amp;gt;();
        for (int num : nums){
            int tmp = res ^ num;
            m.put(num, m.getOrDefault(num, 0) + 1);
        }
        for (int num : m.keySet()){
            if (m.get(num) == 1) System.out.printf(&quot;%d \n&quot;, num);
        }
        return 0;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-5. Longest Palindromic Substring</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-5/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-5/</guid><pubDate>Tue, 26 Nov 2019 03:37:02 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 5. Longest Palindromic Substring 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;babad&quot;
Output: &quot;bab&quot;
Note: &quot;aba&quot; is also a valid answer.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;cbbd&quot;
Output: &quot;bb&quot;s
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题的题意为从字符串的子串中找出最长的回文串．而回文可以递归定义，所以我们可以靠动态规划来解决这个问题．复杂度为O(n^2).&lt;/p&gt;
&lt;h2&gt;Solution 1: dp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static String longestPalindrome(String s) {
        int l = s.length();
        if (l == 0) return s;
        int max = 0;
        int max_i = 0;
        int max_j = 0;
        int dp[][] = new int [l][l];
        for (int i = 0; i &amp;lt; l; ++i){
            dp[i][i] = 1;
        }
        for (int i = 1; i &amp;lt;= l - 1; ++i){
            for (int j = 0; j &amp;lt; l - i; ++j){
                if (s.charAt(j) == s.charAt(j+i)){
                    if (i == 1) dp[j][j+i] = 1;
                    else dp[j][j+i] = dp[j+1][j+i-1];
                }
                if (dp[j][j+i] == 1){
                    if (i + 1 &amp;gt; max){
                        max = i + 1;
                        max_i = j;
                        max_j = j+i;
                    } 
                }
            }
        }
        return s.substring(max_i, max_j+1);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2: dfs solution with stack&lt;/h2&gt;
&lt;p&gt;还有一种思路就是先选定回文的中心，在进行扩展，遍历所有可能的中心得到最长的结果．&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static String longestPalindrome(String s) {
    //each palindrome is centered
    if (s.length() == 0) return s;
    int l = 0;
    int r = 0;
    int max_l = 0; int max_r = 0; int max_ = 0;
    for (int i = 0; i &amp;lt; s.length() - 1; ++i){
        //extend(i, i, )
        l = i;r = i;
        while ( l &amp;gt;= 0 &amp;amp;&amp;amp; r &amp;lt;= s.length() - 1 &amp;amp;&amp;amp; s.charAt(l) == s.charAt(r)) {
            l --; r ++;
        }
        if (max_ &amp;lt; r - l - 1){
            max_ = r - l - 1;
            max_l = l+1; max_r = r-1;
        }
        //extent(i,i+1)
        l = i;r = i+1;
        while ( l &amp;gt;= 0 &amp;amp;&amp;amp; r &amp;lt;= s.length() - 1 &amp;amp;&amp;amp; s.charAt(l) == s.charAt(r)) {
            l --; r ++;
        }
        if (max_ &amp;lt; r - l - 1){
            max_ = r - l - 1;
            max_l = l+1; max_r = r-1;
        }
    }
    return s.substring(max_l, max_r+1);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-200. Number of Islands</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-200/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-200/</guid><pubDate>Tue, 26 Nov 2019 02:39:33 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 200. Number of Islands 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a 2d grid map of &apos;1&apos;s (land) and &apos;0&apos;s (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
11110
11010
11000
00000

Output: 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
11000
11000
00100
00011

Output: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题目的本质就是求矩阵中连续区域的个数．很简单的想法就是用图搜索的方法来探索整个矩阵，将所有访问过的位置全部标为０以免下次在访问．在bfs的实现中，我们需要借助queue，在dfs的实现中，我们可以使用递归，也可以使用stack.&lt;/p&gt;
&lt;h2&gt;Solution 1: bfs with recursive call&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int numIslands(char[][] grid) {
        if (grid.length ==0 || grid[0].length == 0) return 0;
        int cnt = 0;
        for (int i = 0; i &amp;lt; grid.length; ++i){
            for (int j = 0; j &amp;lt; grid[i].length; ++j){
                if (grid[i][j] == &apos;1&apos;){
                    dfs(i,j,grid);
                    cnt += 1;
                }
            }
        }
        return cnt;
    }
    public static void dfs(int i, int j, char[][] grid){
        if (i &amp;lt; 0 || j &amp;lt; 0 || i &amp;gt; grid.length - 1 || j &amp;gt; grid[i].length - 1 || grid[i][j] == &apos;0&apos;) return ;
        grid[i][j] = &apos;0&apos;;
        dfs(i+1, j ,grid);
        dfs(i, j+1 ,grid);
        dfs(i, j-1 ,grid);
        dfs(i-1, j ,grid);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2: dfs solution with stack&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int numIslands(char[][] grid) {
        if (grid.length ==0 || grid[0].length == 0) return 0;
        int cnt = 0;
        Stack&amp;lt;Pair&amp;gt; s = new Stack&amp;lt;Pair&amp;gt;();
        for (int i = 0; i &amp;lt; grid.length; ++i){
            for (int j = 0; j &amp;lt; grid[i].length; ++j){
                if (grid[i][j] == &apos;1&apos;){
                    System.out.printf(&quot;%d %d\n&quot;, i, j);
                    s.push(new Pair(i,j));
                    while (!s.isEmpty()){
                        Pair p = s.pop();
                        grid[p.x][p.y] = &apos;0&apos;;
                        if (p.x+1 &amp;lt; grid.length &amp;amp;&amp;amp; grid[p.x+1][p.y] == &apos;1&apos;) s.push(new Pair(p.x+1,p.y));
                        if (p.y+1 &amp;lt; grid[0].length &amp;amp;&amp;amp; grid[p.x][p.y+1] == &apos;1&apos;) s.push(new Pair(p.x,p.y+1));
                        if (p.x-1 &amp;gt;= 0 &amp;amp;&amp;amp; grid[p.x-1][p.y] == &apos;1&apos;) s.push(new Pair(p.x-1,p.y));
                        if (p.y-1 &amp;gt;= 0 &amp;amp;&amp;amp; grid[p.x][p.y-1] == &apos;1&apos;) s.push(new Pair(p.x,p.y-1));
                    }
                    cnt += 1;
                }
            }
        }
        return cnt;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 3 bfs&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    private static class Pair{
        int x ;
        int y ;
        public Pair(int xx, int yy){this.x = xx; this.y = yy;}
        //public Pair(){this.x = -1; this.y = -1;}
    }
    public static int numIslands(char[][] grid) {
        //dfs?
        if (grid.length == 0 || grid[0].length == 0) return 0;
        int flag[][] = new int [grid.length][grid[0].length];
        Stack&amp;lt;Pair&amp;gt; s = new Stack&amp;lt;Pair&amp;gt;();
        int cur_i = 0;
        int group = 0;
        while (true){
            for (int i = cur_i; i &amp;lt; grid.length; ++i){
                for (int j = 0; j &amp;lt; grid[i].length; ++j){
                    //System.out.printf(&quot;a:%d %d\n&quot;, i, j);
                    if (grid[i][j] == &apos;1&apos; &amp;amp;&amp;amp; flag[i][j] == 0){
                        Pair tmp = new Pair(i,j);
                        s.push(tmp);
                        flag[i][j] = 1;
                        cur_i = i;
                        break;
                    }
                }
                if (!s.isEmpty()){
                    break;
                }
            }
            if (s.isEmpty()) break;
            while (!s.isEmpty()){
                Pair p = s.pop();
                if (p.x+1 &amp;lt; grid.length &amp;amp;&amp;amp; flag[p.x+1][p.y] == 0 &amp;amp;&amp;amp; grid[p.x+1][p.y] == &apos;1&apos;){
                    Pair tmp = new Pair (p.x + 1, p.y);
                    s.push(tmp);
                    flag[p.x+1][p.y] = 1;
                }
                if (p.x-1 &amp;gt;= 0 &amp;amp;&amp;amp; flag[p.x-1][p.y] == 0 &amp;amp;&amp;amp; grid[p.x-1][p.y] == &apos;1&apos;){
                    Pair tmp = new Pair (p.x - 1, p.y);
                    s.push(tmp);
                    flag[p.x-1][p.y] = 1;
                }
                if (p.y+1 &amp;lt; grid[0].length &amp;amp;&amp;amp; flag[p.x][p.y+1] == 0 &amp;amp;&amp;amp; grid[p.x][p.y+1] == &apos;1&apos;){
                    Pair tmp = new Pair(p.x, p.y + 1);
                    s.push(tmp);
                    flag[p.x][p.y+1] = 1;
                }
                if (p.y-1 &amp;gt;= 0 &amp;amp;&amp;amp; flag[p.x][p.y-1] == 0 &amp;amp;&amp;amp; grid[p.x][p.y-1] == &apos;1&apos;){
                    Pair tmp = new Pair (p.x, p.y-1);
                    s.push(tmp);
                    flag[p.x][p.y-1] = 1;
                }
            }
            group += 1;
        }
        return group;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 4: Union find&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution{
    public static int numIslands(char[][] grid) {
        //union search
        if (grid.length == 0 || grid[0].length == 0) return 0;
        Pair[][] father = new Pair [grid.length][grid[0].length];
        for (int i = 0; i &amp;lt; grid.length; ++i){
            for (int j = 0; j &amp;lt; grid[0].length; ++j){
                if (grid[i][j] == &apos;1&apos;){
                    father[i][j] = new Pair(i,j);
                }
                else father[i][j] = new Pair(-1, -1);
            }
        }
        for (int i = 0; i &amp;lt; grid.length; ++i){
            for (int j = 0; j &amp;lt; grid[i].length; ++j){
                if (grid[i][j] == &apos;0&apos;) continue;
                if (i + 1 &amp;lt; grid.length &amp;amp;&amp;amp; grid[i+1][j] == &apos;1&apos; &amp;amp;&amp;amp;(findroot(i, j, father).x != findroot(i+1, j, father).x || findroot(i, j, father).y != findroot(i+1, j, father).y)){
                    join(findroot(i, j, father), findroot(i+1, j, father));
                }
                if (j + 1 &amp;lt; grid[i].length &amp;amp;&amp;amp; grid[i][j+1] == &apos;1&apos; &amp;amp;&amp;amp; (findroot(i, j, father).x != findroot(i, j+1, father).x || findroot(i, j, father).y != findroot(i, j+1, father).y)){
                    join(findroot(i, j, father), findroot(i, j+1, father));
                }
                if (i - 1 &amp;gt;= 0 &amp;amp;&amp;amp; grid[i-1][j] == &apos;1&apos; &amp;amp;&amp;amp; (findroot(i, j, father).x != findroot(i-1, j, father).x || findroot(i, j, father).y != findroot(i-1, j, father).y)){
                    join(findroot(i, j, father), findroot(i-1, j, father));
                }
                if (j - 1 &amp;gt;= 0 &amp;amp;&amp;amp; grid[i][j-1] == &apos;1&apos; &amp;amp;&amp;amp; (findroot(i, j, father).x != findroot(i, j-1, father).x || findroot(i, j, father).y != findroot(i, j-1, father).y)){
                    join(findroot(i, j, father), findroot(i, j-1, father));
                }
                // for (int ii = 0; ii &amp;lt; grid.length; ++ii){
                //     for (int jj = 0; jj &amp;lt; grid[0].length; ++jj){
                //         System.out.printf(&quot;[%d %d] &quot;, father[ii][jj].x, father[ii][jj].y);
                //     }
                //     System.out.println();
                // }
                // System.out.println();
            }
        }
        int group = 0;
        for (int i = 0; i &amp;lt; grid.length; ++i){
            for (int j = 0; j &amp;lt; grid[i].length; ++j){
                if (father[i][j].x == i &amp;amp;&amp;amp; father[i][j].y ==j){
                    group += 1;
                }
            }
        }
        return group;
    }
    public static Pair findroot(int i, int j, Pair[][] father){
        //System.out.printf(&quot;%d %d\n&quot;, i,j);
        while (father[i][j].x != i || father[i][j].y != j){
            int tmp = i;
            i = father[i][j].x;
            j = father[tmp][j].y;
            //System.out.printf(&quot;%d %d\n&quot;, i,j);
        }
        return father[i][j];
    }
    public static void join(Pair f1, Pair f2) {
        f2.x = f1.x;
        f2.y = f1.y;
    }
}&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-516.longest-palindromic-subsequence</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-516/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-516/</guid><pubDate>Tue, 26 Nov 2019 02:39:33 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 516.longest-palindromic-subsequence 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a string s, find the longest palindromic subsequence&apos;s length in s. You may assume that the maximum length of s is 1000.&lt;/p&gt;
&lt;h2&gt;Example 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
&quot;bbbab&quot;
Output:
4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Example 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:

&quot;cbbd&quot;
Output:
2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题目和第5题类似，不过第五题中我们考虑的时候包含区间头尾的回文串，而在这道题目中，我们需要考虑区间内的最长回文串，是一道很明显的动态规划题目，递归公式为&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dp[i][j] = max(dp[i+1][j], dp[i][j-1]) if s[i] neq s[j]
dp[i][j] = max(dp[i+1][j], dp[i][j-1], dp[i+1][j-1]+2) if s[i] eq s[j]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 1: bfs with recursive call&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int longestPalindromeSubseq(String s) {
        int l = s.length();
        if (l == 0) return 0;
        int dp[][] = new int [l][l];
        //int start = 0;
        for (int i = l-1; i &amp;gt;= 0; --i){
            for (int j = i; j &amp;lt; l ; ++j){
                //System.out.printf(&quot;%d %d\n&quot;, i, j);
                if (i == j) dp[i][j] = 1;
                //dp[i][j] = Math.max(dp[i][i+len-2], dp[i+1][i+len]);
                else if (s.charAt(i) == s.charAt(j)){
                    dp[i][j] = dp[i+1][j-1]+2;
                }
                else dp[i][j] = Math.max(dp[i][j-1], dp[i+1][j]);
            }
        }
        return dp[0][l-1];

    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-11. Container With Most Water</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-11/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-11/</guid><pubDate>Mon, 18 Nov 2019 12:01:31 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 11. Container With Most Water 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You may not slant the container and n is at least 2.&lt;/p&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
Input: [1,8,6,2,5,4,8,3,7]
Output: 49
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题虽然代码简单，但是要求我们具有足够的观察能力和分析能力得出规律．首先，最简单的方法就是暴力求解，遍历长度为1-n的所有情况，复杂度为O(n^2).&lt;/p&gt;
&lt;p&gt;我们现在试着找些规律．假设给定的lines为[a,b,...,c,d].首先长度为n的container的最大容积只能是(a,d).对于长度为n-1的两个容器，我们观察可知，只有将较短的那根线向内移动，才有可能得到更大的容积，否则不会超过n时候的情况（木桶效应）．依次类推，我们每次移动较短的那块板，以寻求更大的容积．&lt;/p&gt;
&lt;p&gt;但是我们好像忽略了一种情况，假如a&amp;lt;d,那么我们下一步会取[b,...,c,d]那么有没有可能在[a,b,...,c]中取得较高值的可能呢？答案是否定的．如果在后者有更高的值，那么肯定有一条边是a,假设另外一条边是e,如果a&amp;lt;e，那么最大的面积实际上应该在[a,d]取得．如果a&amp;gt;=e，那么e&amp;lt;=a&amp;lt;d,最大的面积也是在[a,d]取到．反证结束．&lt;/p&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int maxArea(int[] height) {
        int max = 0;
        int l = 0;
        int r = height.length - 1;
        while ( l &amp;lt; r){
            max = Math.max( Math.min(height[l], height[r]) * (r - l), max);
            if (height[l] &amp;lt; height[r]){
                l++;
            }
            else {
                r--;
            }
        }
        return max;
    }
}

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-4. Median of Two Sorted Arrays</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-4/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-4/</guid><pubDate>Mon, 18 Nov 2019 05:52:01 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 4. Median of Two Sorted Arrays 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;There are two sorted arrays nums1 and nums2 of size m and n respectively.&lt;/p&gt;
&lt;p&gt;Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).&lt;/p&gt;
&lt;p&gt;You may assume nums1 and nums2 cannot be both empty.&lt;/p&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
nums1 = [1, 3]
nums2 = [2]

The median is 2.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题是leetcode上第一道hard题目，其困难主要在于题意的理解和边界情况的处理上。&lt;/p&gt;
&lt;h3&gt;题意理解&lt;/h3&gt;
&lt;p&gt;首先，我们要换一种方式来理解中位数。对于中位数，我们有两种理解&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过中位数可以将数组分为两半，使得前一半的元素都小于后一半的元素。&lt;/li&gt;
&lt;li&gt;中位数即为第len/2个元素。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;事实上，从两种不同的理解出发，我们可以得到两种不同的解法。基于第一种理解，我们可以不断进行二分查找，找到中间元素.而对于第二种理解，我们可以从两个数组的头部出发，寻找第len/2个元素．我们首先列出第二种理解对应的解法．其中index表示我们要在两个列表联合构成的集合中要找到的第index个元素．&lt;/p&gt;
&lt;h3&gt;边界情况分析&lt;/h3&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        if ( (m + n)% 2 == 0){
            int l = helper(nums1, nums2, 0, m-1, 0, n-1, (m+n)/2);
            int r = helper(nums1, nums2, 0, m-1, 0, n-1, (m+n)/2+1);
            //System.out.printf(&quot;%d %d&quot;, l, r);
            return ((double)l + r)/2;
        }
        else {
            return helper(nums1, nums2, 0, m-1, 0, n-1, (m+n)/2+1);
        }
    }
    public static int helper(int[] nums1, int []nums2, int s1, int e1, int s2, int e2, int index){
        if (s1 &amp;gt; e1){
            return nums2[s2 + index - 1];
        }
        else if (s2 &amp;gt; e2){
            return nums1[s1 + index - 1];
        }
        if (index == 1){
            return Math.min(nums1[s1], nums2[s2]);
        }
        int mid1 = s1 + index / 2 - 1;
        int mid2 = s2 + index / 2 - 1;
        if (mid1 &amp;gt; e1 ) return helper(nums1, nums2, s1, e1, mid2+1, e2, index - index/2);
        else if (mid2 &amp;gt; e2) return helper(nums1, nums2, mid1+1, e1, s2, e2, index - index/2);
        if (nums1[mid1] &amp;lt;= nums2[mid2]){
            return helper(nums1, nums2, mid1+1, e1, s2, e2, index - index/2);
        }
        else {
            return helper(nums1, nums2, s1, e1, mid2+1, e2, index - index/2);
        }
    }

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;p&gt;Brute force,相当于merge sort&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int nums3[] = new int [nums1.length + nums2.length];
        int i = 0, j = 0, z = 0;
        for (; i &amp;lt; nums1.length &amp;amp;&amp;amp; j &amp;lt; nums2.length;){
            if (nums1[i] &amp;lt;= nums2[j]){
                nums3[z++] = nums1[i++];
            }
            else nums3[z++] = nums2[j++];
        }
        for (;i &amp;lt; nums1.length; ++i){
            nums3[z++] = nums1[i];
        }
        for (;j &amp;lt; nums2.length; ++j){
            nums3[z++] = nums2[j];
        }

        if (nums3.length % 2 == 0){
            return (double)(nums3[nums3.length / 2 - 1] + nums3[nums3.length / 2]) / 2;
        }
        else return (double)nums3[nums3.length / 2];
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Solution 3&lt;/h3&gt;
</content:encoded></item><item><title>LeetCode 题解-221. Maximal Square</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-221/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-221/</guid><pubDate>Wed, 13 Nov 2019 12:50:12 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 221. Maximal Square 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a 2D binary matrix filled with 0&apos;s and 1&apos;s, find the largest square containing only 1&apos;s and return its area.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Output: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;看到网格类题目，首先想到动态规划。既然要用动态规划，我们必须要找到递归规律。我们很自然的想到，用dp[i][j]表示以(i,j)为右下角的最大的正方形。那么&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果matrix[i][j]为0,则dp[i][j]必为0&lt;/li&gt;
&lt;li&gt;如果matrix[i][j]为1,则有两种情况
&lt;ul&gt;
&lt;li&gt;dp[i-1][j-1] &amp;gt; max(dp[i-1][j], dp[i][j-1]) 这种情况下，我们最大能得到min(dp[i-1][j], dp[i][j-1])+1的正方形。&lt;/li&gt;
&lt;li&gt;dp[i-1][j-1] &amp;lt; max(dp[i-1][j], dp[i][j-1]) 这种情况下，我们最大能得到dp[i-1][j-1]+1的正方形。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以&lt;code&gt;dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Solution 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
public static int maximalSquare(char[][] matrix) {
    //right down corner
    if (matrix.length &amp;lt; 1 || matrix[0].length &amp;lt; 1) return 0;
    int dp[][] = new int[matrix.length + 1][matrix[0].length + 1];
    int max = 0;
    for (int i = 1; i &amp;lt; dp.length; ++i){
        for (int j = 1; j &amp;lt; dp[i].length; ++j){
            if (matrix[i-1][j-1] == &apos;0&apos;) dp[i][j] = 0;
            else dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
            max = Math.max(dp[i][j], max);
        }
    }
    return max*max;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;p&gt;事实上，我们并不需要mxn的矩阵来储存所有的dp元素，2xn的矩阵即可，由此我们可以将空间复杂度降低至O(n),达到理论上最优。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static int maximalSquare(char[][] matrix) {
    //right down corner
    if (matrix.length &amp;lt; 1 || matrix[0].length &amp;lt; 1) return 0;
    int dp[][] = new int[2][matrix[0].length + 1];
    int max = 0;
    for (int i = 1; i &amp;lt; matrix.length + 1; ++i){
        for (int j = 1; j &amp;lt; matrix[0].length + 1; ++j){
            int cur = (i-1) % 2;
            int prev = i % 2;
            if (matrix[i-1][j-1] == &apos;0&apos;) dp[cur][j] = 0;
            else dp[cur][j] = Math.min(Math.min(dp[prev][j], dp[cur][j-1]), dp[prev][j-1]) + 1;
            max = Math.max(dp[cur][j], max);
        }
    }

    return max*max;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 3&lt;/h2&gt;
&lt;p&gt;那么，时间复杂度还有没有优化的空间呢？好像没有了，但是我的代码只能击败5%的人，为什么呢？&lt;/p&gt;
</content:encoded></item><item><title>LeetCode 题解-47. Permutation II</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-47/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-47/</guid><pubDate>Thu, 31 Oct 2019 13:42:12 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 47. Permutation II 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a collection of numbers that might contain duplicates, return all possible unique permutations.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;由题意可知，这道题是一道排列题，而且题目要求返回所有的符合的排列结果，这给我们的求解带来了很大的难度。题意很简单，就是将若干个数字的全部排列返回，并剔除重复的元素。&lt;/p&gt;
&lt;p&gt;我们不放回想我们自己是怎么得到一串数列的全排列的。对于n个数的序列，我们假设得到了后n-1个数的所有排列，那么我们只需要将第一个数插入到这些排列的不同的位置即可得到想要n个数的全排列。由此思路我们可以写出递归法的代码。在java具体实现的时候要注意深拷贝的问题。通过已有list复制初始化并不能另开辟空间。&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    public static List&amp;lt;List&amp;lt;Integer&amp;gt; &amp;gt; permuteUnique(int[] nums) {
        return my(nums, 0)；
    }
    public static List&amp;lt;List&amp;lt;Integer&amp;gt; &amp;gt; my(int [] nums, int s){
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; newres = new ArrayList&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt;  ();
        if (s &amp;gt;= nums.length) return newres;
        else if (s == nums.length - 1){
            List&amp;lt;Integer&amp;gt; a = new ArrayList&amp;lt;Integer&amp;gt;();
            a.add(nums[s]);
            newres.add(a);
            return newres;
        }
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; res = my(nums, s+1);
        // create new permutation
        Iterator&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; iter = res.iterator();
        while (iter.hasNext()){
            List&amp;lt;Integer&amp;gt; l = iter.next();
            for (int i = 0; i &amp;lt;= l.size(); ++i){
                List&amp;lt;Integer&amp;gt; tmp = new ArrayList&amp;lt;Integer&amp;gt;();
                for (int j : l){
                    tmp.add(j);
                }
                tmp.add(i, nums[s]);
                if (newres.contains(tmp)){
                    continue;
                }
                newres.add(tmp);
            }
            iter.remove();
        }
        return newres;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optimized Solution&lt;/h2&gt;
&lt;p&gt;回溯法
回溯法&lt;/p&gt;
</content:encoded></item><item><title>LeetCode 题解-104. maximum depth of binary tree</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-104/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-104/</guid><pubDate>Mon, 28 Oct 2019 08:20:09 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 104. maximum depth of binary tree 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a binary tree, find its maximum depth.&lt;/p&gt;
&lt;p&gt;The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: A leaf is a node with no children.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Given binary tree [3,9,20,null,null,15,7], return its depth = 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 1: Iterative solution&lt;/h2&gt;
&lt;p&gt;这道题目的描述很符合递归算法的特征，而树又是非常适合递归算法的数据结构，因此我们很容易写出两行的代码。需要注意的只有对空节点的判断。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想要知道树的最大深度，我们只需要进行一次遍历即可。事实上，深度优先和宽度优先的算法都适用与此题。&lt;/p&gt;
&lt;h2&gt;Solution 2&lt;/h2&gt;
&lt;p&gt;深度有限遍历。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        Stack&amp;lt;TreeNode&amp;gt; s= new Stack&amp;lt;TreeNode&amp;gt;();
        Stack&amp;lt;Integer&amp;gt; ss= new Stack&amp;lt;Integer&amp;gt;();
        s.push(root);
        ss.push(1);
        int max = 0;
        while (!s.isEmpty()){
            TreeNode n = s.pop();
            int d = ss.pop();
            max = Math.max(d, max);
            if (n.left != null){
                s.push(n.left);
                ss.push(d+1);
            }
            if (n.right != null){
                s.push(n.right);
                ss.push(d+1);
            }
        }
        return max;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 3&lt;/h2&gt;
&lt;p&gt;宽度优先遍历&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        Queue&amp;lt;TreeNode&amp;gt; q = new LinkedList&amp;lt;TreeNode&amp;gt;();
        Queue&amp;lt;Integer&amp;gt; qq = new LinkedList&amp;lt;Integer&amp;gt;();
        q.add(root);
        qq.add(1);
        int max = 0;
        while (!q.isEmpty()){
            TreeNode n = q.poll();
            int d = qq.poll();
            max = Math.max(d, max);
            if (n.left != null){
                q.add(n.left);
                qq.add(d+1);
            }
            if (n.right != null){
                q.add(n.right);
                qq.add(d+1);
            }
        }
        return max;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution 4&lt;/h2&gt;
&lt;p&gt;事实上，我们还有一种更有效的遍历求解方法，即层次遍历。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    public static int maxDepth(TreeNode root) {
        if (root == null) return 0;
        Queue&amp;lt;TreeNode&amp;gt; q = new LinkedList&amp;lt;TreeNode&amp;gt;();
        q.add(root);
        int max = 0;
        while (!q.isEmpty()){
            int size = q.size();
            for (int i = 0; i &amp;lt; size; ++i){
                TreeNode tmp = q.poll();
                if (tmp.left != null) q.add(tmp.left);
                if (tmp.right != null) q.add(tmp.right);
            }
            max += 1;
        }
        return max;
    }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-206. Reverse Linked List</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-206/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-206/</guid><pubDate>Mon, 28 Oct 2019 08:20:09 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 206. Reverse Linked List 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Reverse a singly linked list.&lt;/p&gt;
&lt;p&gt;A linked list can be reversed either iteratively or recursively. Could you implement both?&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: 1-&amp;gt;2-&amp;gt;3-&amp;gt;4-&amp;gt;5-&amp;gt;NULL
Output: 5-&amp;gt;4-&amp;gt;3-&amp;gt;2-&amp;gt;1-&amp;gt;NULL
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题的核心其实是指针的交换。方便起见，我们引入多余的head指针，指向第一个node。迭代实现和递归实现如下。&lt;/p&gt;
&lt;h2&gt;Iterative Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    ListNode zero = new ListNode(0);
    zero.next = head;
    ListNode tmp = new ListNode(0);
    while (head != null){
        tmp = head.next;
        if (tmp == null) break;
        head.next = head.next.next;
        tmp.next = zero.next;
        zero.next = tmp;
    }
    return zero.next;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Recursive Solution&lt;/h2&gt;
&lt;p&gt;递归方法的核心是先reverse剩余的n-1个指针，再将第一个指针接到最后。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static ListNode reverseList(ListNode head) {
        if (head == null) return head;
        ListNode tail = head;
        while (tail.next != null){
            tail = tail.next;
        }
        myreverse(head, tail);
        head.next = null;
        return tail;
    }
    public static void myreverse(ListNode head, ListNode tail){
        if (head == tail){
            return ;
        }
        myreverse(head.next, tail);
        head.next.next = head;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-26. Remove duplicates from sorted array</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-26/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-26/</guid><pubDate>Mon, 28 Oct 2019 08:20:09 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 26. Remove duplicates from sorted array 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length.&lt;/p&gt;
&lt;p&gt;Do not allocate extra space for another array, you must do this by &lt;strong&gt;modifying the input array in-place&lt;/strong&gt; with O(1) extra memory.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Given nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively.
It doesn&apos;t matter what you leave beyond the returned length.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Given nums = [0,0,1,1,1,2,2,3,3,4],

Your function should return length = 5, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively.

It doesn&apos;t matter what values are set beyond the returned length
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题目标签是&lt;em&gt;easy&lt;/em&gt;，但是还是很巧妙的。由于题目要求我们只能使用O(1)的额外空间，所以我们不能新开数组。再由题意可知，我们只需要保证数组前半段的正确性，不用考虑后半段的内容，所以我们可以将数组后半段的空间利用起来。由于数组已经被排好序了，所以我们可以预见数组将会符合这样的格式：&lt;code&gt;A(+)…B(+)&lt;/code&gt;。一个简单的想法是，对于重复出现的元素，只保留一个，将剩下的用后续元素覆盖掉。&lt;/p&gt;
&lt;p&gt;具体实现方法即为快慢指针法。我们让慢指针&lt;code&gt;p1&lt;/code&gt;指向去掉重复元素序列的最后一个元素，让快指针&lt;code&gt;p2&lt;/code&gt;指向下一个要加入序列的元素。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始时&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;都指向起始元素。&lt;/li&gt;
&lt;li&gt;如果&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;指向的元素相同，保持&lt;code&gt;p1&lt;/code&gt;不动，&lt;code&gt;p2&lt;/code&gt;指向下一个元素。所以&lt;code&gt;p2&lt;/code&gt;最终会指向大于第一个大于&lt;code&gt;p1&lt;/code&gt;的元素。&lt;/li&gt;
&lt;li&gt;如果&lt;code&gt;p1&lt;/code&gt;和&lt;code&gt;p2&lt;/code&gt;指向的元素不同，我们将&lt;code&gt;p2&lt;/code&gt;处的元素swap到&lt;code&gt;p1+1&lt;/code&gt;的位置。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
        public static int removeDuplicates(int[] nums) {
        int point1 = 0;
        int point2 = 0;
        int tmp;
        // find the number point2 greater than point1,
        // and put it at point1+1
        if (nums.length &amp;lt;= 1){
            return nums.length;
        }
        while (point2 &amp;lt; nums.length){
            if (nums[point1] == nums[point2]){
                point2 += 1;
            }
            else {
                point1 += 1;
                point2 += 1;
                //swap point1 with point2-1
                tmp = nums[point1];
                nums[point1] = nums[point2-1];
                nums[point2-1] = tmp;
            }
        }
        return point1 + 1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Another Solution&lt;/h2&gt;
&lt;p&gt;下面这种带有loop的算法起始是快慢指针的一种变形了，其本质是相同的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static boolean removeDuplicates(String s) {
        if (nums.length &amp;lt;= 1){
            return nums.length;
        }
        int cur = 1;
        for (int i = 1; i &amp;lt; nums.length; ++i){
            if (nums[i] &amp;gt; nums[cur]){
                nums[cur++] = nums[i];
            }
        }
        return cur;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-25. Reverse Nodes in k-Group</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-25/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-25/</guid><pubDate>Mon, 28 Oct 2019 08:20:09 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 25. Reverse Nodes in k-Group 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.&lt;/p&gt;
&lt;p&gt;k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Given this linked list: 1-&amp;gt;2-&amp;gt;3-&amp;gt;4-&amp;gt;5

For k = 2, you should return: 2-&amp;gt;1-&amp;gt;4-&amp;gt;3-&amp;gt;5

For k = 3, you should return: 3-&amp;gt;2-&amp;gt;1-&amp;gt;4-&amp;gt;5
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Note&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Only constant extra memory is allowed.&lt;/li&gt;
&lt;li&gt;You may not alter the values in the list&apos;s nodes, only nodes itself may be changed.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题和206题一样，也是一道链表题目，核心是指针的交换操作。关键点总结如下&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在链表最前端设置zero节点。&lt;/li&gt;
&lt;li&gt;每次选中k个节点，不停地将cur的next节点挪到start的后面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;事实上，代码还可以更简练，通过recursive的方法。
事实上，代码还可以更简练，通过recursive的方法。&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static ListNode reverseKGroup(ListNode head, int k) {
        ListNode zero = new ListNode(0);
        zero.next = head;
        ListNode start = zero;
        ListNode tail = zero;
        while (true){
            for (int i = 0; i &amp;lt; k ; ++i){
                tail = tail.next;
                if (tail == null) break;
            }
            if (tail == null) break;
            start = reverseK(start, k);
            tail = start;
        }
        return zero.next;
    }
    public static ListNode reverseK(ListNode start, int k){
        ListNode cur = start.next;
        for (int i = 0; i &amp;lt; k - 1; ++i){
            ListNode tmp = cur.next.next;
            cur.next.next = start.next;
            start.next = cur.next;
            cur.next =  tmp;
        }

        return cur;
    }
}

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-198. House Robber</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-198/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-198/</guid><pubDate>Thu, 24 Oct 2019 11:47:04 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 198. House Robber 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and &lt;strong&gt;it will automatically contact the police if two adjacent houses were broken into on the same night&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight &lt;strong&gt;without alerting the police&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
             Total amount you can rob = 1 + 3 = 4.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
             Total amount you can rob = 2 + 9 + 1 = 12.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题是一道比较简单的动态规划问题。因为所有的动态规划问题的解决都基于局部最优的推广，在这个问题中，我们想要在不违反约束的情况下拿到n个house最多的现金，所以我们考虑，如果我们已经有前n-1个房子的最优解，这个时候只需要考虑抢不抢第n的房子就行了。根据题目给的约束我们可以得到
\begin{equation}
dp[n] = Max(dp[n-1], dp[n-2] + a[n])
\end{equation}
其中，dp[n]代表前n个房子最多的钱，a[n]表示第n的房子的现金。在实现的时候，为了方便计算，我们将dp[0],dp[1]均设置为0，dp[n]对应低n-2个房子。&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
class Solution {
    public static int rob(int[] nums) {
        if (nums.length == 0) return 0;
        if (nums.length == 1) return nums[0];
        int dp[] = new int [nums.length + 1];
        dp[1] = nums[0];
        for (int i = 2; i &amp;lt; nums.length + 1; ++i){
            dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);
        }
        return Math.max(dp[nums.length], dp[nums.length - 1]);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optimized Solution&lt;/h2&gt;
&lt;p&gt;仔细观察发现，空间复杂度还有优化的空间，因为我们用到的数组其实每次迭代只需要2个元素。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int  rob(int [] nums) {
        if (nums.length == 0) return 0;
            if (nums.length == 1) return nums[0];
            int a = 0, b = 0;
            int tmp;
            for (int i = 0; i &amp;lt; nums.length; ++i){
                tmp = Math.max(b, a + nums[i]);
                a = b;
                b = tmp;
            }
            return Math.max(a,b);
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-20. Valid Parentheses</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-20/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-20/</guid><pubDate>Thu, 24 Oct 2019 10:01:32 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 20. Valid Parentheses 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a string containing just the characters &apos;(&apos;, &apos;)&apos;, &apos;{&apos;, &apos;}&apos;, &apos;[&apos; and &apos;]&apos;, determine if the input string is valid.&lt;/p&gt;
&lt;p&gt;An input string is valid if:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open brackets must be closed by the same type of brackets.&lt;/li&gt;
&lt;li&gt;Open brackets must be closed in the correct order.
Note that an empty string is also considered valid.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Example 1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;()&quot;
Output: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;()[]{}&quot;
Output: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;(]&quot;
Output: false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example 3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;([)]&quot;
Output: false
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题比较简单，只需要使用栈这种数据结构就可以轻松解决。遇到左括号，我们push进栈，遇到右括号，如果此时栈为空或者栈顶不是对应的左括号，我们即可判断匹配失败。如果是对应的左括号，那么pop出栈顶。然而我发现使用java自带的栈的代码只能在leetcode上超过60%的时间复杂度，说明我们代码还有提升空间。进一步考虑使用数组来代替java数据结构，我们只需要创建一个足够大的数组（不需要考虑扩张以及banlance factor），并且记录栈顶的位置即可。&lt;/p&gt;
&lt;p&gt;更进一步的观察我们发现，由于需要考虑三种不同的括号，我们不得不对三个括号的匹配进行列举，使得代码冗长。如果我们在push进栈时将我们期待的右括号push进去，那么我们在后续匹配时就不用遍历三种可能的情况，而是直接比较当前括号和栈顶括号是否相同即可。&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static boolean isValid(String s) {
        if (s.length() == 0) return true;
        if (s.length() % 2 == 1) return false;
        Stack&amp;lt;Integer&amp;gt; st = new Stack&amp;lt;Integer&amp;gt;();
        for (int i = 0; i &amp;lt; s.length(); ++i){
            if (s.charAt(i) == &apos;(&apos;){
                st.push(0);
            }
            else if(s.charAt(i) == &apos;{&apos;){
                st.push(2);
            }
            else if (s.charAt(i) == &apos;[&apos;){
                st.push(4);
            }
            else if (s.charAt(i) == &apos;)&apos;){
                if (! st.isEmpty() &amp;amp;&amp;amp; st.peek() == 0){
                    st.pop();
                }
                else return false;
            }
            else if (s.charAt(i) == &apos;}&apos;){
                if (! st.isEmpty() &amp;amp;&amp;amp; st.peek() == 2){
                    st.pop();
                }
                else return false;
            }
            else {
                if (! st.isEmpty() &amp;amp;&amp;amp; st.peek() == 4){
                    st.pop();
                }
                else return false；
            }
        }
        return st.isEmpty();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Optimized Solution&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static boolean isValid(String s) {
        if (s.length() == 0) return true;
        if (s.length() % 2 == 1) return false;
        //Stack&amp;lt;Character&amp;gt; st = new Stack&amp;lt;Character&amp;gt;();
        char arr[] = new char[s.length()];
        int ind = -1;
        // store what we expected
        for (int i = 0; i &amp;lt; s.length(); ++i){
            if (s.charAt(i) == &apos;(&apos;){
                arr[++ind] = &apos;)&apos;;
            }
            else if (s.charAt(i) == &apos;{&apos;){
                arr[++ind] = &apos;}&apos;;
            }
            else if (s.charAt(i) == &apos;[&apos;){
                arr[++ind] = &apos;]&apos;;
            }
            else if (ind &amp;gt;= 0 &amp;amp;&amp;amp; arr[ind] == s.charAt(i)){
                ind --;
            }
            else {
                return false;
            }
        }
        return ind == -1;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-3. Longest Substring Without Repeating Characters</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-3/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-3/</guid><pubDate>Wed, 23 Oct 2019 09:01:45 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 3. Longest Substring Without Repeating Characters 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given a string, find the length of the longest substring without repeating characters.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;h3&gt;Example 1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;abcabcbb&quot;
Output: 3 
Explanation: The answer is &quot;abc&quot;, with the length of 3.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;bbbbb&quot;
Output: 1
Explanation: The answer is &quot;b&quot;, with the length of 1.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Example 3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Input: &quot;pwwkew&quot;
Output: 3
Explanation: The answer is &quot;wke&quot;, with the length of 3. 
             Note that the answer must be a substring, &quot;pwke&quot; is a subsequence and not a substring.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Analysis&lt;/h2&gt;
&lt;p&gt;这道题很明显是一道Two-point的题目。一个很直观的想法就是维护一个sliding-window，从头到尾扫过去，记录所有可能的结果里最长的那个子串。基于这种思想，我们可以得到时间复杂度为$\Theta(n)$，空间复杂度为$O(min(m,n))$。时间复杂度很直观，我们只需要遍历一次；空间复杂度即为我们存储哈希表和sliding-window的空间。分析可知，哈希表的大小最多为字符集的大小或string的长度，所以，我们取两者最小。&lt;/p&gt;
&lt;p&gt;事实上，基于滑动窗口的算法可以有两种不同的具体实现，如下代码所示。两种方案的不同主要集中在哈希表key-value pair的设计的不同。在第一种方案里，我们将字符作为key，将字符在当前窗口里出现的次数作为value，这样，我们可以通过value是否为0来确定该字符是否已经出现过。在第二种方案里，我们将该字符最近一次出现的位置作为value。两种方案的复杂度相同，但是在submmit的时候结果不同：第一种方案的耗时更少一点。至于具体原因，我猜测是因为在第一种方案里，我们在遇到重复字符时手动去更新哈希表所带来的时间消耗小于在第二种方案里不不更新滑动窗口缩水对哈希表的影响所带来for 循环次数的增加。&lt;/p&gt;
&lt;h2&gt;Solution No.1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;    public static int lengthOfLongestSubstring(String s) {
        Map&amp;lt;Character, Integer&amp;gt; flag = new HashMap&amp;lt;Character, Integer&amp;gt;();
        if (s.length() &amp;lt;= 1) return s.length();
        int a=0,b=0;
        int max = 0;
        while (a &amp;lt;= b &amp;amp;&amp;amp; b &amp;lt; s.length()){
            //System.out.printf(&quot;%d %d\n&quot;,b,  flag.get(s.charAt(b)));
            if (flag.getOrDefault(s.charAt(b), 0) == 0){
                flag.put(s.charAt(b), 1);
            }
            else {
                // pop out from front
                while (s.charAt(a) != s.charAt(b) &amp;amp;&amp;amp; a &amp;lt; b){
                    flag.put(s.charAt(a), 0);
                    a+=1;
                }
                a++;
            }
            //System.out.printf(&quot;%d %d\n&quot;, a,b);
            max = Math.max(max, b - a + 1);
            b += 1;
        }
        return max;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution No.2&lt;/h2&gt;
&lt;p&gt;在solution 2中需要注意的是&lt;code&gt;i = Math.max(i, flag.get(s.charAt(j)) + 1);&lt;/code&gt;这句代码使得我们不用去因为滑动窗口左端pointer向左移动而删除字符而对哈希表进行更新，例如可以考虑&lt;code&gt;tmsmfdut&lt;/code&gt;。假设我们现在&lt;code&gt;j = s.length()-1，i= 2&lt;/code&gt;，直接取计算&lt;code&gt;map.get(s.charAt(j))+1&lt;/code&gt;会给我们带来错误的结果，因为我们计算了两个t。
所以通过取i和&lt;code&gt;flag.get(s.charAt(j)) + 1&lt;/code&gt;两者中的最大值，我们可以确保答案是有效的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     public static int lengthOfLongestSubstring(String s) {
        Map&amp;lt;Character, Integer&amp;gt; flag = new HashMap&amp;lt;Character, Integer&amp;gt;();
        if (s.length() &amp;lt;= 1) return s.length();
        int res = 0;
        for (int i = 0, j = 0; j &amp;lt; s.length(); ++j){
            if (flag.containsKey(s.charAt(j))){
                i = Math.max(i, flag.get(s.charAt(j)) + 1);
                //ensure the current window
            }
            flag.put(s.charAt(j), j);
            res = Math.max(res, j - i + 1);
        }
        return res;
    }
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>LeetCode 题解-1.two sum</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-1/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-1/</guid><pubDate>Sun, 20 Oct 2019 08:31:01 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 1. two sum 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;这道题题意很明确，即从一个数组中找出两个数，满足其和为&lt;code&gt;target&lt;/code&gt;的要求，将这两个数的下标以数组的形式返回。首先，考虑&lt;strong&gt;brute-force&lt;/strong&gt;穷举的方法，时间复杂度为 $\Theta(n^2)$，空间复杂度为$\Theta(1)$.&lt;/p&gt;
&lt;p&gt;其次，考虑时间复杂度更低的方案。既然要找出两个数，我们不妨先确定一个数$a$，如果数组中存在$target-a$，那么我们就找到了答案。如果不行的话，我们再考虑其他数。至于如果确定数组包含某个数，我们考虑使用哈希表，来实现线性查询时间。
在具体实现中，我们需要对数组进行两次遍历，第一次建立哈希表，第二次进行查询。需要注意，我们通过将数组元素的index作为value存入到&lt;code&gt;hashmap&lt;/code&gt;中，以避免第二次遍历的时候出现自己加自己的情况。这种算法的时间复杂度为$\Theta(n)$，空间复杂度为$\Theta(n)$.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int[] twoSum(int[] nums, int target) {
        Map&amp;lt;Integer, Integer&amp;gt; m = new HashMap&amp;lt;Integer, Integer&amp;gt;();
        for (int i = 0; i &amp;lt; nums.length; ++i){
            m.put(nums[i], i);
        }
        for (int i = 0; i &amp;lt; nums.length; ++i){
           if (m.get(target - nums[i]) != null &amp;amp;&amp;amp; m.get(target - nums[i]) != i){
               return new int [] {Math.min(i, m.get(target - nums[i])),
                                Math.max(i, m.get(target - nums[i]))};
           }
        }
        return new int [] {0,0};
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Improved Solution&lt;/h2&gt;
&lt;p&gt;事实上，我们只需要一次遍历就可以解决问题。如代码所示，我们在遍历中首先确定查询当前元素的互补元素是否存在，如果存在的话，直接返回结果。然后在将当前元素加入哈希表，这样就可以避免将同一个元素考虑两次。&lt;/p&gt;
&lt;h2&gt;Improved Code&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    public static int[] twoSum(int[] nums, int target) {
        Map&amp;lt;Integer, Integer&amp;gt; m = new HashMap&amp;lt;Integer, Integer&amp;gt;();
        for (int i = 0; i &amp;lt; nums.length; ++i){
            if (m.containsKey(target - nums[i])){
               return new int [] {Math.min(i, m.get(target - nums[i])),
                                Math.max(i, m.get(target - nums[i]))};
            }
            m.put(nums[i], i);
        }
        return new int [] {0,0};
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Comments&lt;/h2&gt;
&lt;p&gt;这个问题其实也可以转换成 &lt;em&gt;&lt;strong&gt;Two Pointer&lt;/strong&gt;&lt;/em&gt; 来求解，只不过需要先排序，找到两个元素之后还需要在原始数组中找到对应的下标。&lt;/p&gt;
</content:encoded></item><item><title>LeetCode 题解-2.add two numbers</title><link>https://zhanglei1949.github.io/posts/leetcode-problem-2/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/leetcode-problem-2/</guid><pubDate>Sun, 20 Oct 2019 04:11:32 GMT</pubDate><content:encoded>&lt;p&gt;对Leetcode 2. two sum 题目的求解。&lt;/p&gt;
&lt;h2&gt;Problem Description&lt;/h2&gt;
&lt;p&gt;You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.&lt;/p&gt;
&lt;p&gt;You may assume the two numbers do not contain any leading zero, except the number 0 itself.&lt;/p&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Input: (2 -&amp;gt; 4 -&amp;gt; 3) + (5 -&amp;gt; 6 -&amp;gt; 4)
Output: 7 -&amp;gt; 0 -&amp;gt; 8
Explanation: 342 + 465 = 807.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;就像用链表实现大整数运算一样，算法没有什么可将的，就是要注意边界条件&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;考虑两个n位数加起来为n+1位数的情况&lt;/li&gt;
&lt;li&gt;考虑0+一个数的情况&lt;/li&gt;
&lt;li&gt;考虑对齐相加进位的实现。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    
    public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode res = new ListNode(0);
        ListNode tmp = res;
        int last = 0;
        while (l1 != null &amp;amp;&amp;amp; l2 != null){
            tmp.next = new ListNode((l1.val + l2.val + last)%10);
            last = (l1.val + l2.val +last) / 10;
            l1 = l1.next;
            l2 = l2.next;
            tmp = tmp.next;
        }
        ListNode temp = null;
        if (l1 != null){
            temp = l1;
        }
        if (l2 != null){
            temp = l2;
        }
        while (temp != null){
            tmp.next = new ListNode((temp.val + last)%10);
            last = (temp.val +last) / 10;
            temp = temp.next;
            tmp = tmp.next;
        }
        if (last &amp;gt; 0){
            tmp.next = new ListNode(last);
        }
        return res.next;
    }
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Cuda10 installation on Ubuntu 16.04</title><link>https://zhanglei1949.github.io/posts/issues-encountered-when-installing-cuda10-on-ubuntu16/</link><guid isPermaLink="true">https://zhanglei1949.github.io/posts/issues-encountered-when-installing-cuda10-on-ubuntu16/</guid><pubDate>Fri, 18 Oct 2019 08:04:36 GMT</pubDate><content:encoded>&lt;p&gt;由于项目要用到的代码对tensorflow版本要求较高，我需要将机器上的tensorflow1.0+cuda-8.0升级到tensorflow2.0+cuda-10.0。然而在重装了cuda10.0+cudnn7.5之后，运行&lt;code&gt;nvidia-smi&lt;/code&gt;显示&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NVIDIA-SMI has failed because it couldn&apos;t communicate with the NVIDIA driver. Make sure that the latest NVIDIA driver is installed and running.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我理解为是nvidia driver的版本不够高，所以直接从nvidia-384升级到nvidia-430,并重启了计算机。结果就发现重启之后电脑黑屏了。最终费了好大的周折将这个问题解决，现在将具体过程记录如下。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;解决黑屏问题。&lt;/p&gt;
&lt;p&gt;首先重启之后屏幕一直处于黑屏状态，我怀疑是nvidia显卡驱动没有正常工作，于是将显示器的输入口接到主板的集显的输出口。终于显示器上出现了低分辨率的变形的输出。为了避免忍受这种痛苦，&lt;code&gt;ctrl+alt+f1&lt;/code&gt;进入tty1的shell中。既然显卡驱动出现了问题，我们考虑彻底卸载nvidia驱动和cuda以重新开始。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt purge nvidia*
sudo /usr/cuda/bin/uninstall_cuda_10.0.pl
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;卸载完成之后重启，遇到了Ubuntu重复登录界面的问题。即便是输入了正确的用户名和口令，也会继续重复登录。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决重登录问题。&lt;/p&gt;
&lt;p&gt;在上网搜索了上多解决方案之后，大多数人都归因与nouveau和opengl的一些异常。遵照前人的方法，首先，禁用掉nouveau驱动。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo vim etc/modprobe.d/blacklist
#在该文件中加入下面两行代码
blacklist nouveau
options nouveau modeset=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新安装nvidia驱动。需要前往nvidia官网下载自己系统对应的可用驱动，建议选择run file。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#首先关掉x-window manager lightdm
sudo service lightdm stop
#安装驱动
sudo **/NVIDIA-Linux-x86_64-VERSION.run –no-x-check –no-nouveau-check –no-opengl-files
sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里需要注意，后面的三个参数很重要，分别指定关闭x-window 服务，禁用nouveau，不安装opengl文件。重启之后，终于可以登入桌面。接着将显示器的接口插在显卡输出口上，同样也出现了桌面。但是新的问题也出现了：Ubuntu桌面坏了，任务栏消失了，快捷键失灵。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决桌面问题。&lt;/p&gt;
&lt;p&gt;初步确定为是桌面程序出现故障，所以选择最简单直接的办法：重装。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt purge ubuntu-desktop
sudo apt autoremove
sudo apt install ubuntu-desktop
#这里可以选择不同的桌面，例如gnome，unity
sudo apt install unity
sudo reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启之后在登录界面选择&lt;code&gt;ubuntu&lt;/code&gt;桌面，即可进入熟悉的桌面。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解决显卡输出帧数降低，显示卡顿的问题。&lt;/p&gt;
&lt;p&gt;新的问题接踵而至。由于驱动版本过高(430)，我的显卡不太支持，图形化界面掉帧严重，特别是观看网页视频的时候目测只有30-40帧。于是回滚到较低版本的nvidia驱动，这里我选择了418。除了从官方网站下载驱动之外，这里介绍另外一种方案。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;software and updates -&amp;gt; additional drivers -&amp;gt; 选择418驱动 -&amp;gt; apply changes -&amp;gt; reboot
终于，我们显示器画面丝滑流畅！&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item></channel></rss>