1. 其他常用类
1.1. Math类
java.lang.Math提供了一系列静态方法用于科学计算;其方法的参数和返回值一般为double型。如果需要更加强大的数学运算能力,可以使用apache commons下面的Math类库
public class TestMath {
public static void main(String[] args) {
System.out.println(Math.PI); // 3.141592653589793
System.out.println(Math.round(3.14)); // 3
System.out.println(Math.ceil(3.14)); // 4.0
System.out.println(Math.floor(3.14)); // 3.0
System.out.println(Math.sqrt(64)); // 8.0
System.out.println(Math.pow(2, 4)); // 16.0
System.out.println(Math.abs(-5)); // 5
System.out.println(Math.max(30, 40)); // 40
System.out.println(Math.min(30, 40)); // 30
System.out.println(Math.random()); // 0.4160439953177707
}
}
1.2. Random类
Math类中虽然提供了产生随机数的方法Math.random(),但是通常需要的随机数范围不是[0,1]之间的double类型数据。Random类时专门用来生成随机数的,并且Math.random()底层调用的就是Random的nextDouble()方法
public class TestRandom {
public static void main(String[] args) {
Random rand = new Random();
System.out.println(rand.nextInt()); // -1323690853
System.out.println(rand.nextInt(10)); // 9
System.out.println(rand.nextDouble()); // 0.04933796879043928
System.out.println(rand.nextFloat()); // 0.51238453
for (int i = 0; i < 10; i++) {
System.out.print(rand.nextInt(10) + "\t"); // 1 8 2 6 1 7 1 3 5 2
}
}
}
- Random():创建一个新的随机数生成器
- Random(long seed):使用单个long种子创建一个新的随机数生成器
发现只要种子数和nextInt()中的参数一致的话,每次生成的随机数都是一样的(所以这是伪随机数)
1.3. 枚举
JDK1.5引入了枚举类型。枚举类型的定义包括枚举声明和枚举体。枚举体就是放置一些常量。
定义枚举要使用enum关键字。对于性别、季节等内容,定义为字符串类型,是很难限制其取值的,采用枚举可以轻松解决该问题
public enum Gender {
男, 女
}
public class Person {
String name;
Gender sex;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Gender getSex() {
return sex;
}
public void setSex(Gender sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Person person = new Person();
person.sex = Gender.女;
System.out.println(person.getSex()); // 女
}
}
所有枚举类型隐性地继承自java.lang.Enum。枚举实质上还是类!而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是public static final修饰的。可以通过枚举类型名使用
- 注意
- 当需要定义一组常量时,使用枚举类型
- 尽量不要使用枚举的高级特性
2. File类
2.1. File类的使用
File类用来代表文件和文件夹。主要作用:获取文件或文件夹的属性,实现对文件、文件夹的创建和删除,文件夹:file folder,目录:directory
public class TestFile1 {
public static void main(String[] args) {
// 1. 创建一个File对象,指向一个文件或文件夹
File file = new File("/Users/user/Desktop/JavaSE/src/com/user/leihesuanfa");
// 获取文件或文件夹的名称
System.out.println(file.getName()); // leihesuanfa
System.out.println(file.length()); // 224
System.out.println(file.exists()); // true
System.out.println(file.getPath()); // /Users/user/Desktop/JavaSE/src/com/user/leihesuanfa
System.out.println(file.getAbsoluteFile()); // /Users/user/Desktop/JavaSE/src/com/user/leihesuanfa
// 判断file是否指向一个目录
System.out.println(file.isDirectory()); // true
// 判断file是否指向一个文件
System.out.println(file.isFile()); // false
System.out.println(file.isHidden()); // false
System.out.println(file.canWrite()); // true
System.out.println(file.canRead()); // true
System.out.println(file.canExecute()); // true
// 文件夹下有哪些子文件夹和文件
File fileArr[] = file.listFiles();
for (File f : fileArr) {
System.out.print(new Date(f.lastModified()).toLocaleString());
if(f.isFile()) System.out.print(" " + f.length() + ' ');
else System.out.print(" <DIR> ");
/*
* 2024年8月4日 10:10:47 1584 TestFile1.java
* 2024年8月3日 21:55:44 639 TestMath.java
* 2024年8月3日 22:42:38 641 Person.java
* 2024年8月3日 21:59:29 538 TestRandom.java
* 2024年8月3日 22:42:19 65 Gender.java
* */
System.out.println(f.getName());
}
}
}
2.2. 使用File类新建、删除文件和文件夹
import java.io.File;
import java.io.IOException;
public class TestFile2 {
public static void main(String[] args) {
File file = new File("/Users/usr/Desktop/JavaSE/src/com/user/leihesuanfa/readme.txt");
if(file.exists()) file.delete();
else {
try {
// 判断所在文件夹是否存在
File dir = file.getAbsoluteFile();
// 不存在,创建文件夹
if(!dir.exists()) dir.mkdirs();
// 创建文件
file.createNewFile();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 注意
- File不仅可以指向一个文件,也可以指向一个文件夹(作为一个文件对待)
- File不能对文件的内容进行操作,需要借助IO流实现
3. 数据结构基础
3.1. 概述
3.1.1. 什么是数据结构
表示一组数据元素及其相互关系的,分为数据的逻辑结构和数据的存储结构
数据结构 = 逻辑结构 + 存储结构 + (在存储结构上的)运算/操作
3.1.2. 数据的逻辑结构
数据的逻辑结构指数据元素之间的逻辑关系(和实现无关)
逻辑结构分为三种结构:线性结构、树状结构、网状结构
- 线性结构:有且只有一个开始节点和一个终端节点,并且所有节点都最多只有一个直接前驱和直接后继,是一对一的线性关系的数据结构
- 线性表就是典型的线型结构,他的特征:
- 集合中存在唯一一个元素:第一个元素和最后一个元素
- 除最后元素外,其他数据元素均有唯一的直接后继
- 除第一元素外,其他数据元素均有唯一的直接前驱
- 线性表就是典型的线型结构,他的特征:
- 树型结构:除了一个数据元素(元素01)以外,每个数据元素有且仅有一个直接前驱元素,但是可以有多个直接后继元素,是一对多的联系
- 网格结构:每个数据元素可以有多个直接前驱元素,也可以有多个直接后继元素,是多对多的联系
3.1.3. 数据的存储结构
数据的存储结构:把包括数据元素本身的存储以及数据元素之间关系的表示,是数据的逻辑结构在计算机中的表示。
数据的存储结构包括:顺序存储、链式存储、索引存储、散列存储
- 顺序存储结构:把逻辑上相邻的节点,存储在物理位置上相邻的存储单元中,结点之间的逻辑关系由存储单元的临接关系体现
- 数据元素的存储对应于一块连续的存储空间,数据元素之间的前驱和后继关系通过数据元素在存储器中的相对位置来反映
- 数据元素的存储对应于一块连续的存储空间,数据元素之间的前驱和后继关系通过数据元素在存储器中的相对位置来反映
- 链式存储结构:数据元素的存储对应不连续的存储空间,每个存储节点对应一个需要存储的数据元素。每个节点由数据域和指针域组成,元素之间的逻辑关系通过存储节点之间的链接关系反映。逻辑上相邻节点物理上不必相邻。
- 索引存储结构:除建立存储节点信息外,还建立附加的索引表来标识结点的地址
- 散列存储结构:根据节点的关键字直接计算出该节点的存储地址,比如java中的HashSet、HashMap底层就是散列存储结构,添加、查询速度快
- 注意
- 同一逻辑结构可以对应多种存储结构
- 同样的运算,在不同的存储结构中,其实现过程是不同的
3.2. 线性表
3.2.1. 线性表的定义
线性表是n个类型相同数据元素的有限序列,通常记作(a0, a1,…, ai-1, ai, ai+1,…, an-1)
- 相同数据类型:从a0到an-1的n个数据元素是具有相同属性的元素
- 序列(顺序性):相同数据类型在内存中存储时,每个元素会占用相同的内存空间,便于后续的查询定位
- 在线性表的相邻数据元素之间存在着序偶关系:即ai-1是ai的直接前驱,则ai是ai-1的直接后续
- 唯一没有直接前驱的元素a0称为表头,唯一没有后续的元素an-1称为表尾;除了表头和表尾元素外,任何一个元素都有且仅有一个直接前驱和直接后继
- 有限:线性表中数据元素的个数n定义为线性表的长度,n是一个有限值,当n=0时,线性表为空。
- 在非空的线性表中,每个数据元素在线性表中都有唯一确定的序号,例如a0的序号是0,ai的序号是i
- 在一个具有n>0个数据元素的线性表中,数据元素序号的范围是[0, n-1]
- 顺序表
- 链表
3.2.2. 顺序表–顺序存储结构
- 特点:在内存中分配连续的空间,只存储数据,不存储地址信息,位置就隐含着地址
- 优点
- 节省内存空间:分配给数据的存储单元全用于存放节点的数据,节点之间的逻辑关系没有占用额外的存储空间
- 索引查找效率高:每一个节点对应一个序号,由该序号直接计算出来节点的存储地址
- 缺点
- 插入和删除操作需要移动元素,效率较低
- 必须提前分配固定数量的空间,如果存储元素少,可能导致空闲浪费
- 按照内容查询效率低
3.2.3. 链表–链式存储结构
- 特点:数据元素的存储对应的是不连续的存储空间,每个存储节点对应一个需要存储的数据元素。每个节点由数据域和指针域组成。元素之间的逻辑关系通过存储节点之间的链接关系反映。逻辑上相邻的节点物理上必不相邻
- 优点
- 插入、删除灵活(不必移动节点,只要改变节点中的指针,但是需先定位到元素上)
- 有元素才会分配节点空间,不会有闲置的节点
- 缺点
- 比顺序存储结构的存储密度小(每个节点由数据域和指针域组成,所以相同空间内全存满,顺序比链式存储更多)
- 查找节点时链式存储比顺序存储慢(每个节点地址不连续、无规律,导致按索引查询效率低)
- 在使用单链表实现线性表的时候,为了使程序更加简洁,通常在单链表的最前面添加一个哑元节点,也称为头节点,在头节点中,不存储任何实质的数据对象,其next域指向线性表中0号元素所在的节点
- 可以对空表、非空表以及首元节点统一处理,编程更方便,常用头节点
3.2.4. 其他链表
单链表只能通过一个节点的引用访问其后续节点,无法直接访问其前驱节点,要在单链表中找到某个节点的前驱节点,必须从链表的首节点出发,依次向后查找,需要O(n)的时间;
为此可以扩展单链表的节点结构,使得通过一个节点的引用,既能访问其后续节点,又能访问其前驱节点
方法:在单链表节点结构中新增加一个域,该域用于指向接待你的直接前驱节点
-
双向链表:通过上述定义的节点使用pre以及next域依次串联在一起形成的。
在双向链表中,同样需要完成数据元素的查找、插入和删除。双向链表的查找操作可以从链表的首节点开始,也可以从尾节点开始,但是需要的时间和在单链表中是一样的
Java中的LinkedList底层使用的就是双向链表 -
循环链表:首节点和尾节点链接在一起的。循环链表可以被视为无头无尾的。遍历一个循环链表时,开始于任意一个节点,沿着列表的任一方向直到返回开始的节点。
- 循环链表中,第一个节点之前的就是最后一个节点
- 循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易
- 对于新加入的节点,应该在首节点之前还是尾节点之后,可以根据实际要求灵活处理
-
单向链表的循环带头节点的非空链表
-
单向链表的循环带头节点的空链表
-
双向链表的循环带头节点的非空链表
-
双向链表的循环带头节点的空链表
4. 数据结构基础
4.1. 栈和队列
4.1.1. 栈
栈又称堆栈,是运算受限的线性表。其限制是仅允许在表的一端进行插入和删除
表中进行插入、删除操作的一端称为栈顶,栈顶保存的元素称为栈顶元素,表的一端称为栈底
当栈中没有数据元素时称为空栈
- 向一个栈插入元素称为进栈或入栈 push
- 向一个栈中删除元素称为出栈或退栈 pop
因此,堆栈又称后进先出表
4.1.2. 队列
队列也是一种运算受限的线性表,其限制是仅允许在表的一端插入,在表的另一端删除
在队列中,把插入数据元素的一端称为队尾rear,删除数据元素的一端称为对首front
- 向队尾插入元素称为进队或入队,新元素入队后成为新的队尾元素
- 从队尾中删除元素称为离队或出队,元素出队后,其后续元素成为新的队首元素
因此,队列又称为先进先出表
4.1.3. 双端队列deque
双端队列是指两端都可以进行进队和出队操作的队列,将队列的两端分别称为前端和后端,两端都可以入对和出队,其元素的逻辑结构仍是线性结构。
- 进队:前端进的元素排列在队列中后端进的元素的前面,后端进的元素排列在队列中前端进的元素的后面
- 出队:无论前端出还是后端出,先出的元素排列在后出的元素前面
- 输出受限的双端队列:一个端点允许插入和删除,另一个端点只允许插入
- 输入受限的双端队列:一个端点允许插入和删除,另一个端点只允许删除
- 双端队列既可以用来队列操作,也可以用来实现栈操作(只操作一端就是栈)
4.2. 树和二叉树
4.2.1. 树
树是由集合以及在该集合上定义的一种关系构成的。集合中的元素称为树的节点,定义的关系称为父子关系
父子关系在树的节点之间建立了一个层次结构
树的节点包含一个数据元素及若干指向其子树的若干分支
-
树是n(n>=0)个结点的有限集
- 或是一颗空树(n = 0),空树中不包含任何节点
- 或是一颗非空树(n > 0),此时有且仅有一个特定的根节点
当n>1时,其余节点可分为m(m > 0)个互不相交的有限集T1, T2, …, Tm,其中每一个本身又是一棵树,并且称为树的子树
T1 = {B, E, F} T2 = {C, G} T3 = {D, H, I, J}每个集合都是根A的子树
-
节点的度与树的度:
- 度:节点拥有的子树数目,度为0的节点称为叶子或终端节点。度不为0的节点称为非终端节点或分支节点。除根之外的分支节点也称为内部节点
- 树内各节点的度的最大值称为树的度
-
父亲、儿子、兄弟
- 父亲:一个节点的直接前驱节点
- 儿子:一个节点的直接后继节点
- 兄弟:同一个父亲节点的其他节点
-
祖先、子孙、堂兄弟
- 将父子关系进行扩展,就可以得到祖先、子孙、堂兄弟等关系
- 祖先:从根到该节点路径上的所有节点
- 子孙:以某节点为根的树中的任一节点
- 堂兄弟:父亲在同一层次的节点
4.2.2. 二叉树
二叉树:每个节点的度均不超过2的有序树
二叉树或是一颗空树,或是由一颗一个根节点和两颗互不相交的左子树和右子树组成的非空树
二叉树中每个节点的孩子树只能是0、1、2个,并且每个孩子都有左右之分
- 左/右孩子:位于左/右边的孩子
- 左/右子树:以左/右为根的子树
- 满二叉树:高度为k并且的2的k+1次幂减1个节点的二叉树
满二叉树中,每层节点都达到最大数,即每层节点都是满的 - 完全二叉树:若在一颗满二叉树中,在最下层从最右侧起去掉相邻的若干叶子节点,得到的二叉树就是完全二叉树
满二叉树必为完全二叉树,但完全二叉树不一定是满二叉树 - 二叉树的数据结构:顺序存储结构和链式存储结构(更多使用)
- 链式存储结构:设计不同的节点结构可构成不同的链式存储结构
- 在二叉树中每个节点都有两个孩子:可设计每个节点至少包括3个域:数据域(存放数据元素)、左孩子域(存放指向左孩子节点的指针)、右孩子域(存放指向右孩子节点的指针),利用此节点结构得到的二叉树存储结构称为二叉链表
- 为了方便找到父节点,可以在节点结构中增加一个指针域,指向节点的父节点,采用此节点结构得到的二叉树存储结构称为三叉链表
4.2.3. 查找树
具有下列性质的二叉树:
- 左子树不为空,且左子树上所有节点的值均小于他的根节点的值
- 右子树上所有节点的值均大于他的根节点的值
- 左、右子树叶分别为二叉排列树
注意:对二叉查找树进行中序遍历,得到有序集合 - 平衡二叉树:它是一颗空树,或他的左右两个子树的高度差(平衡因子)的绝对值不超多1,并且左右两个子树都是一颗平衡二叉树,同时平衡二叉树必定是二叉搜索树,反之不一定
- 平衡因子(平衡度):节点的平衡因子 = 节点的左子树的高度 - 右子树的高度
- 平衡二叉树:每个节点的平衡因子都为1、-1、0的二叉排序树,或者说每个节点的左右子树的高度最多差1的二叉排序树
- 平衡二叉树的目的是为了减少二叉查找树层次,提高查找速度
- 平衡二叉树的实现方法:AVL、红黑树、替罪羊树、Treap、伸展树
- 红黑树:它是一种平衡二叉树。红黑树的每个节点都有存储位表示节点的颜色,可以是红或黑,红黑树的特性:
- 每个节点是红色或黑色
- 根节点是黑色
- 每个叶子节点(NIL)是黑色(为空NIL或NULL的叶子节点)
- 如果一个节点是红色,他的子节点必须是黑色
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
- 注意:确保没有一条路径会比其他路径长出两倍,因此,红黑树是相对接近平衡的二叉树
红黑树的应用比较广泛,主要用来存储有序数据,时间复杂度是O(logN),效率非常高;它可以在O(logN)时间内做查找、插入和删除,这里的n是树中元素的数目
Java集合中的TreeSet和TreeMap,C++STL中的set、map,以及linux虚拟内存的管理,都是通过红黑树实现的
4.3. 图
图是一种网状数据结构,图是由非空的顶点集合和一个描述顶点之间关系的集合组成的
若图中顶点之间的连线是有/无方向的,这样的图称为有/无向图
- 加权图
- 权:图不但需要表示元素之间是否存在某种关系,而且图的边往往与具有一定实际意义的数有关,即每条边都有与他相关的实数,称为权
- 权值可以表示从一个顶点到另一个顶点的距离或消耗等信息
- 边上具有权值的图称为带权图
- 图的存储结构:顺序存储结构和链式存储结构(更多采用)
- 临接表:链表,链式存储结构