提交 c1f02b6c 编写于 作者: W wizardforcel

2022-01-08 18:29:44

上级 c4a39e8d
......@@ -118,7 +118,7 @@ Package.appxmanifest
```
用户可以通过在屏幕上右键单击鼠标,或者在使用触摸屏设备时滑动指针,并选择**下一步****上一步**来循环切换颜色和更改应用程序的背景。这是一个特定于应用程序的阵列,其他应用程序不太可能使用它。在下面,我们看到了类的默认构造函数。
用户可以通过在屏幕上右键单击鼠标,或者在使用触摸屏设备时滑动指针,并选择**下一步****上一步**来循环切换颜色和更改应用程序的背景。这是一个特定于应用程序的数组,其他应用程序不太可能使用它。在下面,我们看到了类的默认构造函数。
```cpp
SimpleTextRenderer::SimpleTextRenderer():m_renderNeeded(true), m_backgroundColorIndex(0),m_textPosition(0.0f, 0.0f) { }
......@@ -230,7 +230,7 @@ Package.appxmanifest
场景的实际绘制就是在渲染方法中进行的。场景的大部分绘制都是由 m_d2dContext 对象完成的。渲染方法从陈述`m_d2dcontext->BeginDraw`开始;这条线耦合到底部附近的`m_d2dContext->EndDraw`方法调用。您应该将所有的 Direct2D 绘图放在这两个函数调用之间。BeginDraw 用于指定一些代码的开始,这些代码为渲染目标构建一批渲染命令。EndDraw 指定该批命令已经完成,可以进行渲染。
下一行调用`Clear`方法,传递用户当前选择的颜色。这导致屏幕被清除为纯色,这是先前定义的`BackgroundColors`阵列,用户可以在其中循环。
下一行调用`Clear`方法,传递用户当前选择的颜色。这导致屏幕被清除为纯色,这是先前定义的`BackgroundColors`数组,用户可以在其中循环。
| ![](img/tip.png) | 提示:在渲染方法中将屏幕清除为黑色以外的颜色是一个好主意,即使您的后续绘图将完全覆盖清除的屏幕。如果您不这样做,并且程序有问题,您可能会盯着黑屏(或随机闪烁的颜色或像素),根本无法知道渲染方法是否正在被调用。 |
......
......@@ -147,7 +147,7 @@
### 顶点和索引缓冲区
在示例代码中,我们描述了三个彩色顶点。在定义了顶点数组之后,有一个对 d3dDevice 的`CreateBuffer`方法的调用。这种方法采用我们的阵列,并将数据复制到设备(图形处理器)内存中。因此,顶点缓冲区是一种设备资源。
在示例代码中,我们描述了三个彩色顶点。在定义了顶点数组之后,有一个对 d3dDevice 的`CreateBuffer`方法的调用。这种方法采用我们的数组,并将数据复制到设备(图形处理器)内存中。因此,顶点缓冲区是一种设备资源。
```cpp
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
......@@ -372,7 +372,7 @@ DirectX 根据索引缓冲区中点的顺时针或逆时针顺序确定任何特
`Render`方法中,有一个对`IASetPrimitiveTopology`的调用,它要求 GPU 将点渲染为三角形。渲染高度图的另一个好方法是使用线条。您可以将此方法的参数从`D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST`更改为`D3D11_PRIMITIVE_TOPOLOGY_LINELIST`,您的高度图将呈现为彩色线条的集合。
| ![](img/tip.png) | 提示:在前面的例子中,我使用局部数组将索引和顶点存储在系统内存中;即使是中等大小的高度图,这也会很快导致堆栈溢出。函数的本地参数存储在堆栈上,这样,当函数返回时,为它们分配的内存将自动清除。如果需要更大的高度图,应该考虑将堆与“new”运算符一起使用。 |
| ![](img/tip.png) | 提示:在前面的例子中,我使用局部数组将索引和顶点存储在系统内存中;即使是中等大小的高度图,这也会很快导致栈溢出。函数的本地参数存储在栈上,这样,当函数返回时,为它们分配的内存将自动清除。如果需要更大的高度图,应该考虑将堆与“new”运算符一起使用。 |
## 第十九章:投射选项
......
......@@ -120,7 +120,7 @@
## 渲染目标、交换链和后台缓冲区
图形处理器将像素数据写入内存中的阵列,然后发送到显示器进行显示。图形处理器写入像素的内存缓冲区称为渲染目标。通常有两个或多个缓冲区;一个正在屏幕上显示,而 GPU 将下一帧写入另一个看不见的帧。用户可以看到的缓冲区称为前缓冲区。图形处理器写入的渲染目标称为后台缓冲区。当图形处理器完成向后台缓冲区渲染帧时,缓冲区会交换。后缓冲区成为前缓冲区并显示在屏幕上,前缓冲区成为后缓冲区。图形处理器将下一帧渲染到新的后缓冲区,该缓冲区以前是前缓冲区。将数据反复写入后台缓冲区和交换缓冲区可以实现流畅的图形。这些缓冲器都是 RGB 像素数据的二维阵列
图形处理器将像素数据写入内存中的数组,然后发送到显示器进行显示。图形处理器写入像素的内存缓冲区称为渲染目标。通常有两个或多个缓冲区;一个正在屏幕上显示,而 GPU 将下一帧写入另一个看不见的帧。用户可以看到的缓冲区称为前缓冲区。图形处理器写入的渲染目标称为后台缓冲区。当图形处理器完成向后台缓冲区渲染帧时,缓冲区会交换。后缓冲区成为前缓冲区并显示在屏幕上,前缓冲区成为后缓冲区。图形处理器将下一帧渲染到新的后缓冲区,该缓冲区以前是前缓冲区。将数据反复写入后台缓冲区和交换缓冲区可以实现流畅的图形。这些缓冲器都是 RGB 像素数据的二维数组
缓冲区被称为交换链的对象依次渲染和交换多次。它被称为交换链,因为不需要只有两个缓冲区;可能有一个由许多缓冲区组成的链,每个缓冲区依次呈现并翻转到屏幕上。
......
......@@ -75,7 +75,7 @@
```
这个类相当简单;它由一个名为`m_keystates``bool`阵列和几个用于按下和释放按键的吸气器和设置器组成。
这个类相当简单;它由一个名为`m_keystates``bool`数组和几个用于按下和释放按键的吸气器和设置器组成。
要将对 **Keyboard.h** 文件的引用添加到 **SimpleTextRenderer.h** 文件,请参见下面的代码表。
......
......@@ -59,7 +59,7 @@
```
这个程序创建了一个名为`ServicesToRun`的数组。该阵列基于`ServiceBase`。NET 类,并存储一个`monitorservice`自定义类的实例。`Monitorservice`也是来源于`ServiceBase`。NET 类,并将管理正在开发的 Windows 服务。
这个程序创建了一个名为`ServicesToRun`的数组。该数组基于`ServiceBase`。NET 类,并存储一个`monitorservice`自定义类的实例。`Monitorservice`也是来源于`ServiceBase`。NET 类,并将管理正在开发的 Windows 服务。
一旦创建了数组,程序调用`ServiceBase``Run`方法,将数组作为参数传递,服务执行开始。
......
......@@ -31,7 +31,7 @@
| /InstallStateDir=[directoryName] | 指定的目录。包含用于卸载程序集的数据的 InstallState 文件。默认值是包含程序集的目录。 |
| /log file =[文件名] | 指定记录安装进度的日志文件的名称。默认情况下,如果省略**/日志文件**选项,将会出现一个名为*的日志文件*。InstallLog 已创建。如果省略*文件名*,则不会生成日志文件。 |
| /logtocconsole = { true | false } | 如果为真,则向控制台显示输出。如果为 false(默认值),禁止向控制台输出。 |
| /ShowCallStack | 如果安装过程中的任何时候发生异常,将调用栈输出到日志文件。 |
| /ShowCallStack | 如果安装过程中的任何时候发生异常,将调用栈输出到日志文件。 |
| /u [ninstall] | 卸载指定的程序集。与其他选项不同,/u 适用于所有程序集,无论该选项出现在命令行的什么位置。 |
评论
......
......@@ -270,7 +270,7 @@ Azure AD 可以完全独立于您的内部活动目录;你可以将任何你
* **单点登录,读取目录数据**选项:您将能够登录,您的应用程序将获得的令牌将对查询目录有效。
* **单点登录,读写目录数据**选项:你可以完全控制你的活动目录。
读取和写入操作都是通过图形应用编程接口完成的,这是一个基于 REST 的编程接口,旨在让任何具有 HTTP 栈的平台上的应用程序获得对目录的委托访问。
读取和写入操作都是通过图形应用编程接口完成的,这是一个基于 REST 的编程接口,旨在让任何具有 HTTP 栈的平台上的应用程序获得对目录的委托访问。
关于第一个“组合框”,它让我们选择我们要瞄准的广告类型和我们要瞄准的组织数量,这个决定相当容易。
......@@ -380,7 +380,7 @@ Azure AD 可以完全独立于您的内部活动目录;你可以将任何你
### 构建安全的网络应用编程接口端点
如今,以 RESTful 方式公开数据变得越来越方便,因为它允许每个支持或拥有 HTTP 栈的设备与我们的 web 端点进行对话并使用我们的数据。但是,向客户公开提供公司敏感信息的 web 端点,而不设计某种安全级别来阻止未经授权的客户,可能会导致一些严重的安全问题。在本书的这一部分中,我们将构建一个基本的 ASP.NET 网络应用编程接口项目,该项目使用我们在云上的组织目录作为安全层,以便只允许经过身份验证的客户端使用我们的服务。
如今,以 RESTful 方式公开数据变得越来越方便,因为它允许每个支持或拥有 HTTP 栈的设备与我们的 web 端点进行对话并使用我们的数据。但是,向客户公开提供公司敏感信息的 web 端点,而不设计某种安全级别来阻止未经授权的客户,可能会导致一些严重的安全问题。在本书的这一部分中,我们将构建一个基本的 ASP.NET 网络应用编程接口项目,该项目使用我们在云上的组织目录作为安全层,以便只允许经过身份验证的客户端使用我们的服务。
随着 Visual Studio 2013 的发布,您可以将这一切抛在脑后。这个版本从微软开放网络接口引入了创新的 ASP.NET 工具和安全中间件。NET (OWIN)组件,使保护您的网络应用编程接口变得简单。新的 ASP.NET 工具和模板允许您配置一个网络应用编程接口项目,将身份验证直接外包给 Windows Azure 活动目录(AD),在本地项目和 Windows Azure AD 中的相应条目中发出必要的代码。
......
# 第 1 章算法和数据结构
# 一、算法和数据结构
## 我们为什么在乎?
......
# 第二章链表
# 二、链表
## 概述
......@@ -12,7 +12,7 @@
图 1:存储在数组中的整数数据
如图所示,阵列数据存储为逻辑分段的单个连续分配的内存块。存储在数组中的数据被放在其中一个段中,并通过其在数组中的位置或索引进行引用。
如图所示,数组数据存储为逻辑分段的单个连续分配的内存块。存储在数组中的数据被放在其中一个段中,并通过其在数组中的位置或索引进行引用。
这是存储数据的好方法。大多数编程语言使得分配数组和操作数组内容变得非常容易。连续数据存储提供了性能优势(即数据局部性),对数据进行迭代很简单,并且可以在恒定时间内通过索引(随机访问)直接访问数据。
......@@ -50,7 +50,7 @@
```
这个解决方案有几个问题,但最明显的是当读取超过 20 个值时。就像现在的程序一样,从 21 到 *n* 的值被忽略了。这可以通过分配超过 20 个值来缓解,可能是 200 或 2000 个。也许大小可以由用户配置,或者如果阵列已满,可以分配一个更大的阵列,并将所有现有数据复制到其中。最终,这些解决方案会造成复杂性和内存浪费。
这个解决方案有几个问题,但最明显的是当读取超过 20 个值时。就像现在的程序一样,从 21 到 *n* 的值被忽略了。这可以通过分配超过 20 个值来缓解,可能是 200 或 2000 个。也许大小可以由用户配置,或者如果数组已满,可以分配一个更大的数组,并将所有现有数据复制到其中。最终,这些解决方案会造成复杂性和内存浪费。
我们需要的是一个集合,它允许我们添加任意数量的整数值,然后按照它们被添加的顺序枚举这些整数。该集合不应具有固定的最大大小,也不需要随机访问索引。我们需要的是一个链表。
......@@ -83,7 +83,7 @@
```
请注意,阵列解决方案的所有问题都已不复存在。不再存在阵列不够大或分配的容量超出需要的问题。
请注意,数组解决方案的所有问题都已不复存在。不再存在数组不够大或分配的容量超出需要的问题。
您还应该注意到,该解决方案通知了我们稍后将做出的一些设计决策,即`LinkedList`类接受泛型类型参数并实现`IEnumerable`接口。
......
# 第三章阵榜
# 三、数组
## 概述
......@@ -14,7 +14,7 @@
* 一排`T (_items)`。该数组将保存集合中的项。
* 将数组初始化为大小 0 的默认构造函数。
* 接受整数长度的构造函数。该长度将成为阵列的默认容量。记住数组的容量和集合`Count`不是一回事。当使用非默认构造函数时,可能会有一些场景允许用户向`ArrayList`类提供大小调整提示,以最小化需要重新分配内部数组的次数。
* 接受整数长度的构造函数。该长度将成为数组的默认容量。记住数组的容量和集合`Count`不是一回事。当使用非默认构造函数时,可能会有一些场景允许用户向`ArrayList`类提供大小调整提示,以最小化需要重新分配内部数组的次数。
```cs
public class ArrayList<T> : System.Collections.Generic.IList<T>
......@@ -123,20 +123,20 @@
2. 将元素从较小的数组复制到较大的数组。
3. 将内部数组更新为更大的数组。
在这一点上,我们唯一需要回答的问题是新阵列应该有多大?这个问题的答案由`ArrayList`增长政策来定义。
在这一点上,我们唯一需要回答的问题是新数组应该有多大?这个问题的答案由`ArrayList`增长政策来定义。
我们将研究两种增长策略,对于每种策略,我们将研究阵列增长的速度以及它对性能的影响。
我们将研究两种增长策略,对于每种策略,我们将研究数组增长的速度以及它对性能的影响。
#### 加倍(单声道和转子)
我们可以在线查看`ArrayList`类的两个实现: [Mono](https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Collections/ArrayList.cs)[Rotor](http://www.123aspx.com/rotor/RotorSrc.aspx?rot=39823) 。它们都使用一种简单的算法,每次需要分配时,该算法会将数组的大小增加一倍。如果阵列大小为 0,默认容量为 16。算法是:
我们可以在线查看`ArrayList`类的两个实现: [Mono](https://github.com/mono/mono/blob/master/mcs/class/corlib/System.Collections/ArrayList.cs)[Rotor](http://www.123aspx.com/rotor/RotorSrc.aspx?rot=39823) 。它们都使用一种简单的算法,每次需要分配时,该算法会将数组的大小增加一倍。如果数组大小为 0,默认容量为 16。算法是:
```cs
size = size == 0 ? 1 : size * 2;
```
该算法的分配和数组副本较少,但平均比 Java 方法浪费更多的空间。换句话说,它偏向于有更多的 *O* (1)个插入,这应该会减少集合执行耗时的分配和复制操作的次数。这是以更大的平均内存占用以及平均更多的空阵列插槽为代价的。
该算法的分配和数组副本较少,但平均比 Java 方法浪费更多的空间。换句话说,它偏向于有更多的 *O* (1)个插入,这应该会减少集合执行耗时的分配和复制操作的次数。这是以更大的平均内存占用以及平均更多的空数组插槽为代价的。
#### 增长放缓(Java)
......@@ -178,13 +178,13 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
| 行为 | 在集合中的指定索引处添加提供的值。如果指定的索引等于或大于`Count`,将引发异常 |
| 表演 | *O* ( *n* ) |
在特定索引处插入需要将插入点后的所有项目向右移动一个。如果后备阵列已满,则需要先增加它,然后才能进行转移。
在特定索引处插入需要将插入点后的所有项目向右移动一个。如果后备数组已满,则需要先增加它,然后才能进行转移。
在下面的示例中,有一个容量为五项的数组,其中四项正在使用中。值“3”将作为数组中的第三项插入(索引 2)。
![](img/image007.png)
图 7:插入前的阵列(末端有一个开放的插槽)
图 7:插入前的数组(末端有一个开放的插槽)
![](img/image008.png)
......@@ -254,7 +254,7 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
![](img/image007.png)
图 12:阵列向左移动,释放了最后一个插槽
图 12:数组向左移动,释放了最后一个插槽
```cs
public void RemoveAt(int index)
......@@ -414,7 +414,7 @@ Java 使用了类似的方法,但是数组的增长稍微慢一点。它用来
| 行为 | 从指定的数组索引开始,将内部数组的内容从头到尾复制到提供的数组中。 |
| 表演 | *O* ( *n* ) |
请注意,该方法不是简单地遵从`_items`数组的`CopyTo`方法。这是因为我们只想复制从索引`0``Count`的范围,而不是整个阵列容量。使用`Array.Copy`允许我们指定要复制的项目数量。
请注意,该方法不是简单地遵从`_items`数组的`CopyTo`方法。这是因为我们只想复制从索引`0``Count`的范围,而不是整个数组容量。使用`Array.Copy`允许我们指定要复制的项目数量。
```cs
public void CopyTo(T[] array, int arrayIndex)
......
# 第四章堆栈和队列
# 四、栈和队列
## 概述
......@@ -6,9 +6,9 @@
## 堆叠
栈是以后进先出模式向调用者返回对象的集合。这意味着添加到集合中的最后一个对象将是返回的第一个对象。
栈是以后进先出模式向调用者返回对象的集合。这意味着添加到集合中的最后一个对象将是返回的第一个对象。
堆栈不同于列表和类似数组的集合。它们不能被直接索引,对象使用不同的方法添加和移除,并且它们的内容比列表和数组更不透明。我这样说的意思是,虽然基于列表的集合提供了`Contains`方法,但是堆栈没有。此外,堆栈不可枚举。为了理解这是为什么,让我们来看看什么是堆栈,以及堆栈的使用如何驱动这些差异。
栈不同于列表和类似数组的集合。它们不能被直接索引,对象使用不同的方法添加和移除,并且它们的内容比列表和数组更不透明。我这样说的意思是,虽然基于列表的集合提供了`Contains`方法,但是栈没有。此外,栈不可枚举。为了理解这是为什么,让我们来看看什么是栈,以及栈的使用如何驱动这些差异。
一叠最常见的类比之一是餐馆的盘子叠。这是一个简单的弹簧加载装置,清洁板叠放在上面。弹簧确保无论堆叠中有多少板,都可以轻松接近顶板。干净的盘子被添加到堆叠的顶部,当顾客移除盘子时,他或她正在移除最上面的盘子(最近添加的盘子)。
......@@ -24,15 +24,15 @@
图 14:将红色、蓝色和绿色板添加到板架中
这里要理解的关键点是,随着新板块的加入,它们会被添加到栈的顶部。如果客户取回一个盘子,他或她将获得最近添加的盘子(图 14 中的绿色盘子)。下一个顾客会拿到蓝色的盘子,最后红色的盘子会被拿走。
这里要理解的关键点是,随着新板块的加入,它们会被添加到栈的顶部。如果客户取回一个盘子,他或她将获得最近添加的盘子(图 14 中的绿色盘子)。下一个顾客会拿到蓝色的盘子,最后红色的盘子会被拿走。
现在我们了解了栈是如何工作的,让我们定义一些新的术语。
现在我们了解了栈是如何工作的,让我们定义一些新的术语。
当一个项目被添加到堆栈中时,使用`Push`方法将其“推”上。当从堆栈中移除一个项目时,使用`Pop`方法将其“弹出”。堆栈中最上面的项目,最近添加的,可以使用`Peek`方法“偷看”。窥视允许您查看物品,而无需将其从堆叠中取出(就像货架上的顾客能够看到顶板的颜色一样)。记住这些术语,让我们来看看`Stack`类的实现。
当一个项目被添加到栈中时,使用`Push`方法将其“推”上。当从栈中移除一个项目时,使用`Pop`方法将其“弹出”。栈中最上面的项目,最近添加的,可以使用`Peek`方法“偷看”。窥视允许您查看物品,而无需将其从堆叠中取出(就像货架上的顾客能够看到顶板的颜色一样)。记住这些术语,让我们来看看`Stack`类的实现。
### 类别定义
`Stack`类定义了`Push``Pop``Peek`方法,一个`Count`属性,并使用`LinkedList<T>`类存储栈中包含的值。
`Stack`类定义了`Push``Pop``Peek`方法,一个`Count`属性,并使用`LinkedList<T>`类存储栈中包含的值。
```cs
public class Stack<T>
......@@ -64,7 +64,7 @@
### 推
| 行为 | 将项目添加到栈顶部。 |
| 行为 | 将项目添加到栈顶部。 |
| 表演 | *O* (1) |
因为我们使用链表作为后备存储,所以我们需要做的就是将新项目添加到链表的末尾。
......@@ -79,7 +79,7 @@
### 流行
| 行为 | 移除并返回添加到堆栈中的最后一项。如果堆栈为空,则抛出`InvalidOperationException`。 |
| 行为 | 移除并返回添加到栈中的最后一项。如果栈为空,则抛出`InvalidOperationException`。 |
| 表演 | *O* (1) |
`Push`将物品添加到列表的后面,所以我们将从后面“弹出”它们。如果列表为空,则会引发异常。
......@@ -103,7 +103,7 @@
### Peek
| 行为 | 返回添加到堆栈中的最后一项,但将该项留在堆栈中。如果堆栈为空,则抛出`InvalidOperationException`。 |
| 行为 | 返回添加到栈中的最后一项,但将该项留在栈中。如果栈为空,则抛出`InvalidOperationException`。 |
| 表演 | *O* (1) |
```cs
......@@ -121,10 +121,10 @@
### 计数
| 行为 | 返回栈中的项数。 |
| 行为 | 返回栈中的项数。 |
| 表演 | *O* (1) |
既然栈应该是一个不透明的数据结构,为什么我们有一个`Count`属性?知道堆栈是否为空(Count == 0)非常有用,尤其是因为`Pop`在堆栈为空时抛出异常。
既然栈应该是一个不透明的数据结构,为什么我们有一个`Count`属性?知道栈是否为空(Count == 0)非常有用,尤其是因为`Pop`栈为空时抛出异常。
```cs
public int Count
......@@ -139,7 +139,7 @@
### 示例:RPN 计算器
经典的栈示例是反向波兰符号(RPN)计算器。
经典的栈示例是反向波兰符号(RPN)计算器。
RPN 语法相当简单。它使用
......@@ -151,7 +151,7 @@ RPN 语法相当简单。它使用
换句话说,我们应该说“4 2 +”,而不是说“4 + 2”如果你想了解 RPN 语法的历史意义,我鼓励你去维基百科或者你最喜欢的搜索引擎。
评估 RPN 的方式,以及堆栈在实现 RPN 计算器时如此有用的原因,可以在以下算法中看到(堆栈操作是粗体的):
评估 RPN 的方式,以及栈在实现 RPN 计算器时如此有用的原因,可以在以下算法中看到(栈操作是粗体的):
```cs
for each input value
......@@ -174,7 +174,7 @@ RPN 语法相当简单。它使用
```
现在栈包含单个值:`6`(答案)。
现在栈包含单个值:`6`(答案)。
下面是一个简单计算器的完整实现,该计算器从控制台输入中读取一个等式(例如,“4 2 +”),在每个空格(例如[“4”、“2”和“+”)拆分输入,并对输入执行 RPN 算法。循环一直持续到输入单词“退出”。
......@@ -241,7 +241,7 @@ RPN 语法相当简单。它使用
## 队列
队列非常类似于栈,它们提供了一个不透明的集合,对象可以通过在基于列表的集合上增加值的方式添加(入队)或移除(出列)。
队列非常类似于栈,它们提供了一个不透明的集合,对象可以通过在基于列表的集合上增加值的方式添加(入队)或移除(出列)。
队列是先进先出(FIFO)集合。这意味着项目将按照添加的顺序从队列中删除。你可以把排队想象成商店收银台前的一排队伍——人们按照到达的顺序排队接受服务。
......@@ -529,11 +529,11 @@ RPN 语法相当简单。它使用
```
### 示例:实现
### 示例:实现栈
deq 经常被用来实现其他数据结构。
我们已经看到了一个使用`LinkedList`实现的堆栈,所以现在让我们看看一个使用`Deque`实现的堆栈。
我们已经看到了一个使用`LinkedList`实现的栈,所以现在让我们看看一个使用`Deque`实现的栈。
你可能想知道为什么我会选择使用`Deque`而不是`LinkedList`来实现`Stack`。原因之一是性能和代码可重用性。链表的代价是每个节点的开销和降低的数据局部性——项目在堆中分配,内存位置可能彼此不靠近,从而在中央处理器和内存硬件级别导致大量缓存未命中和页面错误。一个性能更好的队列实现可能使用数组而不是列表作为后备存储。这将减少每个节点的开销,并通过解决一些局部性问题来提高性能。
......@@ -574,7 +574,7 @@ deq 经常被用来实现其他数据结构。
请注意,所有的错误检查现在被推迟到`Deque`并且对`Deque`所做的任何优化或错误修复将自动应用到`Stack`类。实现一个`Queue`同样简单,因此留给读者做练习。
### 阵列后备存储器
### 数组后备存储器
如前所述,使用数组而不是链表作为`Deque<int>`(一组整数)的后备存储有很多好处。从概念上看,这似乎很简单,但实际上有几个问题需要解决才能成功。
......@@ -623,7 +623,7 @@ deq 经常被用来实现其他数据结构。
此时,数组被填充。添加另一个项目时,将出现以下情况:
1. 增长策略将定义新阵列的大小。
1. 增长策略将定义新数组的大小。
2. 项目将从头到尾复制到新数组中。
3. 将添加新项目。
1. `EnqueueFirst`–项目在索引 0 处添加(复制操作使其保持打开状态)。
......
# 第五章二叉查找
# 五、二叉搜索
到目前为止,我们已经看到了以线性方式组织数据的数据结构。链表包含从单个起始节点到单个终止节点的数据。数组将数据保存在连续的一维块中。
在本章中,我们将看到增加一个维度将如何允许我们引入一个新的数据结构:树。具体来说,我们将看到一种被称为二叉查找树的树。二分搜索法树采用一般的树结构,并应用一组简单的规则来定义树的结构。
在本章中,我们将看到增加一个维度将如何允许我们引入一个新的数据结构:树。具体来说,我们将看到一种被称为二叉搜索树的树。二分搜索法树采用一般的树结构,并应用一组简单的规则来定义树的结构。
在我们了解这些规则之前,让我们先了解一下什么是树。
......@@ -18,9 +18,9 @@
上图所示的树是一个普通的树。它代表父/子关系,但是结构没有规则。首席执行官有一个直接下属,但也可能没有或有二十个。在图中,**销售**显示在**营销**的左边,但是那个点单没有意义。事实上,唯一可观察到的约束是每个节点最多有一个父节点(而最顶层的节点**董事会**没有父节点)。
## 二叉查找树概况
## 二叉搜索树概况
二叉查找树使用与上图所示的普通树相同的基本结构,但增加了一些规则。这些规则是:
二叉搜索树使用与上图所示的普通树相同的基本结构,但增加了一些规则。这些规则是:
1. 每个节点可以有 0、1 或 2 个子节点。
2. 任何小于节点值的值都将传递给左子节点(或左子节点的子节点)。
......@@ -30,7 +30,7 @@
![](img/image022.png)
图 23:二叉查找
图 23:二叉搜索
请注意我们指定的约束是如何在图中实施的。根节点(8)左边的每个值都小于 8,右边的每个值都大于或等于根节点。这条规则递归地应用于沿途的每个节点。
......@@ -91,7 +91,7 @@
```
## 二叉查找树班
## 二叉搜索树班
`BinaryTree`类提供了操纵树所需的基本方法:`Add``Remove`、一个`Contains`方法来确定树中是否存在一个项目、几种遍历和枚举方法(这些方法允许我们以各种明确定义的顺序枚举树中的节点)以及正常的`Count``Clear`方法。
......
# 第六章设定
# 六、集合
`Set`是实现基本代数集算法的集合类型,包括并、交、差和对称差。这些算法中的每一个将在它们各自的章节中详细解释。
......@@ -30,7 +30,7 @@
`Set`类实现了`IEnumerable`接口,并接受了一个应该是`IComparable`类型的泛型参数(测试等式对于集合算法的运行是必要的)。
集合的成员将包含在. NET `List`类中,但实际上,集合通常包含在树结构中,例如[二叉查找](5.html#_Binary_Search_Tree)。底层容器的这种选择会影响各种算法的复杂性。例如,使用`List``Contains`的复杂度为 *O* ( *n* ),而使用一棵树平均会产生 *O* (log *n* )。
集合的成员将包含在. NET `List`类中,但实际上,集合通常包含在树结构中,例如[二叉搜索](5.html#_Binary_Search_Tree)。底层容器的这种选择会影响各种算法的复杂性。例如,使用`List``Contains`的复杂度为 *O* ( *n* ),而使用一棵树平均会产生 *O* (log *n* )。
除了我们将要实现的方法之外,`Set`还包括一个默认构造函数和一个接受要填充集合的项的`IEnumerable`的构造函数。
......
# 第七章排序算法
# 七、排序算法
在本章中,我们将了解用于对数组中的数据进行排序的五种算法。我们将以天真的算法[冒泡排序](#_Bubble_Sort_1)开始,以最常见的通用排序算法[快速排序](#_Quick_Sort_1)结束。
......@@ -36,7 +36,7 @@
图 36:未排序的整数数组
在第一次通过数组时,比较值 3 和 7。由于 7 大于 3,因此不执行交换。接下来,比较 7 和 4。7 大于 4,因此值被交换,从而将 7 移近数组的末尾一步。该阵列现在如下所示:
在第一次通过数组时,比较值 3 和 7。由于 7 大于 3,因此不执行交换。接下来,比较 7 和 4。7 大于 4,因此值被交换,从而将 7 移近数组的末尾一步。该数组现在如下所示:
![](img/image037.png)
......@@ -112,7 +112,7 @@
接下来检查索引 2 (4)处的值。因为 4 小于 7,所以已知 4 需要移动到排序数组区域中的适当位置。现在的问题是该值应该插入到排序数组的哪个索引中。这样做的方法是图 43 下面的代码示例中所示的`FindInsertionIndex`。此方法将待插入的值(4)与排序范围内的值进行比较,从索引 0 开始,直到找到应插入该值的点。
此方法确定索引 1(在 3 和 7 之间)是合适的插入点。然后,插入算法(图 43 后面的代码示例中的`Insert`方法)通过从数组中移除要插入的值并将所有值从插入点向右移动移除的项来执行插入。该阵列现在如下所示:
此方法确定索引 1(在 3 和 7 之间)是合适的插入点。然后,插入算法(图 43 后面的代码示例中的`Insert`方法)通过从数组中移除要插入的值并将所有值从插入点向右移动移除的项来执行插入。该数组现在如下所示:
![](img/image037.png)
......@@ -289,13 +289,13 @@
图 47:未排序的整数数组
现在我们把阵列切成两半:
现在我们把数组切成两半:
![](img/image048.png)
图 48:未排序的数组被切成两半
现在,这两个阵列都被反复切成两半,直到每个项目都独立出来:
现在,这两个数组都被反复切成两半,直到每个项目都独立出来:
![](img/image049.png)
......@@ -411,7 +411,7 @@
图 54:将值移动到分区值的左侧和右侧
此时,我们知道 6 在数组中的正确位置。我们知道这一点是因为左边的每个值都小于分区值,右边的所有值都大于或等于分区值。现在,我们在阵列的两个未排序的分区上重复这个过程。
此时,我们知道 6 在数组中的正确位置。我们知道这一点是因为左边的每个值都小于分区值,右边的所有值都大于或等于分区值。现在,我们在数组的两个未排序的分区上重复这个过程。
通过递归调用每个数组分区的`quicksort`方法,在示例代码中完成重复。请注意,这一次左数组在索引 1 处被分区,值为 5。将值移动到适当位置的过程会将值 5 移动到另一个索引。我指出这一点是为了强调您选择的是分区值,而不是分区索引。
......
......@@ -48,24 +48,24 @@
回想一下,要在哈希表中存储一个项目,我们需要有一个键和值。我们的目标是价值,所以现在我们需要选择一把钥匙。理想情况下,我们会选择能够唯一代表存储对象的东西;但是,在这种情况下,我们将使用员工姓名(`Robert Horvick`)来演示密钥可以是任何数据类型。在实践中,`Employee`类将包含一个唯一的标识,用于区分多个同名员工,该标识将是我们使用的密钥。
后备阵列
后备数组
为了快速访问项目,哈希表由数组( *O* (1)随机访问)而不是列表( *O* ( *n* )随机访问)支持。在任何给定时刻,数组都有两个有趣的属性:
1. 容量
2. 填充因数
容量是数组可能容纳的项目数。例如,以下空阵列的容量为 10:
容量是数组可能容纳的项目数。例如,以下空数组的容量为 10:
![](img/image012.png)
图 12:容量为 10 的阵列
图 12:容量为 10 的数组
填充因子是已填充(使用中)的数组项目的百分比。例如,以下数组的填充因子为 0.40 (40%):
![](img/image013.png)
图 13:容量为 10、填充系数为 0.40 (40%)的阵列
图 13:容量为 10、填充系数为 0.40 (40%)的数组
请注意,数组是以明显随机的方式填充的。虽然数组包含四个项,但这些项并不存储在索引 0–3 中,而是存储在索引 1、2、4 和 6 中。这是因为存储项目的索引是由散列函数确定的,在我们的示例中,散列函数采用关键组件“`Robert Horvick`”,并返回一个整数散列码。然后使用模运算将这个散列码调整到数组的大小。例如:
......@@ -924,7 +924,7 @@ DJB2 唯一值:99.88282%
| 行为 | 向哈希表中添加键-值对,如果表中已经存在该键,将引发异常。 |
| 表演 | *O* (1)平均。 *O* ( *n* + 1)出现阵增长时。 |
这个方法在`HashTableArray`类上提供了一个抽象层次来处理当添加操作超过最大容量时后备数组的增长。当扩展后备阵列时,它使用填充因子来确定给定当前后备阵列容量的最大项目数。
这个方法在`HashTableArray`类上提供了一个抽象层次来处理当添加操作超过最大容量时后备数组的增长。当扩展后备数组时,它使用填充因子来确定给定当前后备数组容量的最大项目数。
```cs
/// <summary>
......
......@@ -168,7 +168,7 @@
图 24:向有效堆中添加 10
我们的后备阵列现在看起来如下:
我们的后备数组现在看起来如下:
```cs
[8, 6, 5, 3, 4, 10]
......@@ -181,7 +181,7 @@
图 25:交换 10 和 5 个节点
我们的后备阵列现在如下所示:
我们的后备数组现在如下所示:
```cs
[8, 6, 10, 3, 4, 5]
......@@ -194,7 +194,7 @@
图 26:交换 10 和 8 个节点
我们的后备阵列现在看起来如下:
我们的后备数组现在看起来如下:
```cs
[10, 6, 8, 3, 4, 5]
......@@ -246,13 +246,13 @@
`RemoveMax`的工作原理与`Add`相似,但方向相反。`Add`从树的底部(或数组的末尾)开始,将值向上移动到适当的位置,`RemoveMax`从存储堆中最大的值开始,该值在数组索引 0 中。这是将返回给调用者的值。
由于该值正在被移除,数组索引 0 现在是空闲的。为了确保我们的阵列没有任何间隙,我们需要向其中移动一些东西。我们要做的是抓取数组中的最后一项,并将其向前移动到索引 0。在树形视图中,它看起来像这样:
由于该值正在被移除,数组索引 0 现在是空闲的。为了确保我们的数组没有任何间隙,我们需要向其中移动一些东西。我们要做的是抓取数组中的最后一项,并将其向前移动到索引 0。在树形视图中,它看起来像这样:
![](img/image028.jpg)
图 27:将最后一个数组项移动到索引 0
我们的后备阵列变化如下:
我们的后备数组变化如下:
```cs
[10, 6, 8, 3, 4, 5] => [5, 6, 8, 3, 4]
......@@ -273,7 +273,7 @@
图 29:与最大的子代交换创建一个有效的堆
我们的后备阵列变化如下:
我们的后备数组变化如下:
```cs
[5, 6, 8, 3, 4] => [8, 6, 5, 3, 4]
......@@ -425,7 +425,7 @@
如果我的电脑正在为 VoIP 电话连接发送数据包,并且正在传输我去脸书度假的照片,那么我很可能希望语音连接优先于图片传输。图片传输时间长一点我可能不会注意到,但是你可以肯定我会注意到我的语音通话质量差。
在本例中,网络堆栈可能提供一种机制,允许网络流量为自己声明一些优先级属性,以确定数据的时间敏感性。网络堆栈可能会在内部使用优先级队列来实现这一点。这显然是一个复杂话题的琐碎化,但重点应该是明确的:有些数据集比其他数据集更重要。
在本例中,网络栈可能提供一种机制,允许网络流量为自己声明一些优先级属性,以确定数据的时间敏感性。网络栈可能会在内部使用优先级队列来实现这一点。这显然是一个复杂话题的琐碎化,但重点应该是明确的:有些数据集比其他数据集更重要。
### 优先级队列类别
......
......@@ -725,7 +725,7 @@ AVL 树`Add`操作与二叉查找树加法操作几乎相同。它使用本章
| 行为 | 返回一个枚举数,该枚举数执行 AVL 树的有序遍历。 |
| 表演 | *O* (1)返回枚举器。 *O* ( *n* )为调用者枚举每个节点。 |
本系列的第一本书更详细地描述了 inorder(最小到最大值)遍历算法。下面的实现使用一个栈来演示在没有递归的情况下执行遍历。
本系列的第一本书更详细地描述了 inorder(最小到最大值)遍历算法。下面的实现使用一个栈来演示在没有递归的情况下执行遍历。
```cs
/// <summary>
......
......@@ -92,7 +92,7 @@ ECMAScript 6 (ES6),也称为 ECMAScript 2015,为开发人员带来了长期
* 巴别塔——这是迄今为止 ES6 最受欢迎的 transpiler。它附带了一个 2015 年预设,您可以使用它来支持 2015 年规范的大部分内容。你可以在这里找到更多关于 Babel.js 的信息: [https://babeljs.io](https://babeljs.io) 。有一点需要注意的是,巴贝尔 6 是最近发布的,然而 REPL 在线还没有更新。
* traceur–这是 ES6 的另一款流行 transpiler。它不像巴贝尔那样完全符合 ES6 的规范,但它仍然是一个很好的选择。你可以在这里找到更多关于特蕾西的信息:https://github.com/google/traceur-compiler。
* TypeScript–这可能是微软栈开发人员最受欢迎的 transpiler。TypeScript 严格来说并不是 ES6,因为它在混合中添加了类型和接口。就 ES6 合规性而言,它落后于 Babel 和 Traceur。然而,它是一个很棒的工具,如果你喜欢得到强类型的编译时异常,这就是适合你的工具。你可以在这里找到更多关于打字稿的信息:http://www.typescriptlang.org/。
* TypeScript–这可能是微软栈开发人员最受欢迎的 transpiler。TypeScript 严格来说并不是 ES6,因为它在混合中添加了类型和接口。就 ES6 合规性而言,它落后于 Babel 和 Traceur。然而,它是一个很棒的工具,如果你喜欢得到强类型的编译时异常,这就是适合你的工具。你可以在这里找到更多关于打字稿的信息:http://www.typescriptlang.org/。
桌面浏览器支持进展得相当不错。你会惊讶地发现,微软 Edge 在标准合规性方面走在了火狐的后面。Chrome 紧随其后,但仍有一些路要走。
......
......@@ -830,9 +830,9 @@ Array.values()
| Uint8Array | one | 8 位无符号整数 | 八位字节 | uint8_t |
| 我是游泳健将 | one | 8 位无符号整数(箝位) | <八位字节 | uint8_t |
| Int16Array | Two | 16 位二进制补码有符号整数 | 短的 | int16_t |
| Uint16 阵列 | Two | 16 位无符号整数 | 无符号短 | uint16_t |
| Uint16 数组 | Two | 16 位无符号整数 | 无符号短 | uint16_t |
| Int32Array | four | 32 位二进制补码有符号整数 | 长的 | int32_t |
| Uint32 阵列 | four | 32 位无符号整数 | 无符号长 | uint32_t |
| Uint32 数组 | four | 32 位无符号整数 | 无符号长 | uint32_t |
| 浮动 32 数组 | four | 32 位 IEEE 浮点数 | 无限制浮动 | 漂浮物 |
| Float64Array | eight | 64 位 IEEE 浮点数 | 无限制双重 | 两倍 |
......@@ -912,7 +912,7 @@ Array.values()
```
我们可以像创建常规数组一样创建和访问具有更具体名称的类型化数组视图类,而不是创建通用的`DataView`。这些阵列列于表 1 中。
我们可以像创建常规数组一样创建和访问具有更具体名称的类型化数组视图类,而不是创建通用的`DataView`。这些数组列于表 1 中。
考虑以下示例:
......
ES6 提供尾调用优化(TCO),在这里您可以进行一些函数调用,而不会增加栈。这在 JavaScript 的递归和函数式编程中非常有用。
ES6 提供尾调用优化(TCO),在这里您可以进行一些函数调用,而不会增加栈。这在 JavaScript 的递归和函数式编程中非常有用。
让我们看一个例子:
......@@ -24,8 +24,8 @@ ES6 提供尾调用优化(TCO),在这里您可以进行一些函数调用,
需要注意的是,您必须处于严格模式下,此优化才能工作。
还要注意,阶乘函数中发生的最后一件事是,它使用对自身的调用来返回值。这是函数式编程的关键规则之一,大多数函数式编程语言在处理递归时不会导致栈溢出。
还要注意,阶乘函数中发生的最后一件事是,它使用对自身的调用来返回值。这是函数式编程的关键规则之一,大多数函数式编程语言在处理递归时不会导致栈溢出。
总而言之,如果在 return 语句之前发生的最后一件事是调用一个不需要访问任何当前局部变量的函数,那么 ES6 指定的解释器将通过重用栈帧来优化该调用。
总而言之,如果在 return 语句之前发生的最后一件事是调用一个不需要访问任何当前局部变量的函数,那么 ES6 指定的解释器将通过重用栈帧来优化该调用。
| ![](img/00003.gif) | 注意:在我们的浏览器有 ES6 解释器之前,这不太可能得到优化。 |
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册