Skip to content

Commit

Permalink
2021-01-07 23:56:23
Browse files Browse the repository at this point in the history
  • Loading branch information
wizardforcel committed Jan 7, 2021
1 parent 2a91ca7 commit 42b6ae7
Show file tree
Hide file tree
Showing 39 changed files with 4,446 additions and 1 deletion.
20 changes: 19 additions & 1 deletion SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
+ [ApacheCN 数据结构与算法译文集](README.md)
+ [数据结构思维中文版](docs/think-dast-zh/README.md)
+ [前言](docs/think-dast-zh/0.md)
+ [第一章 接口](docs/think-dast-zh/1.md)
+ [第二章 算法分析](docs/think-dast-zh/2.md)
+ [第三章 `ArrayList`](docs/think-dast-zh/3.md)
+ [第四章 `LinkedList`](docs/think-dast-zh/4.md)
+ [第五章 双链表](docs/think-dast-zh/5.md)
+ [第六章 树的遍历](docs/think-dast-zh/6.md)
+ [第七章 到达哲学](docs/think-dast-zh/7.md)
+ [第八章 索引器](docs/think-dast-zh/8.md)
+ [第九章 `Map`接口](docs/think-dast-zh/9.md)
+ [第十章 哈希](docs/think-dast-zh/10.md)
+ [第十一章 `HashMap`](docs/think-dast-zh/11.md)
+ [第十二章 `TreeMap`](docs/think-dast-zh/12.md)
+ [第十三章 二叉搜索树](docs/think-dast-zh/13.md)
+ [第十四章 持久化](docs/think-dast-zh/14.md)
+ [第十五章 爬取维基百科](docs/think-dast-zh/15.md)
+ [第十六章 布尔搜索](docs/think-dast-zh/16.md)
+ [第十七章 排序](docs/think-dast-zh/17.md)
+ [Leetcode C++ 题解](docs/leetcode/cpp/README.md)
+ [1. Two Sum](docs/leetcode/cpp/0001._Two_Sum.md)
+ [2. Add Two Numbers](docs/leetcode/cpp/0002._Add_Two_Numbers.md)
Expand Down
83 changes: 83 additions & 0 deletions docs/think-dast-zh/0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 前言

> 原文:[Preface](http://greenteapress.com/thinkdast/html/thinkdast001.html)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
## 本书背后的哲学

数据结构和算法是过去 50 年来最重要的发明之一,它们是软件工程师需要了解的基础工具。但是在我看来,这些话题的大部分书籍都过于理论,过于庞大,也是“自底向上”的:

过于理论

算法的数学分析基于许多简化假设,它们限制了实践中的可用性。这个话题的许多描述都掩盖了简化,并专注于数学。在这本书中,我介绍了这个话题的最实际的子集,并省略或不强调其余的内容。

过于庞大

这些话题的大多数书籍至少有 500 页,有些超过 1000 页。通过关注我认为对软件工程师最有用的话题,我把这本书限制在 200 页以下。

过于“自底向上”

许多数据结构的书籍着重于数据结构如何工作(实现),而不是使用它们(接口)。在这本书中,我从接口开始,“自顶向下”。读者在学习如何使用 Java 集合框架中的结构之后,再了解它们的工作原理。

最后,有些书将这个材料展示在上下文之外,缺少动机:这只是另一个数据结构!我试图使之生动起来,通过围绕一个应用 - 网页搜索 - 来组织这些话题,它广泛使用数据结构,并且是一个有趣和重要的话题。

这个应用激发了一些话题,通常不会在介绍性数据结构的课中涵盖,包括 Redis 的持久化数据结构。


我已经做出了一些艰难的决定,来进行取舍,但我也做了一些妥协。我包括了大多数读者永远不会使用的一些话题,但是可能在技术面试中,你需要知道这些话题。对于这些话题,我提出了传统的观点和我怀疑的理由。

本书还介绍了软件工程实践的基本方面,包括版本控制和单元测试。大多数章节都包括一个练习,允许读者应用他们学到的内容。每个练习都提供自动化测试,来检查解决方案。对于大多数练习,我在下一章的开头展示我的解决方案。

### 0.1 预备条件

本书面向计算机科学及相关领域的大学生,专业软件工程师,软件工程培训人员和技术面试准备人员。

在你开始读这本书之前,你应该很熟悉 Java,尤其应该知道如何定义一个扩展现有类的新类,或实现一个`interface`。如果你不熟悉 Java 了,这里有两本书可以用于起步:

+ Downey 和 Mayfield,《Think Java》(O'Reilly Media,2016),它面向以前从未编程过的人。
+ Sierra 和 Bates,《Head First Java》(O'Reilly Media,2005),它适用于已经知道另一种编程语言的人。

如果你不熟悉 Java 中的接口,你可能需要在 <http://thinkdast.com/interface> 上完成一个名为“什么是接口”的教程 。

一个词汇注解:“接口”这个词可能会令人困惑。在应用编程接口(API)的上下文中,它指代一组提供某些功能的类和方法。

在 Java 的上下文中,它还指代一个与类相似的语言特性,它规定了一组方法。为了避免混淆,我将使用正常字体中的“接口”来表示接口的一般思想,代码字体的`interface`用于 Java 语言特性。

你还应该熟悉类型参数和泛型类型。例如,你应该知道如何使用类型参数创建对象,如`ArrayList<Integer>`。如果不是,你可以在 <http://thinkdast.com/types> 上了解类型参数。

你应该熟悉 Java 集合框架(JCF​​),你可以阅读 <http://thinkdast.com/collections>。特别是,你应该知道`List interface`,以及`ArrayList``LinkedList`类。

理想情况下,你应该熟悉 Apache Ant,它是 Java 的自动化构建工具。你可以在 <http://thinkdast.com/anttut> 上阅读 Ant 的更多信息。

你应该熟悉 JUnit,它是 Java 的单元测试框架。你可以在 <http://thinkdast.com/junit> 上阅读更多信息。

## 处理代码

本书的代码位于 <http://thinkdast.com/repo> 上的 Git 仓库中 。

Git 是一个“版本控制系统”,允许你跟踪构成项目的文件。Git 控制下的文件集合称为“仓库”。

GitHub 是一个托管服务,为 Git 仓库提供存储和方便的 Web 界面。它提供了几种使用代码的方法:

+ 你可以通过按下`Fork`(派生)按钮,在 GitHub 上创建仓库的副本。如果你还没有 GitHub 帐户,则需要创建一个。派生之后,你可以在 GitHub 上拥有你自己的仓库,你可以使用它们来跟踪你编写的代码。然后,你可以“克隆”仓库,它将文件的副本下载到你的计算机。
+ 或者,你可以克隆仓库而不进行派生。如果你选择此选项,则不需要 GitHub 帐户,但你无法将更改保存在 GitHub 上。
+ 如果你不想使用 Git,你可以使用 GitHub 页面上的`Download`(下载)按钮或此链接<http://thinkdast.com/zip>,以 ZIP 压缩包格式下载代码。

克隆仓库或解压 ZIP 文件后,你应该有一个名为`ThinkDataStructures`的目录,其中有一个名为`code`的子目录。

本书中的示例是使用 Java SE 7 开发和测试的。如果你使用的是较旧的版本,一些示例将无法正常工作。如果你使用的是更新版本,那么它们都应该能用。

## 贡献者

这本书是我为纽约市 Flatiron School 写的课程的一个改编版,它提供了编程和网页开发相关的各种在线课程。他们提供基于这个材料的课程,提供在线开发环境,来自教师和其他学生的帮助,以及结业证书。你可以在 <http://flatironschool.com>上找到更多信息 。

+ 在 Flatiron School,Joe Burgess,Ann John 和 Charles Pletcher 通过实现和测试,提供了来自初始规范的指导,建议和更正。谢谢你们!
+ 我非常感谢我的技术审校员 Barry Whitman, Patrick White 和 Chris Mayfield,他提出了许多有用的建议,并捕获了许多错误。当然,任何剩余的错误都是我的错,而不是他们的错!
+ 感谢 Olin College 的数据结构和算法课程中的教师和学生,他们读了这本书并提供了有用的反馈。

如果你对文本有任何意见或建议,请发送至:<[email protected]>
156 changes: 156 additions & 0 deletions docs/think-dast-zh/1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# 第一章 接口

> 原文:[Chapter 1 Interfaces](http://greenteapress.com/thinkdast/html/thinkdast002.html)
> 译者:[飞龙](https://github.com/wizardforcel)
> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/)
> 自豪地采用[谷歌翻译](https://translate.google.cn/)
本书展示了三个话题:

+ 数据结构:从 Java 集合框架(JCF)中的结构开始,你将学习如何使用列表和映射等数据结构,你将看到它们的工作原理。
+ 算法分析:我提供了技术,来分析代码以及预测运行速度和需要多少空间(内存)。
+ 信息检索:为了激发前两个主题,并使练习更加有趣,我们将使用数据结构和算法构建简单的 Web 搜索引擎。

以下是话题顺序的大纲:

+ 我们将从`List`接口开始,你将编写实现这个接口的两种不同的方式。然后我们将你的实现与 Java `ArrayList``LinkedList`类进行比较。
+ 接下来,我将介绍树形数据结构,你将处理第一个应用程序:一个程序,从维基百科页面读取页面,解析内容,并遍历生成的树来查找链接和其他特性。我们将使用这些工具来测试“到达哲学”的猜想(你可以通过阅读 <http://thinkdast.com/getphil> 来了解)。
+ 我们将了解 Java 的`Map`接口和`HashMap`实现。然后,你将使用哈希表和二叉搜索树来编写实现此接口的类。
+ 最后,你将使用这些(以及其他一些我之前介绍的)类来实现一个 Web 搜索引擎,其中包括:一个查找和读取页面的爬虫程序,一个存储网页内容的索引器,以便有效地搜索,以及一个从用户那里接受查询并返回相关结果的检索器。

让我们开始吧。

## 1.1 为什么有两种`List`

当人们开始使用 Java 集合框架时,有时候会混淆`ArrayList``LinkedList`。为什么 Java 提供两个`List interface`的实现呢?你应该如何选择使用哪一个?我们将在接下来的几章回答这些问题。

我将以回顾`interface`和实现它们的类开始,我将介绍“面向接口编程”的概念。

在最初的几个练习中,你将实现类似于`ArrayList``LinkedList`的类,这样你就会知道他们如何工作,我们会看到,他们每个类都有优点和缺点。对于`ArrayList`,一些操作更快或占用更少的空间;但对于`LinkedList`其他操作更快或空间更少。哪一个更适合于特定的应用程序,取决于它最常执行的操作。

## 1.2 Java 中的接口

Java `interface`规定了一组方法;任何实现这个`interface`的类都必须提供这些方法。例如,这里是`Comparable`的源代码,它是定义在`java.lang`包中的`interface`

```java
public interface Comparable<T> {
public int compareTo(T o);
}
```

这个`interface`的定义使用类型参数`T`,这使得`Comparable`是个泛型类型。为了实现这个`interface`,一个类必须:

+ 规定类型`T`,以及,
+ 提供一个名为`compareTo`的方法,接受一个对象作为参数,并返回`int`

例如,以下是`java.lang.Integer`的源代码:

```java
public final class Integer extends Number implements Comparable<Integer> {

public int compareTo(Integer anotherInteger) {
int thisVal = this.value;
int anotherVal = anotherInteger.value;
return (thisVal<anotherVal ? -1 : (thisVal==anotherVal ? 0 : 1));
}

// other methods omitted
}
```

> 译者注:根据[`Comparable<T>`的文档](http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo%28T%29),不必要这么复杂,直接返回`this.value - that.value`就足够了。
这个类扩展了`Number`,所以它继承了`Number`的方法和实例变量;它实现`Comparable<Integer>`,所以它提供了一个名为`compareTo`的方法,接受`Integer`并返回一个`int`

当一个类声明它实现一个`interface`,编译器会检查,它提供了所有`interface`定义的方法。

除此之外,这个`compareTo`的实现使用“三元运算符”,有时写作`?:`。如果你不熟悉,可以阅读 <http://thinkdast.com/ternary>

## 1.3 `List`接口

Java集合框架(JCF)定义了一个`interface`,称为 `List`,并提供了两个实现方式,`ArrayList``LinkedList`

这个`interface`定义了`List`是什么意思;实现它的任何类`interface`必须提供一组特定的方法,包括`add``get``remove`,以及其它大约 20 个。

`ArrayList``LinkedList`提供这些方法,因此可以互换使用。用于`List`也可用于`ArrayList``LinkedList`,或实现`List`的其它任何对象。

这是一个人为的示例,展示了这一点:

```java
public class ListClientExample {
private List list;

public ListClientExample() {
list = new LinkedList();
}

private List getList() {
return list;
}

public static void main(String[] args) {
ListClientExample lce = new ListClientExample();
List list = lce.getList();
System.out.println(list);
}
}
```

`ListClientExample`没有任何有用的东西,但它封装了`List`,并具有一个类的基本要素。也就是说,它包含一个`List`实例变量。我会使用这个类来表达这个要点,然后你将在第一个练习中使用它。

通过实例化(也就是创建)新的`LinkedList`,这个`ListClientExample`构造函数初始化`list`;读取器方法叫做`getList`,返回内部`List`对象的引用;并且`main`包含几行代码来测试这些方法。

这个例子的要点是,它尽可能地使用`List`,避免指定`LinkedList``ArrayList`,除非有必要。例如,实例变量被声明为`List`,并且`getList`返回`List`,但都不指定哪种类型的列表。

如果你改变主意并决定使用`ArrayList`,你只需要改变构造函数; 你不必进行任何其他更改。

这种风格被称为基于接口的编程,或者更随意,“面向接口编程”(见 <http://thinkdast.com/interbaseprog>)。这里我们谈论接口的一般思想,而不是 Java 接口。

当你使用库时,你的代码只依赖于类似“列表”的接口。它不应该依赖于一个特定的实现,像`ArrayList`。这样,如果将来的实现发生变化,使用它的代码仍然可以工作。

另一方面,如果接口改变,依赖于它的代码也必须改变。 这就是为什么库的开发人员避免更改接口,除非绝对有必要。

## 1.4 练习 1

因为这是第一个练习,我们会保持简单。你将从上一节获取代码并交换实现;也就是说,你会将`LinkedList`替换为`ArrayList`。因为面向接口编写程序,你将能够通过更改一行并添加一个`import`语句来交换实现。

以建立你的开发环境来开始。对于所有的练习,你需要能够编译和运行 Java 代码。我使用 JDK7 来开发示例。如果你使用的是更新的版本,则所有内容都应该仍然可以正常工作。如果你使用的是旧版本,可能会发现某些东西不兼容。

我建议使用交互式开发环境(IDE)来获取语法检查,自动完成和源代码重构。这些功能可帮助你避免错误或快速找到它们。但是,如果你正在准备技术面试,请记住,在面试期间你不会拥有这些工具,因此你也可以在没有他们的情况下练习编写代码。

如果你尚未下载本书的代码,请参阅 0.1 节中的指南。

在名为`code`的目录中,你应该找到这些文件和目录:

+ `build.xml`是一个 Ant 文件,可以更容易地编译和运行代码。
+ `lib`包含你需要的库(对于这个练习,只是 JUnit)。
+ `src`包含源代码。

如果你浏览`src/com/allendowney/thinkdast`,你将找到此练习的源代码:

+ `ListClientExample.java`包含上一节的代码。
+ `ListClientExampleTest.java`包含一个 JUnit 测试`ListClientExample`

查看`ListClientExample`并确保你了解它的作用。然后编译并运行它。如果你使用 Ant,你可以访问代码目录并运行`ant ListClientExample`

你可能会得到一个警告。

```
List is a raw type. References to generic type List<E>
should be parameterized.
```

为了使这个例子保持简单,我没有留意在列表中指定元素的类型。如果此警告让你烦恼,你可以通过将`List``LinkedList`替换为`List<Integer>``LinkedList<Integer>`来修复。

回顾`ListClientExampleTest`。它运行一个测试,创建一个`ListClientExample`,调用`getList`,然后检查结果是否是一个`ArrayList`。最初,这个测试会失败,因为结果是一个`LinkedList`,而不是一个`ArrayList`。运行这个测试并确认它失败。

注意:这个测试对于这个练习是有意义的,但它不是测试的一个很好的例子。良好的测试应该检查被测类是否满足接口的要求;他们不应该依赖于实现的细节。

`ListClientExample`中,将`LinkedList`替换为`ArrayList`。你可能需要添加一个`import`语句。编译并运行`ListClientExample`。然后再次运行测试。修改了这个之后,测试现在应该通过了。

为了这个此测试通过,你只需要在构造函数中更改`LinkedList`;你不必更改任何`List`出现的地方。如果你这样做会发生什么?来吧,将一个或者多个`List`替换为`ArrayList`。程序仍然可以正常工作,但现在是“过度指定”了。如果你将来改变主意,并希望再次交换接口,则必须更改代码。

`ListClientExample`构造函数中,如果将`ArrayList`替换为`List`,会发生什么?为什么不能实例化`List`
Loading

0 comments on commit 42b6ae7

Please sign in to comment.