算法
常见算法
二分法
ps:之前写的笔记没保存上,都没了,只能截了些图片
分块查找
核心思想:
块内无序,块间有序
实现步骤: .
1.创建数组blockArr存放每一个块对象的信息
2.先查找blockArr确定要查找的数据属于哪一块
3.再单独遍历这一 块数据即可
冒泡排序
核心思想:
1,相邻的元素两两比较,大的放右边,小的放左边。
2,第一轮比较完毕之后,最大值就已经确定,第二二轮可以少循环-一次,后面以此类推。
3,如果数组中有n个数据,总共我们只要执行n-1轮的代码就可以。
代码实现
//1.定义数组
int[] arr = {2, 4, 5, 3, 1};
//2.利用冒泡排序将数组中的数据变成12345
//外循环:表示我要执行多少轮。如果有n个数据, 那么执行n - 1轮
for (int i = 0; i < arr.length - 1; i++) {
//内循环:每- -轮中我如何比较数据并找到当前的最大值
//-1:为了防止索引越界
//-i:提高效率,每一轮执行的次数应该比上一轮少一次。
for (int j = 0; j < arr.length - 1 - i; j++) {
//i依次表示数组中的每一一个索引:日123 4
if (arr[j] > arr[i + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
选择排序
选择排序:
1,从0索引开始,跟后面的元素一一比较。
2,小的放前面,大的放后面。
3,第一次循环结束后,最小的数据已经确定。
4,第二次循环从1索引开始以此类推。
int[] arr = {2, 4, 5, 3, 1};
//外循环:几轮
//i:表示这一轮中,我拿着哪个索引上的数据跟后面的数据进行比较并交换
for (int i = 0; i < arr.length - 1; i++) {
//内循环:每一轮我要干什么事情?
//拿着i跟i后面的数据进行比较交换
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
插入排序
插入排序:将0索引的元素到N索引的元素看做是有序的,把N+1索引的元素到最后一个当成是无序的。遍历无序的数据,将遍历到的元素插入有序序列中适当的位置,如遇到相同数据,插在后面。
N的范围: 0~最大索引
快速排序
将排序范围中的第一个数字作为基准数,再定义两个变量start, end
start从前往后找比基准数大的,end从后往前找比基准数小的。
找到之后交换start和end指向的元素,并循环这一过程,直到start和end
处于同一个位置,该位置是基准数在数组中应存入的位置,再让基准数归位。
归位后的效果:基准数左边的,比基准数小,基准数右边的,比基准数大
代码实现:
int[]arr={6,1,2,7,9,3,4,5,10,8};
/*参数一:我们要排序的数组
参数二:要排序数组的起始索引
参数三:要排序数组的结束索引*/
quickSort(arr,0,arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static void quickSort(int[] arr, int i, int j) {
//定义两个基准数
int start = i;
int end = j;
if (start>end){
return;
}
//记录基准数
int baseNumber = arr[i];
//利用循环找到要交换的数字
while (start != end) {
//利用end,从后往前开始找,找比基准数小的数字
while (true) {
if (end <= start || arr[end] < baseNumber) {
break;
}
end--;
}
while (true) {
if (end <= start || arr[start] > baseNumber) {
break;
}
start++;
}
//把end和start指向的元素进行交换
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
//基准数归位
//就是拿着这个范围中的第一个数字, 跟start指向的元素进行交换
int temp = arr[i];
arr[i] = arr[start] ;
arr[start] = temp ;
//确定6左边的范围,重复刚刚所做的事情
quickSort(arr,i, start - 1);
//确定6右边的范围,重复刚刚所做的事情
quickSort(arr, start + 1,j);
}
Arrays
操控数组的工具类
compar方法的形参
参数一o1: 表示在无序序列中,遍历得到的每一一个元素
参数二02:有序序列中的元素
返回值:
负数:表示当前要插入的元素是小的,放在前面
正数:表示当前要插入的元素是大的,放在后面
0:表示当前要插入的元素跟现在的元素比是一样的们也会放在后面
总结:o1-o2升序 反之降序
Lambda
函数式编程( Functional programming)
是一种思想特点。
函数式编程思想,忽略面向对象的复杂语法,强调做什么,而不是谁去做。
Lambda表达式就是函数式思想的体现。
基本作用
简化函数式接口的匿名内部类的写法
使用前提
必须是接口的匿名内部类,接口中只能有一个抽象方法
好处 更简洁,更灵活
Integer[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
//lambda完整格式
Arrays.sort(arr, (Integer o1,Integer o2) -> {
return o1 - o2;
}
);
//lambda省略写法
Arrays.sort(arr, (o1, o2) -> o1 - o2);
System. out . println(Arrays. toString(arr));
练习
String[] arr = {"a", "aaa", "aaa", "aa"};
//如果以后我们要把数组中的数据按照指定的方式进行排列,就需要用到sort方法,而且要指定排序的规则
Arrays. sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//字符串的长度进行排序
return o1.length() - o2.length();
}
});
//Lambda完整格式
Arrays. sort(arr, (String o1, String o2)->{
return o1.length() - o2.length();
}
);
//Lambda简写格式
//小括号:数据类型可以省略,如果参数只有一个,小括号还可以省略
//大括号:如果方法体只有一行,return, 分号,大括号都可以省略
Arrays . sort(arr, (o1, o2)-> o1.length() - o2.length());
集合进阶
集合体系结构
Collection集合
Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
/*注意点:
Collection是一个接口,不能直接创建他的对象。
所以学习他的方法时,只能创建他实现类的对象。
实现类:
ArrayList*/
//目的:为了学习Collection接口里面的方法
//在做一些练习的时候,还是按照之前的方式去创建对象。
Collection<String> co1l = new ArrayList<>();
//判断集合中某-一个学生对象是否包含
Student s4 = new Student(" zhangsan", 23);
co1l.add(s4)
//因为contains方法在底层依赖equals方法判断对象是否一致的。
//如果存的是自定义对象,没有重写equals方法,那么默认使用object 类中的equals方法进行判断,而0bject 类中equals方法,依赖地址值进行判断。
//需求:如果同姓名和同年龄,就认为是同-一个学生。
//所以,需要在自定义的Javabean类中,要重写equals方法。
System.out.println(coll.contains(s4));
Collection的遍历方式
迭代器遍历
细节注意点:
1,如果该位置没有元素,还获取会报错NoSuchElementException
2,迭代器遍历完毕,指针不会复位
3,,循环中只能用一次next方法
4,迭代器遍历时,不能用集合的方法进行增加或者删除
如果真的要删除,可以用迭代器的remove方法,或者等遍历结束
代码实现
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll. add("aaa");
coll. add("bbb");
coll.add("ccc");
coll. add("ddd");
//2.获取迭代器对象
//迭代器就好比是-一个箭头,默认指向集合的0索引处
Iterator<String> it = coll. iterator();
//3.利用循环不断的去获取集合中的每-一个元素
while(it . hasNext()){//判断返回值是否为空
//4. next方法的两件事情:获取元素并移动指针
String str = it.next();
System. out . println(str);
}
增强for遍历
所有的单列集合和数组才能用增强for进行遍历。
格式:
for(元素的数据类型 变量名:数组或者集合){
}
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.利用增强for进行遍历
//注意点:
//s其实就是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
for (String s : coll) {
System.out.println(s);
}
//快速生成方式
//集合的名字+for回车
细节:修改增强for中的变量,不会改变集合中原本的数据
Lambda表达式遍历
//1.创建集合并添加元素
Collection<String> coll = new ArrayList<>();
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
//2.用匿名内部类
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//lambda表达式
coll.forEach(s -> System.out.println(s));
List集合
特点
有序:存和取的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
特有方法
删除方法
//List系列集合中的两个删除的方法
//1.直接删除元素
//2.通过索引进行删除
//1.创建集合并添加元素
List<Integer> list = new ArrayList<>();
list.add(1);
list . add(2);
list.add(3);
//2.删除元素.
//请问:此时删除的是1这个元素,还是1索引上的元素?
//为什么?
//因为在调用方法的时候,如果方法出现了重载现象
//优先调用,实参跟形参类型一致的那个 方法。
//list. remove(1);
//手动装箱,手动把基本数据类型的1,变成Integer类 型
Integer i = Integer . value0f(1);
list. remove(i);
System. out . println(list);
遍历方式:
1.迭代器
2.列表迭代器
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
// 5.列表迭代器
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的
//额外添加了一个方法:在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while (it.hasNext()) {
String str = it.next();
if ("bbb".equals(str)) {
//用遍历器的方法添加元素
it.add("qqq");
}
}
3.增强for
4. Lambda表达式
5.普通for循环
总结
ArrayList集合
底层逻辑
LinkedList集合
底层为双向指针 便于增删
泛型
泛型的细节
●泛型中不能写基本数据类型
●指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
●如果不写泛型,类型默认是Object
泛型类
使用场景:当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类 <E>
泛型方法:
方法中形参类型不确定时,可以使用类名后面定义的泛型<E>
泛型的通配符:
?也表示不确定的类型 相当于E
他可以进行类型的限定
? extends E:表示可以传递E或者E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
应用场景:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符
关键点:可以限定类型的范围。
泛型不具备继承性,但数据具备继承性
Set集合
●无序:存取顺序不一致
●不重复:可以去除重复
●无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素
实现类特点
●HashSet: 无序、不重复、无索引
●LinkedHashSet: 有序、不重复、无索引
●TreeSet: 可排序、不重复、无索引
HashSet
底层原理
注意:
JDK8以后, 当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
如果集合中存储的是自定义对象,必须要重写hashCode和equals方法
LinkedHashSet
这里的有序指的是保证存储和取出的元素顺序一致
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了-个双链表的机制记录存储的顺序。
数据去重:默认使用HashSet
如果要求去重且存取有序,才使用LinkedHashSet
TreeSet
可排序:按照元素的默认规则(有小到大)排序。
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
排序方式
默认排序/自然排序: Javabean类 实现Comparable接口指定比较规则
先实现接口
再重写方法
比较器排序:创建TreeSet对象时候,传递比较器Comparator指定规则
//1.创建集合
//o1:表示当前要添加的元素
//o2:表示已经在红黑树存在的元素
//返回值规则跟之前是一样的
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照长度排序
int i = o1.length() - o2.length();
//如果一样长则按照首字母排序
i = i == 0 ? o1.compareTo(o2) : i;
return i;
}
});
//2.添加元素
ts.add("c");
ts.add("ab");
ts.add("df");
ts.add("qwer");
//3.打印集合
System.out.println(ts);
注:如果这两种都重写了,按照第二种
总结
双列集合
特点
①双列集合一次需要存一对数据,分别为键和值
②键不能重复,值可以重复
③键和值是一 一对应的,每一个键只能找到自己对应的值
④键+值这个整体我们称之为“键值对”或者“键值对对象”,在Java中叫做"Entry对象”
Map
常见API
代码实现
//1.创建Map集合的对象,new实现类对象
Map<String, String> m = new HashMap<>();
//2.添加元素
//put方法的细节:
//添加/覆盖
//在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中
//在添加数据的时候,如果键是存在的,那么会把原有的键值对象覆盖,会把被覆盖的值进行返回。
m.put("郭靖", "黄蓉");
m.put("韦小宝", "沐剑屏");
m.put("尹志平", "小龙女");
String valua = m.put("韦小宝", "双儿");//沐剑屏
System.out.println(valua);
//3.打印集合
System.out.println(m);
Map的遍历方式
键找值
//1.创建Map集合的对象,new实现类对象
Map<String, String> m = new HashMap<>();
//2.添加元素
m.put("郭靖", "黄蓉");
m.put("韦小宝", "沐剑屏");
m.put("尹志平", "小龙女");
//3.通过键找值
//3.1获取所有的键,把这些键放到一个单列集合当中
Set<String> keys = m.keySet();
//3.2遍历单列集合,得到每一一个键
for (String key : keys) {
//System. out . println(key);
//3.3 利用map集合中的键获取对应的值 get
String value = m.get(key);
System.out.println(key + "=" + value);
}
//迭代器遍历
Iterator<String> i = keys.iterator();
while (i.hasNext()){
String s = i.next();
String value = m.get(s);
System.out.println(s + "=" + value);
}
键值对
//3.Map集合的第二种遍历方式
//通过键值对对象进行遍历
//3.1 通过一个方法获取所有的键值对对象,返回一个Set集合
Set<Map.Entry<String, String>> entries = map.entrySet();
//3.2遍历entries这个集合,去得到里面的每一一个键值对对象
for (Map.Entry<String, String> entry : entries) {
//3.3利用entry调用get方法获取键和值
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
Lambda表达式
//3.利用lambda表达式进行遍历
map. forEach(new BiConsumer<String, String>() {
@Override
public void accept(String key, String value) {
System. out . println(key + "=" + value);
}
});
map.forEach((key,value)-> System.out.println(key+"="+value));
HashMap
特点:
①HashMap是Map里面的一个实现类。
②没有额外需要学习的特有方法,直接使用Map里面的方法
③特点都是由键决定的:无序、不重复、无索引
④HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
总结
1.依赖hashCode方法和equals方法保证键的唯一
2.如果键存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法
LinkHashMap
●由键决定:有序、不重复、无索引。
●这里的有序指的是保证存储和取出的元素顺序一致
●原理:底层数据结构是依然哈希表,只是每个键值对元素又额外的多了- -个双链表的机制记录存储的顺序。
TreeMap
●TreeMap跟TreeSet底层原理一 样,都是红黑树结构的。
●由键决定特性:不重复、无索引、可排序
●可排序:对键进行排序。
●注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则
可变参数
本质上就是一个数组
作用:在形参中接收多个数据
格式:数据类型..参数名称
举例: int...a
注意事项:
●形参列表中可变参数只能有一个
●可变参数必须放在形 参列表的最后面
Collections
●java.util.Collections:是集合工具类
●作用: Collections不是集合,而是集合的工具类。
常用API
数据结构
栈:
后进先出,先进后出
队列:
先进先出,后进后出
数组:
查询快,增删慢
链表:
增删相对较快
树
二叉树的内部结构
父节点地址,值,左子节点,右子节点
二叉查找树
每一个节点上最多有两个子节点
任意节点左子树上的值都小于当前节点
任意节点右子树上的值都大于当前节点
前序遍历:从根结点开始,然后按照当前结点,左子结点,右子结点的顺序遍历
中序遍历:从最左边的子节点开始,然后按照左子结点,当前结点,右子结点的顺序遍历
后序遍历:从最左边的子节点开始,然后按照左子结点,右子结点,当前结点的顺序遍历
层序遍历:一层一层的遍历
平衡二叉树
任意节点子树高度不超过二
需要旋转的情况
红黑树
1.每一个节点或是红色的,或者是黑色的
2.根节点必须是黑色
3.如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
4.如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
5.对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;
拓展
不可变集合
在List、Set、 Map接口中, 都存在静态的of方法,可以获取一个不可变的集合。
细节:
●List: 直接用
●Set: 元素不能重复
●Map:元素不能重复、键值对数量最多是10个。
超过10个用ofEntries方法
//1.创建一个 普通的Map集合
HashMap<String, String> hm = new HashMap<>();
hm.put("张三", "南京");
hm.put("李四", "北京");
hm.put("王五", "上海");
//2.利用上面的数据来获取-一个不可变的集合
//获取到所有的键值对对象(Entry对象)
Set<Map.Entry<String, String>> entries = hm.entrySet();
//把entries变成一个数组
Map.Entry[] arr1 = new Map.Entry[0];
//toArray方法在底层会比较集合的长度跟数组的长度两者的大小
//如果集合的长度>数组的长度:数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//如果集合的长度<=数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
Map.Entry[] arr2 = entries.toArray(arr1);//把键值变成指定的数组
//不可变的map集合
Map map = Map.ofEntries(arr2);
Map的不可变集合
键是不能重复的
Map里面的of方法,参数是有上限的,最多只能传递20个参数,10 个键值对