集合框架(1)
数据结构是了解数据存储在内存中的顺序和位置关系;算法是为求解一个问题所需要遵循的、被清楚指定的简单指令的集合。数据结构是为算法服务的,算法是要作用在特定的数据结构上的。
常见的数据结构与算法:
数据结构:数组、链表、栈和队列、散列表hash、二叉树、堆、跳表、图
算法:递归、排序(学习七种和三种扩展,冒泡和快排必须掌握)、搜索、哈希、贪心、分治、回溯、动态规划、字符串匹配
学习方法
记忆接口中的方法,记忆对应接口的实现类(区别和如何选择,选择的原因是学习的重点)
集合框架
- 如何持有一组数据最合理
- 程序 = 数据结构 (如何持有数据)+ 算法(如何解决问题)
- https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
衡量算法:时间和空间的选择
复杂度分析,快和省的问题,引入O复杂度表示法,表示代码的执行时间苏浙数据规模的变化趋势
O(1) 常量级 O(logN)(估算,并不是精确计算)对数阶 O(n^m)指数阶
递归
- 待求解问题可分解成几个子问题;
- 待求解问题与分解和的子问题只有数据规模不同,求解思路一致
- 存在递归终止条件
练习:假设有n级台阶,人可以每步跨1级或者2级台阶,上这n级台阶有多少种方法
思路:f(n) = f(n-1) + f(n-2) 终止条件 f(1) = 2 f(2) = 2
编程实现:找到将大的问题分解为小问题的规律,并且基于此规律写出递推公式,然后推导出终止条件,最后将递推公式和终止条件编译成代码
public static int up(int num) {
if (num == 1) {
return 1;
}
if (num == 2) {
return 2;
}
if (num > 3) {
return up(num - 1) + up(num - 2);
}
return 0;
}
public static void main(String[] args) {
int a = up(10);
System.out.println(a);
}
问题
- 堆栈溢出。由于函数调用会使用函数调用栈来保存当前的运行现场,但是系统栈或者虚拟机栈一般不会太大,所以如果递归求解的数据规模很大,调用层次很深,可能导致栈溢出 StackOverflowError
- 重复计算问题。可以引入备忘录保存已经求解的结果,典型的以浪费内存为代价换取高执行效率
数组
数组(本身是一种引用类型):一组相关类型的变量集合,这些变量可以按照统一下标的方式进行操作,是一种典型的线性结构
特性:
- 随机访问。支持在O(1)内按照下标进行快速访问元素
- 插入和删除数据可能会有数据移动问题。时间复杂度是O(n)
- 警惕数据访问越界问题ArrayIndexOutOfBoundsException
数组的基本使用
- 声明。int[] arr 或者 int arr[]
- 分配空间。数组空间是连续的,arr = new int[10],java不支持变长数组,如果需要变长则需要自行编程实现
- 赋值。元素都有默认值。
- 处理。可以通过下标对数组元素进行操作
排序算法1
排序算法的衡量标准:
- 时间复杂度
- 空间复杂度
- 稳定性
Java中的排序算法:冒泡、插入、选择、快速、哈希、归并、堆…
冒泡排序
核心思想:相邻比较,交换位置
空间复杂度O(1)、时间复杂度O(n^2(两级for循环嵌套))、稳定
public static void main(String[] args) {
int[] arr = { 2, 21, 49, 17, 50, 1 };
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
System.out.println(Arrays.toString(arr));
}
快速排序
核心思想:通过一次排序将数据序列分成左右两个部分,其中左半部分的值都比右半部分的值小,然后再分别对左右部分的数据进行排序,直到整个数据序列有序
空间复杂度O(1)、时间复杂度O(nlongn)、不稳定
数据结构概述
基本数据结构3种:线性表、树、图
线性表
线性表是一种线性结构,是由零个或者多个数据元素构成的有序序列。
特征:除了头尾元素,每个元素有且只有一个直接前驱和一个直接后继;而头元素,没有直接前驱,尾元素没有直接后继
常见的线性结构有数组和链表结构
数组
存储区域是连续的,占用内存比较严重,所以空间复杂度较大,但是(必须先进行排序)可以使用二分法查找元素,时间复杂度较小
特点:
- 地址方便,时间复杂度O(1)
- 插入和删除比较困难,可能会引发元素移动,时间复杂度O(n)
- java中的数组是定长的,如果需要变长则需要自行编程实现(可使用System.arrayCopy方法)
链表
数组需要一块连续的内存空间存储数据,对内存要求较高。而链表通过指针可以将一组零散的内存块串联起来使用。
数组擅长按照下标进行随机访问,链表擅长删除、插入操作
具体实现:在链表中使用节点存储数据,并使用引用串联各个元素。每个节点除了存储数据本身之外还需要额外存储下一个节点的地址
单向链表
public class Node{
private int date;//存储集体数据
private Node next;//记录下一个节点的作用,尾节点指向null
}
创建2个节点的流程
Node node1 = new Node();
node1.setDate(123);//具体存储数据,next此时为null;所以node1是一个尾节点
Node node2 = new Node();
node2.setDate(456);
node1.setNext(node2);//使node1的下一个元素指针指向到node2对象
Link的实现
public class linkList{
private Node head = null;//整个链表的头指针
public static class Node{
private int date;
private Node next;
}
}
新增数据
public void insert(Node a,Node b){//在节点a后面插入新节点b
if(a == null){//在链表的头部添加数据b
b.next = head;
head = b;
}else {
b.next = a.next;
a.next = b;
}
}
删除数据
// 删除已知前驱节点为a的节点b,b的前驱节点为a
public void remove(Node a, Node b) {
// 如果a为null则表示删除头节点b
if (a == null) {
head = head.next;
} else
a.next = b.next;
}
// 删除存储的数据值为value的节点
public void remove(int value) {
Node q = head;
Node p = null;
// 遍历整个单向链表,查询具体存放数据为value的节点
while (q != null && q.data != value) {
p = q;
q = q.next;
}
if (q != null) {
if (p == null)
head = q.next;
else
p.next = q.next;
}
}
查找节点
// 查找存储数据为 value的节点
public Node find(int value) {
Node p = head;
while (p != null && p.data != value) {
p = p.next;
}
return p;
}
获取指定下标的节点
// 获取指定下标的节点
public Node get(int index) {
Node p = head;
int res = 0;
while (p != null && res != index) {
res++;
p = p.next;
}
return p;
}
Java集合
Java集合类存放于java.util包中,是用于存放对象的容器
- 集合中只能存放对象,如果存入一个简单类型,实际上进行了自动转换【装箱】
- 集合中存放的是多个对象的引用,对象本身还是存放在堆内存
- 集合中可以存放不同类型、不限数量的数据
Collection接口
一般来说 Collection 接口是集合框架的顶级接口,但是事实上不是
特征: 集合中的数据无序,允许重复
接口中定义的方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-05FCvCCN-1646973994026)(C:\Users\阿白\AppData\Roaming\Typora\typora-user-images\image-20220215220311721.png)]
size():int 获取集合中的元素个数
- 数组中实际上是不能直接获取其中存储的元素个数,集合中可以分为两个概念:容器是表示可以存放的元素个数,类似于集合的 length;size 表示实际存储的元素个数
add(Object obj):boolean 向集合中添加元素 obj
isEmpty():boolean 判断集合中的元素个数是否为 0
注意:只判断集合中是否没有元素,并不会判断集合是否为 null
- cc.size()<1
contains(Object obj):boolean 判断当前集合中是否包含对象 obj
- 使用==还是 equals 进行判定? equals
Object 类中的 equals 方法定义
public boolean equals(Object obj){
return (this == obj);
}
如果自定义类中没有定义 equals 方法时,其中的 equals 方法是从 Object 类中继承得到的,这个方法是采用==进行判断,如果需要按照某个业务规则进行判断,则必须自定义覆盖 equals 方法
toArray():Object[]将集合转换为数组
add(Object obj):boolean 向集合中添加元素 obj
addAll(Collection) 将另外一个集合中的所有元素添加到当前集合中
remove(Object obj):boolean 从集合中删除指定的元素 obj
clear():void 清空集合中的所有元素
List子接口
Collection 接口的子接口,特征:数据有序【索引编号】允许重复。在 Collection 接口的基础上添加了每个元素的索引编号,编号从 0 开始到 size-1 结束
public interface List<E> extends Collection<E>{}
add(Object):boolean 向集合末尾追加元素
add(int index, E element):void 向指定索引位置 index 上添加元素 element,原始数据自动后移
remove(Object):boolean 删除第一个和 object 参数相等的元素,实际上是 Collection 接口中的方法
remove(int index):Object 删除指定索引值对应的元素,并返回被删除的元素。
get(int index):E 获取指定索引号对应的元素,要求 index 应该在[0,size-1]范围内,否则异常
set(int index, E element):E 用于给指定索引位置上进行赋值操作,这个位置上必须有对应的数据,已经赋过值了,这里实际上就是修改操作,否则越界异常
indexOf(Object obj):int 从前向后查询第一个元素和 obj 相等的元素的对应索引值。如果不存在相等的时候返回-1
lastIndexOf(Object obj):int 从后向前查询最后一个元素和 obj 相等的元素的对应索引值。如果不存在相等的时候返回-1
Set子接口
没有新的方法,特征:无序、不允许重复
public interface Set<E> extends Collection<E>{}
基础用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1uBtMgxH-1646973994028)(C:\Users\阿白\AppData\Roaming\Typora\typora-user-images\image-20220215221130403.png)]
个位置上必须有对应的数据,已经赋过值了,这里实际上就是修改操作,否则越界异常
indexOf(Object obj):int 从前向后查询第一个元素和 obj 相等的元素的对应索引值。如果不存在相等的时候返回-1
lastIndexOf(Object obj):int 从后向前查询最后一个元素和 obj 相等的元素的对应索引值。如果不存在相等的时候返回-1
Set子接口
没有新的方法,特征:无序、不允许重复
public interface Set<E> extends Collection<E>{}
基础用法
[外链图片转存中…(img-1uBtMgxH-1646973994028)]