1、 目录第一章 Java与 面 向 对 象 程 序 设 计 1Java语 言 基 础 知 识 1基 本 数 据 类 型 及 运 算 .1流 程 控 制 语 句 .3字 符 串 .3数 组 .5Java的 面 向 对 象 特 性 7类 与 对 象 .7继 承 .9接 口 .10异 常 .11Java与指 针12数 据 结 构 与 算 法 基 础 .15数 据 结 构 .15基 本 概 念 .15抽 象 数 据 类 型 .17小 结 .19算 法 及 性 能 分 析 .19算 法 .19时 间 复 杂 性 .20空 间 复 杂 性 .24算 法 时 间 复 杂 度 分 析 .25最 佳 、最 坏 与
2、 平 均 情 况 分 析 .27均 摊 分 析 .29线 性 表 .32线 性 表 及 抽 象 数 据 类 型 .32线 性 表 定 义 .32线 性 表 的 抽 象 数 据 类 型 .32List接口 34Strategy接口 .35线 性 表 的 顺 序 存 储 与 实 现 .36线 性 表 的 链 式 存 储 与 实 现 .42单 链 表 .42双 向 链 表 .46线 性 表 的 单 链 表 实 现 .48两 种 实 现 的 对 比 .53基 于 时 间 的 比 较 .53基 于 空 间 的 比 较 .53链 接 表 .54基 于 结 点 的 操 作 .54链 接 表 接 口 .54基
3、 于 双 向 链 表 实 现 的 链 接 表 .561.11.1.11.1.21.1.31.1.41.21.2.11.2.21.2.31.31.4第二章2.12.1.12.1.22.1.32.22.2.12.2.22.2.32.2.42.2.52.2.6第三章3.13.1.13.1.23.1.33.1.43.23.33.3.13.3.23.3.33.43.53.4.13.4.23.5.13.5.23.5.313.6第四章4.1迭 代 器 .59栈 与 队 列 .62栈 .62栈 的 定 义 及 抽 象 数 据 类 型 .62栈 的 顺 序 存 储 实 现 .63栈 的 链 式 存 储 实 现
4、.65队 列 .66队 列 的 定 义 及 抽 象 数 据 类 型 .66队 列 的 顺 序 存 储 实 现 .68队 列 的 链 式 存 储 实 现 .71堆 栈 的 应 用 .72进 制 转 换 .72括 号 匹 配 检 测 .73迷 宫 求 解 .74递 归 .78递 归 与 堆 栈 .78递 归 的 概 念 .78递 归 的 实 现 与 堆 栈 .80基 于 归 纳 的 递 归 .81递 推 关 系 求 解 .83求 解 递 推 关 系 的 常 用 方 法 .83线 性 齐 次 递 推 式 的 求 解 .85非 齐 次 递 推 关 系 的 解 .86Master Method 87分
5、治 法 .89分 治 法 的 基 本 思 想 .89矩 阵 乘 法 .91选 择 问 题 .93树 .96树 的 定 义 及 基 本 术 语 .96二 叉 树 .99二 叉 树 的 定 义 .99二 叉 树 的 性 质 .99二 叉 树 的 存 储 结 构 .101二 叉 树 基 本 操 作 的 实 现 .105树 、森 林 .112树 的 存 储 结 构 .112树 、森 林 与 二 叉 树 的 相 互 转 换 .114树 与 森 林 的 遍 历 .115由 遍 历 序 列 还 原 树 结 构 .116Huffman树 117二 叉 编 码 树 .117Huffman树 及 Huffman编
6、码 118图 .1234.1.14.1.24.1.34.24.34.2.14.2.24.2.34.3.14.3.24.3.3第五章5.15.1.15.1.25.25.35.3.15.3.25.3.35.3.45.45.4.15.4.25.4.3第六章6.16.26.2.16.2.26.2.36.36.46.4.16.4.26.4.36.4.46.56.5.16.5.2第七章24.44.5图 的 定 义 .123图 及 基 本 术 语 .123抽 象 数 据 类 型 .127图 的 存 储 方 法 .129邻 接 矩 阵 .129邻 接 表 .131双 链 式 存 储 结 构 .132图ADT
7、实现设计 138图 的 遍 历 .139深 度 优 先 搜 索 .139广 度 优 先 搜 索 .142图 的 连 通 性 .143无 向 图 的 连 通 分 量 和 生 成 树 .143有 向 图 的 强 连 通 分 量 .144最 小 生 成 树 .145最 短 距 离 .151单 源 最 短 路 径 .151任 意 顶 点 间 的 最 短 路 径 .155有 向 无 环 图 及 其 应 用 .1574.4.14.4.24.5.14.5.24.5.34.64.74.7.14.7.24.84.8.14.8.24.8.34.94.9.14.9.24.104.10.14.10.2拓 扑 排 序
8、.157关 键 路 径 .159第八章 查 找 .164查 找 的 定 义 .164基 本 概 念 .164查 找 表 接 口 定 义 .165顺 序 查 找 与 折 半 查 找 .165查 找 树 .168二 叉 查 找 树 .168AVL树 .175B-树 .183哈 希 .188哈 希 表 .189哈 希 函 数 .190冲 突 解 决 .191排 序 .194排 序 的 基 本 概 念 .194插 入 类 排 序 .195直 接 插 入 排 序 .195折 半 插 入 排 序 .196希 尔 排 序 .197交 换 类 排 序 .199起 泡 排 序 .199快 速 排 序 .200选
9、 择 类 排 序 .2028.18.1.18.1.28.28.38.3.18.3.28.3.38.48.4.18.4.28.4.3第九章9.19.29.2.19.2.29.2.39.39.49.3.19.3.239.4.19.4.29.4.3简 单 选 择 排 序 .202树 型 选 择 排 序 .203堆 排 序 .204归 并 排 序 .208基 于 比 较 的 排 序 的 对 比 .209在 线 性 时 间 内 排 序 .211计 数 排 序 .211基 数 排 序 .2129.59.69.79.7.19.7.24作者:周鹏 单位:三峡大学理学院周鹏第 一 章 Java 与 面 向 对
10、象 程 序 设 计在这一章中向读者简要介绍有关 Java 的基本知识。 Java 语言是一种广泛使用并且具有许 多 良 好 的 如 面 向 对 象 、可 移 植 性 、健 壮 性 等 特 性 的 计 算 机 高 级 程 序 设 计 语 言 ,在 这 里 对Java 的介 绍不可能面面俱到,因此在第一章中只对理解书中Java 代码的相关知识进行介绍。对 于 熟 悉 Java 的读者可以不阅读本章。1.1 Java 语 言 基 础 知 识1.1.1 基 本 数 据 类 型 及 运 算在 Java 中每个变量在使用前均必须声明它的类型。 Java 共有八种基本数据类型:四种是 整 型 ,两 种 浮
11、点 型 ,一 种 字 符 型 以 及 用 于 表 示 真 假 的 布 尔 类 型 。各 种 数 据 类 型 的 细 节 如 表1-1 所示。表 1-1 Java 数 据 类 型类型 存储空间32 bit范围-2147483648,2147483647-32768,32767intshortlongbytefloat16 bit64 bit8 bit-9223372036854775808, 9223372036854775807-128,12732 bit64 bit16 bit1 bit-3.4E38,3.4E38doublechar-1.7E308,1.7E308Unicode 字符boo
12、lean True,false在 声 明 一 个 变 量 时 ,应 先 给 出 此 变 量 的 类 型 ,随 后 写 上 变 量 名 。在 声 明 变 量 时 一 行 中 可以有声明多个变量,并且可以在声明 变量的同时对变量进 行初始化。例如:int i;double x, y = 1.2;char c = z;boolean flag;在程序设计中,常常需要在不同的数字数据类型之间进行 转换。 图1-1 给出了数字类型间的合法转换。1charintbyte short longfloat double图 1-1 数 字 类 型 间 的 合 法 转 换图 1-1 中 六 个 实 箭 头 表 示
13、 无 信 息 损 失 的 转 换 ,而 三 个 虚 箭 头 表 示 的 转 换 则 可 能 会 丢 失 精有时在程序设计中也需要进行在图 1-1 中没有出现的转换,在 Java 中这种数字转换也度。是 可 以 进 行 的 ,不 过 信 息 可 能 会 丢 失 。在 可 能 丢 失 信 息 的 情 况 下 进 行 的 转 换 是 通 过 强 制 类 型转 换 来 完 成 的 。其 语 法 是 在 需 要 进 行 强 制 类 型 转 换 的 表 达 式 前 使 用 圆 括 号 ,圆 括 号 中 是 需 要转换的目标类型。例如:double x = 7.8;int n = (int)x; /x 等
14、于 7Java 使用常 见的算术运算符*进行加、减、乘、除的运算。当除法运算符作用于两个整数时,是进行整数除法。整数的模(即求余)运算使用 % 运算符。对整型变量一种 最 常 见 的 操 作 就 是 递 增 与 递 减 运 算 ,与 C/C+ 一 样 Java也 支 持 递 增 和 递 减 运 算 符 。例 如 :int n = 7, m = 2;double d = 7;n = n / m;d /= m;/n 等 于 3/d 等 于 3.5/n 等 于 2/a 等 于 4n-;int a = 2 * n+;int b = 2 * +m; /b 等 于 6此 外 Java还 具 有 完 备 的
15、 关 系 运 算 符 ,如 =(是 否 相 等 ), (小 于 ), (大 于 ), =(小于等于),=(大于等于),!=(不等于);并且Java使用for (int k=0; k 0,运 行 时 间 最 多 为 cn 。也 就 是 说 符 号 提 供了一个运行时间的上界。定 义 2-1 令 T(n)和 f(n)是 非 负 函 数 ,如 果 存 在 一 个 非 负 整 数 N 以及一个常数 c0, 使 得 :n N,T(n) cf(n)则 T(n) = (f(n)。即如果 limn T(n)/f(n) 存在,那么22T(n)limn f(n) T(n) = (f(n)n(n 1)例 如 ,算
16、法 2-1 的时间复杂度 T(n) + 3(n 1) ,由 于 当 n 2 时 ,T(n) 2n2,2T(n) n(n 1)/ 2 + 3(n 1)n 21则有 T(n)则有 T(n)= = (n ) 。或 令2 f(n) = n2 ,因 为 limn f(n) lim = ,n 22(n ) 。 符 号 符 号 给 出 了 算 法 时 间 复 杂 度 的 上 界 ,而 符 号 在 运 行 时 间 的 常 数 因 子 范 围 内 给 出 了 时间复杂度的下界。 符 号 可 以 解 释 为 :如 果 输 入 大 于 等 于 某 个 阈 值 N,算 法 的 运 行 时 间 下 限 是 f(n)的
17、c 倍 ,其中 c 是一个正常数,则称算法的时间复杂度是(f(n)的。的形式定义与符号对称。定 义 2-2 令 T(n)和 f(n)是 非 负 函 数 ,如 果 存 在 一 个 非 负 整 数 N 以及一个常数 c0, 使 得 :n N,T(n) cf(n)则 T(n) = (f(n)。即如果 limn T(n)/f(n) 存在,那么T(n)limn f(n) 0 T(n) = (f(n)n(n 1) T(n) 1n4例如,算法 2-1 的时间复杂度 T(n) ,由于当 n 2 时,n(n 1)/ 22,则有2T(n) 1T(n) = (n22) 。或 令 f(n) = n)。2 ,因 为 l
18、im lim = 0 ,因 此 则 有 f(n) n n 2 2nT(n) = (n 符 号 符 号 给 出 了 算 法 时 间 复 杂 度 的 上 界 , 符 号 给 出 了 时 间 复 杂 度 的 下 界 ,而 给 出 了 算法时间复杂度的精确阶。 符 号 可 以 解 释 为 :如 果 输 入 大 于 等 于 某 个 阈 值 N,算 法 的 运 行 时 间 在 下 限 c f(n)和上限1c f(n)之间(00 和 c 0,使1 223得:n N,c f(n) T(n) c f(n)1 2则 T(n) = (f(n)。即如果 limn T(n)/f(n) 存在,那么T(n)limn f(n
19、) = c (c 0) T(n) = (f(n)n(n -1)2n(n 1)例如,算法 2-1 的时间复杂度 T(n) + 3(n 1) ,由于当 n 2 时,21 T(n)n T(n) 2n ,则 有 T(n) = (n ) 。或 令 f(n) = n2 2 2 2 ,因 为 limn f (n)= 1 ,则 有4 2T(n) = (n2 ) 。定义 2-3 的一个重要推论是T(n) = (n2 ) ,当且仅当 T(n) = (n2 ) 并且 T(n) = (n2 )通 过 以 上 的 分 析 我 们 可 以 看 出 :我 们 评 价 算 法 的 运 行 时 间 是 通 过 分 析 在 一
20、定 规 模 下 算 法执 行 基 本 操 作 的 次 数 来 反 映 的 ,并 且 由 于 我 们 只 对 大 规 模 问 题 的 运 行 时 间 感 兴 趣 ,所 以 是 使用算法的渐进时间复杂度 T(n)来 度 量 算 法 的 时 间 性 能 的 。 、 、 符 号 分 别 定 义 了 时 间 复杂度的上界、下界以及精确阶 。2.2.3 空 间 复 杂 性在上一小节中我们讨论了算法的时间复杂性,下面我 们来 讨论算法的空间复杂性。算 法 的 空 间 复 杂 性 同 样 是 由 算 法 运 行 时 使 用 的 空 间 来 评 价 的 。我 们 把 算 法 使 用 的 空 间 定义 为 :为
21、了 求 解 问 题 的 实 例 而 执 行 的 操 作 所 需 要 的 存 储 空 间 的 数 目 ,但 是 它 不 包 括 用 来 存 储输入实例的空间。同 样 前 面 对 于 评 价 算 法 时 间 复 杂 性 的 讨 论 都 可 以 用 于 对 算 法 的 空 间 复 杂 性 的 讨 论 。并 且在 这 里 有 这 样 一 个 观 察 结 论 :算 法 的 空 间 复 杂 性 是 以 时 间 复 杂 性 为 上 界 的 。这 是 因 为 在 算 法中 每 访 问 一 次 存 储 空 间 都 是 需 要 使 用 一 定 时 间 的 ,即 使 每 次 访 问 的 都 是 不 同 的 存 储
22、空 间 ,空间 的 大 小 也 不 会 超 过 基 本 操 作 次 数 常 数 倍 ,因 此 算 法 的 空 间 复 杂 性 是 以 时 间 复 杂 性 为 上 界的。如果使用 S(n)与 T(n)分 别 表 示 算 法 的 空 间 复 杂 度 和 时 间 复 杂 度 ,则 有 S(n) = (T(n) 。例如在算法 2-1 中 ,为 了 算 法 的 执 行 ,我 们 使 用 了 常 数 个 中 间 变 量 ,每 个 变 量 的 存 储 空间都是常数大小,所以在算法 2-1 中 :S(n) = (1) = (1) = (1)242.2.4 算 法 时 间 复 杂 度 分 析我 们 不 但 要
23、了 解 什 么 是 算 法 时 间 复 杂 度 ,还 要 学 会 分 析 算 法 的 时 间 复 杂 度 。最 简 单 的 方法 就 是 将 算 法 执 行 的 所 有 基 本 操 作 都 计 算 出 来 ,然 后 得 出 算 法 的 时 间 复 杂 度 。但 是 很 多 时 候这种方法是不可取的,因为它太麻 烦而且可能计算不出所有基本操作的 执行次数。一 般 来 说 ,不 存 在 固 定 的 方 法 ,使 用 它 就 可 以 得 到 一 个 算 法 的 时 间 复 杂 度 。但 是 在 分 析算法的时间复杂度时有一些常用技术是可以使用的。 计 算 循 环 次 数运 行 时 间 往 往 都 是
24、 集 中 在 循 环 中 ,循 环 之 外 往 往 是 一 些 简 单 的 具 有 常 数 基 本 操 作 的 运算 ,而 这 些 常 数 次 的 基 本 操 作 在 渐 进 时 间 复 杂 度 中 是 不 起 作 用 的 。这 就 使 得 运 行 时 间 往 往 和循环的次数具有相同的阶。下面给出几个使用这种方法分析算法时间复杂度的例子。算 法 2-2 function1输入:正整数 n输出:循环执行的总次数代 码 :public int function1 (int n) int i = 1, count = 0;while (i = n) i = i * 2; count+; retur
25、n count;例 2-6 分析上面的算法function1 的 时 间 复 杂 度 。在 这 里 只 有 一 层 循 环 ,假 设 循 环 k次 ,循环每进行一次会执行常数个基本操作,因此算法的时间 复杂度T(n) = (k)。因为循环只k k执行了k 次 ,i在循环执行过程中的变化趋势为 1,2,4,82 ,在 执 行 完 第 k次循环后i=2 ,所 以有2k-1 n 2k k = log n+1由此可知 T(n) = (k) = (long n+1) = (log n) 。算 法 2-3 function2输入:正整数 n输出:循环执行的总次数代 码 :public int functi
26、on2 (int n)int count=0, s=0;while (sn) count+; s = s + count; return count;25例 2-7 分析上面的算法 function2 的 时 间 复 杂 度 。这 个 算 法 与 算 法 function1 类 似 ,算法的时间复杂度与循环次数具有相同的阶。假 设 while 循环执行了 k 次,而 count 在 while循环执行过程中的变化趋势为 0,1,2k,在 执行完第 k 次循 环后 i=k。而 s 在第 i(0ik+1)次循环后的值为i(i +1)s = 0 +1+ 2 + .+ i = ,因为循环只执行了 k
27、次,所以有2(k 1)k k(k +1) 1 k = (n )2 n 2 21因此, T(n) = (k) = (n ) 。2算 法 2-4 function3输入:正整数 n输出:循环执行的总次数代 码 :public int function3 (int n) int i = 1, count = 0;while (i = n) for (int j = 0; j i; j+)count+;i = i * 2;return count;例 2-8 分析上面的算法function3 的 时 间 复 杂 度 。假 设 while循环执行了k 次 ,for循环每k k次执行i次 ,而 i在whi
28、le循环执行过程中的变化趋势为 1,2,4,82 ,在 执 行 完 第 k次循环后i=2 。所以,循环总的执行次数为k11+ 2 + .+ 2k1 = 2 = 2 1,并且由例 2-6 知i k k = log n+1i=0T(n) = (2k1) = (2log n+1 ) = (n) 。因此, 分 析 最 高 频 度 的 基 本 操 作在某些算法中,使用统计循环 次数的方法来完成算法时间 复杂度的估算是非常复杂的,有时甚至是无法完成的。请读者先阅读下面的例子。下面的算法是将两个已经有序的数组 a0,m-1, b0,n-1合 并 为 一 个 更 大 的 有 序 数 组 。合并两个数组的算法是使用两个变量 pa, pb 初始时分别指向数 组 a、b 的 第 一 个 元 素 ,每 次 比较 apa与 bpb,将小的放入新的数组 c。更新指向小元素的变量,使之加 1,指向后续元素 。这 个 过 程 一 直 进 行 下 去 ,直 到 将 某 个 数 组 中 的 所 有 元 素 放 入 新 的 数 组 为 止 。此 时 再 将 另26