文章目录
《算法》全书组织结构
- 基础:介绍实现,分析和比较算法的基本原则和方法,包括下面概述的内容
- 排序:数组排序,优先队列,选举及归并等
- 查找:庞大的数据集查找指定条目的算法
- 图:图的主要内容是对象和它们的连接(有权重和方向),解决最短路径等问题
- 字符串:研究对字符串操作的算法
- 背景:讨论其他领域前沿,如运筹学
概述
-
算法:解决问题的方法
-
数据结构:便于算法操作的组织数据的方法
-
本章介绍学习前的准备:
- Java基础编程模型(用到的Java库,语法等)
- 介绍数据抽象并定义抽象数据类型以进行模块化编程,介绍实现抽象数据类型的过程,即定义其API,通过Java的类机制来使用
- 学习三种基础的抽象数据类型:背包,队列,栈,然后用数组等实现它们的API,作为算法实现的样板和起点
- 分析算法性能方法:基本做法:先对性能提出假设,建模,实验验证,必要时重复上述过程分析算法性能方法
- 最后一个连通性问题,活学活用,最终实现union-find抽象数据结构
算法
- 编写程序:计算机语言实现一个已有方法(载体)
- 是算法,而不是编程语言描述解决问题的方法,该方法要有限,确定,有效
- 以求两非负整数的公约数的算法为例(书)
- 便于算法实现----》数据结构组织数据
- 编写复杂问题,要理解,定义和控制问题的复杂度及分解这些问题,然后选择合适算法
- 选择算法-》分析算法性能(时间空间复杂度)
基础编程模型
- 使用特定编程语言使得分离算法的思想和实现细节变得困难,所以只用大部分编程语言共有的语法
- 实现算法所用到的语言特性,软件库和操作系统特性称为基础编程模型,可作为文档来查询
- P9显示本书代码风格,力求与各种Java编程惯例和语言构造相一致
Java程序基本结构
- 原始数据类型:精确地定义整数,浮点数等(不同取值范围),组合为类似数学公式定义的表达式
- 语句:声明,赋值,条件,循环,调用和返回
- 数组:相同数据类型
- 静态方法:封装和重用代码,使coder能用独立模块编程
- 字符串:一连串字符
- 标准输入/输出
- 数据抽象:定义非原始数据类型,数据抽象和封装(自定义类)
原始数据类型和表达式
- 数据类型:一组数据及对其所能进行的操作的集合
- Java控制变量(通过标识符来引用),而变量有类型和值
- 初级运算(加减乘除)的关键是参与运算的数据类型和产生的数据类型应该一致
表达式
- Java运算符优先级:见书
- 浮点-》整型,会截断小数部分,而非四舍五入
- int型用一个字长为32位的机器字即可表示(有的电脑有字长为64位的机器字,但int型还是32位)
- Java作为强类型语言,会检查类型的一致性
- 用模板来描述语句结构(如判断语句)
代码的简便写法
- 声明与初始化一起
- 隐式赋值:i++;i +=2;
- 单语句省略花括号
静态方法
- 静态方法又称为函数
- 典型静态方法:判断素数,计算平方根等(见书)
- 方法的返回值替代表达式中的方法调用
- 返回语句结束函数并将控制权交给调用者
方法性质
- 方法参数按值传递:方法处理的是参数的值,而不是参数本身,该方式使得改变参数变量的值对调用者无影响(不要试图修改参数变量来改变参数)。值传递意味数组参数将会是原数组的别名(指向同一个内存空间):方法中使用数组参数变量能引用调用者的数组并改变其内容(不能改变原数组变量本身)
- 方法名可重载
- 方法只能返回一个值,但能包含多个返回语句
- void的静态方法会产生副作用(输入输出等)
递归(❤)
- 递归总有一个简单情况,方法的第一条语句总是包含return的条件语句
- 递归调用自己总是去尝试解决子问题,hence,递归收敛到最简单的情况
- 递归调用的父问题与子问题不该有交集(如二分法的子问题操作的数组部分不同)
静态方法库
- 定义Java类中的一组静态方法
- 模块化即面向对象编程
- Java开发模式是编写一个静态方法库(包含main方法)来完成某任务,实现模块(静态方法库)化编程
- main方法的参数是输入的字符串组成的数组
- main方法可编成测试用例,当用例复杂时,可独立成一模块
外部库
- 系统标准库(默认导入):java.lang.*
- 导入的系统库
- 第三方库
- 本地库
- 第三方及自己模块化方法编写的方法库扩展了编程模型
- 用例指的是调用其他库中方法的程序
- 实现指的是实现某API的Java代码
- Math库方法实例(见书P17)
- 本书编写的库(见P18-19)
自己编写库
- 编写用例,在实现算法中将计算过程分解为可控的部分
- 明确库与其对应的API(API将实现与调用分离)
- 实现API及能独立测试的main函数
tips
- 创建数组时指明数组大小是因为让编译器知道为数组预留的空间
- 数组的方法,包括reverse和矩阵乘法(见书)
字符串
- 一连串字符(char型值)组成
- 字符串拼接
- 字符串类型转换(同基本数据类型)
- 自动转换:基本数据类型+字符串=字符串
- 命令行参数:main方法的参数是命令行输入的字符串组成的数组
- 以后学习string类在Java中的表示方法
输入输出
- 本书提供一外部库用以建立Java程序与外界交流的简易模型
- 经典模型中,Java程序可从命令行参数或从一个抽象字符流(即标准输入流)中获得输入,并将输出写入一个名为标准输出流的字符流中
- 我们需要考虑Java程序和操作系统之间的接口-》简要地讨论OS和IDE所提供的相应机制:
- 默认情况下,命令行参数,标准输入输出与应用程序绑定,而应用程序是由能接受命令输入的操作系统或开发环境所支持 。我们笼统地用终端来指这个应用程序提供的供输入和显示的窗口
命令和参数
- 通过命令行向OS输入命令行参数(默认为字符串),OS将其传递给Java程序
- OS常用命令(见书,javac/java/more)
标准输出(❤)
- 格式化输出(printf),第一个参数为格式字符串,描述第二个参数如何在输出被转化为一个字符串(PS:该方法有多个参数)
- 格式字符串第一个字符是%,后面跟一个字符表示的转换代码(如d,f,s), 之间可插入整数表示转换后的字符串宽度),若宽度不够,则左边加空格,要想右边加空格,要使用负宽度,若转换后的字符串宽度要大于指定宽度,则宽度设置忽略
- 实例: “3.2f”:表示小数位保留2位(小数点后的2,若对于字符串,则表示截取的长度),宽度为3(更多见书P23)
- 转换代码表示的数据类型和对应参数数据类型匹配
- Sring的format方法参数同printf
标准输入(❤)
- 从标准输入流中获取数据
- by default,系统会将标准输出定位到虚拟终端
- 输入的内容即为输入流
- 输入时,多参数用空白字符分隔,空白字符包括空格,制表符,换行符等
- 我们标准输入库中静态方法的API(P24)
重定向和管道(理解基本意思)
- 标准输入和输出使得我们可利用命令行的扩展功能,只需要向命令中加入提示符(> 或<),可将输出或输入重定向为一文件
- 标准输出流默认打印至虚拟终端,可重定向为一文件
- 标准输入流默认从终端读取数据,可重定向为一文件
- 一个程序的输出重定向另一个程序的输入称为管道(% java Random 1000 10 20 | java Average)
- 上述解释:两个Java程序,第一个产生随机数,第二个求平均值,将Random的标准输出流和Average的标准输入流指定为同一个流
- 上述方法突破能处理的输入输出流的长度限制(计算机即使没有空间存储10亿个数也行),一边输出,将输出流的末尾添加字符串,一边输入,从输入流的开头删除字符串
- 见书P25图
本书提供的库
- 基于文件的输入输出库(In,Out):我们库提供了向文件写入或读取文件的数组的方法
- 标准绘图库(StdDraw):当前,输入输出抽象层的重点只是文本字符串,加入标准绘图库(标准绘图抽象层),产生图像输出的抽象层,该标准绘图库默认比例尺为段位正方形(所有坐标均在0和1之间),标准的实现将画布显示为一窗口,图形为黑色,背景为白色,各API使用见书P26-27
二分查找
- 特点:高效,应用广泛,借此展示学习算法的过程
- 程序见P28
- 关注其功能(波浪线处):输入流(这里重定向为一文本文件)获取entry,过滤掉存在于白名单文件(另一个文本文件)的entry,并打印(输出)输入流中还剩下的entry
数据抽象
即面向对象编程,模块化编程,其好处在于
- 模块化编程复用代码
- 方便构造数据结构(如链式)
- 准确定义算法问题,如对于一些算法问题,解决方式都是定义数据结构并高效地实现它们的一组操作
答疑
- Java的字节码:Java程序的一种低级表示,可以运行于JVM,将程序抽象为字节码并保证其跨平台性
- Java允许整型溢出并返回错误的值,因为Java对于原始数据类型不自动检查溢出
- 使用内置常数:Double.POSITIVE_INFINITY和Double.NEGATIVE_INFINITY可将double变量初始化为无限大
- Java自动进行类型转换
- 若使用一变量前未初始化,且代码中存在使用该未初始化的变量的路径,Java抛出编译异常
- 1/0与1.0/0.0,前者产生运行时除以0的异常(终止程序,由于该值未定义),后者是无穷大
- 只有原始数据类型定义<和>运算符
- 表达式a/b的商会向0取整
- &,|,^表示整数的位逻辑操作,&&和||仅在独立的布尔表达式中有效
- 嵌套if语句中的else与最近的if相搭配
- for循环头部代码和主体代码在同一个代码段中,递增变量循环结束后便不可用,而等价的while则不然