文章目录
1、前言
本书研究多种重要而实用的算法;和算法关系最紧密的是数据结构;本书使用java做为编程语言。
- 基础编程模型:描述和实现算法所用到的语言特性、软件库和操作系统特性总称
- 数据抽象并定义抽象数据类型:便于进行模块化编程
- 三种基本抽象数据类型:背包、队列和栈
- 性能:性能是算法研究的一个核心问题
算法第四版主要内容框架:
- 基础:包括Java编程模型、数据抽象、基本数据结构、集合类的抽象数据类型、算法性能分析的方法和一个案例分析
- 排序:有序的重新排列数组中的元素是非常重要的基础算法。我们会深入研究各种排序算法,包括插入排序、选择排序、希尔排序、快速排序、归并排序和堆排序。同时我们还会讨论另外一些算法,他们用于解决几个与排序相关的问题,例如优先队列、选举以及归并。
- 查找:从庞大的数据集中找到指定的条目也是非常重要的。我们将会讨论基本的和高级的查找算法,包括二叉查找树、平衡查找树和散列表。
- 图:图的主要内容是对象和他们的连接,连接可能有权重和方向。利用图可以为大量重要而困难的问题建模,因此图算法的设计也是本书的一个重要的研究领域。我们会研究深度优先搜索、广度优先搜索、连通性问题以及若干其他算法和应用,包括Kruskal和Prim的最小生成树算法、Dijkstra和Bellman-Ford的最短路径算法。
- 字符串:字符串是现代应用程序中的重要数据类型。我们将会研究一系列处理字符串的算法,首先是对字符串键的排序和查找的快速算法,然后是子字符串查找、正则表达式模式匹配和数据压缩算法。此外,在分析一些本身就十分重要的基础问题之后,这一章对相关领域的前沿话题也做了介绍。
- 背景:这一章将讨论与本书内容有关的若干其他前沿研究领域,包括科学计算、运筹学和计算理论。我们会介绍性地将一下基于事件的模拟、B树、后缀数组、最大流量问题以及其他高级主题。最后会讲一讲搜索问题、问题转化和NP完全性等算法研究的支柱理论,以及它们和本书的联系。
2、基础编程模型
我们学习选的方法是用Java编程语言编写的程序来实现算法。这样做的原因:
- 程序是对算法精确、优雅和完全的描述
- 可以通过运行程序来学习算法的各种性质
- 可以在应用程序中直接使用这些算法
缺点:
- 使分离算法的思想和实现细节变得困难
2.1、Java程序的基本结构
一段Java程序(类)或者是一个静态方法(函数)库,或者定义一个数据类型。要创建静态方法库和定义数据类型,会用到下面七种语法:
- 原始数据类型:他们在计算机程序中精确地定义整数、浮点数和布尔值等。他们的定义包括取值范围和能够对相应的值进行的操作,它们能够被组合为类似于数学公式定义的表达式。
- 语句:语句通过创建变量并对其赋值、控制运行流程或者引发副作用来进行计算。常用的一种语句
- 声明
- 赋值
- 条件
- 循环
- 调用
- 返回
- 数组:数组是多个同种数据类型的集合。
- 静态方法:静态方法可以封装并重用代码,使我们用独立的模块开发程序。
- 字符串:字符串是一连串的字符,Java内置了对它们的一些操作。
- 标准输入/输出:标准输入输出是程序与外界沟通的桥梁。
- 数据抽象:数据抽象封装和重用代码,使我们可以定义非原始的数据类型,进而支持面向对象编程。
示例:二分查找
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;
import java.util.Arrays;
// 导入一个Java库
public class BinarySearch {
/**
* 二分查找法:静态方法
* @param key 参数变量,参数类型int
* @param a 目标数组
* @return key在数组a中的位置;否则返回-1
*/
public static int rank(int key, int[] a) {
// 初始化声明语句
int lo = 0;
int hi = a.length - 1;
// 循环
while (lo <= hi) {
int mid = (hi - lo) / 2 + lo;
if (key < a[mid]) hi = mid - 1;
else if (key > a[mid]) lo = mid + 1;
else return mid;
}
// 返回语句
return -1;
}
public static void main(String[] args) {
// 初始化数组
int[] whiteList = {23423, 123, 23 , 324234, 5234, 123, 2424, 2322, 9942, 2323, 2323};
Arrays.sort(whiteList);
// 输入
int i = StdIn.readInt();
// 输出
StdOut.println(rank(i, whiteList));
}
}
2.2、原始数据类型和表达式
数据类型就是一组数据和对其所能进行的操作的集合。以为为4种Java语言最基本的原始数据类型:
- 整形及其算术运算符(int)
- 双精度实数类型及其算术运算符(double)
- 布尔型,它的值{true,false}及其逻辑操作(boolean)
- 字符型,它的值是你能够输入的英文字母数字字符和符号(char)
Java程序控制的是用标识符命名的变量。每个变量都有自己的类型并存储了一个合法的值。在Java代码中我们用类似数学表达式的表达式来实现对各种类型的操作。对于原始数据类型来说,我们用标识符来引用变量,用+、-、*、/ 等运算符来指定操作,用字面量,例如1或者3.14来表示值,用形如(x+2.55)/2的表达式来表示对值的操作。
Java程序的基本组成:
术语 | 例子 | 定义 |
---|---|---|
原始数据类型 | int double boolean char | 一组数据和对所能进行的操作的集合 |
标识符 | a Ab_b ab123 | 由字母、数字、下划线和$组成的字符串,首字符不能是数字 |
变量 | [任意标识符] | 表示某种数据类型的值 |
运算符 | + - * / % | 表示某种数据类型的运算 |
字面量 | int 1 double: 2.0 boolean: true char: ‘a’ | 值在源代码中的表示 |
表达式 | 字面量、变量或是能够计算出结果的一串字面量、变量和运算符的组合 |
只要能够指定值域和在此值域上的操作,就能定义一个数据类型。
类型 | 值域 | 运算符 | 典型表达式 | 值 |
---|---|---|---|---|
int | − 2 31 -2^{31} −231至 + 2 31 − 1 +2^{31}-1 +231−1的整数(32位,二进制补码) | + - * / % | 5+3 | 8 |
double | 双精度实数(64位,IEEE754标准) | + - * / | 3.14+0.03 | 3.17 |
boolean | true 或者 false | && || ! ^ | true && false | false |
char | 字符(16位) | (算术运算符,但很少使用) |
2.2.1、表达式
如上表所示,Java使用的是中缀表达式:一个字面量(或是一个表达式),紧接着一个运算符,再接着另外一个字面量(或者表达式)。Java中运算符有优先级和方向。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jkO0S3EB-1638710540104)(I:\study\java\note\algorithm\algorithms4\fundametal\operator-priority.png)]
2.2.2、类型转换
类型转换包括隐式类型换行和强制类型转换。
-
隐式类型转换:一般就是数值类型的转换,整数->浮点型。如果表达式中既有浮点型,又有整数,运算时,会把整形自动转换为浮点型进行运算。
-
强制类型转换:格式-(数据类型)数值。例如(int)3.7 值是3.
2.2.3、其他原始数据类型
- 64位整数及其算术运算符(long)
- 16位整数及其算法运算符(short)
- 16位字符及其算术运算符(char)
- 8为整数及其算法运算符(byte)
- 32位单精度实数及其算术运算符(float)
2.3、语句
Java程序是由语句组成的。语句能够通过创建和操作变量、对变量赋值并控制这些操作的执行流程来描述运算。语句通常被组织成代码段,即花括号中的一系列语句。
- 声明语句:创建某种类型的变量并用标识符为其命名。
- 赋值语句:将某种类型的数值赋予一个变量。
- 条件语句:能够简单的改变执行流程-根据指定的条件执行符合条件的代码段。
- 循环语句:更彻底地改变执行流程-只要条件为真就不断反复的执行代码段中的语句。
- 调用和返回语句:和方法相关。
2.4、数组
数组能够顺序存储相同类型的多个数据。除了存储数据,我们也希望能够访问数据。访问数组中的某个元素是方式是将其编号然后索引。
2.4.1、创建并初始化数组
创建数组步骤:
- 声明数组的类型和名称
- 创建数组
- 初始化数组元素
示例:
// 完整模式
double[a];
a = new double[N];
for(int i = 0; i < N; i++)
a[i] = 1.0;
// 简化写法
double[] a = new double[N];
// 声明初始化
int[] b = {1, 5, 32, 22};
2.4.2、二维数组
Java中二维数组就是元素都是一维数组的一维数组。二维数组可以是参差不齐(元素数组的长度可以不一致),但大多数情况下我们都会使用MxN,级M行N列。二维数组的创建:
double[][] a = new double[M][N]
示例:
double[][] a = new double[M][N];
for(int i = 0; i < M; i++)
for(int j = 0; j < N; j++)
a[i][j] = 1.0;
2.5、方法
对方法的所有性质的完整性描述,有兴趣的自行查阅相关文档,以下展示几点:
- 方法的参数按值传递
- 方法名可以被重载
- 方法只能返回一个值,但可以包含多个返回语句。
- 方法可以产生副作用
2.5.1、递归
我们会经常使用递归,因为递归代码比相应的非递归代码更加简洁优雅、易懂。,但是需要注意一下三点:
- 递归总有一个最简单的情况-结束条件,方法的第一条语句总是一个包含return的条件语句
- 递归调用总是去尝试解决一个规模更小的子问题,这样递归才能收敛到最简单的情况
- 递归调用的父问题和尝试解决的子问题之间不应该有交集
2.6、API
模块化编程的一个重要组成部分就是记录库方法的用法并供其他人参考的文档。我们会统一使用应用程序编程接口(API)的方式列出本书中使用的每个库方法名称、签名和简短的描述。库包括Java库,本书实现的标准库以及自定义编写的库。
2.7、输入输出
2.7.1、标准会图库
标准绘图库中还包含一些方法来改变画布的大小和比例、直线的颜色和宽度、文本字体、绘图时间(用于动画)等。StdDraw中常用静态方法的API如下:
API | 描述 |
---|---|
void setXscale(double x0, double x1) | 将x的范围设为(x0, x1) |
void setYscale(double y0, double y1) | 将y的范围设为(y0, y1) |
void setPenRadius(double r) | 将画笔的粗细半径设为r |
void setPenColor(Color c) | 将画笔的颜色设为c |
void setFont(Font f) | 将文本字体设为f |
void setCanvasSize(int w, int h) | 将画布窗口的宽和高设为w和h |
void clear(Color c) | 清空画布并用颜色c将其填充 |
void show(int dt) | 显示所有图像并暂停dt毫秒 |
示例1:已排序的随机数组
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdRandom;
import java.util.Arrays;
public class TestStdDraw01 {
public static void main(String[] args) {
int N = 50;
double[] a = new double[N];
for (int i = 0; i < N; i++) {
a[i] = StdRandom.uniform();
}
Arrays.sort(a);
for (int i = 0; i < N; i++) {
double x = 1.0 * i / N;
double y = a[i] / 2.0;
double rw = 0.5 / N;
double rh = a[i] / 2.0;
StdDraw.filledRectangle(x, y, rw, rh);
}
}
}
结果图示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B36j1ZWe-1638710540105)(I:\study\java\note\algorithm\algorithms4\fundametal\2021-12-05_ordered-random-array.png)]
源代码仓库:https://gitee.com/gaogzhen/algorithm