1.旋转(理解)
概念
对于一颗平衡的二叉树,如果后续再继续向平衡的二叉树中插入数据导致平衡二叉树不在平衡了,那么我们需要通过旋转的方式把不平衡的二叉树变为平衡的二叉树。
分类
左旋 右旋 左左 右右 左右 右左
左左:只需要做一次右旋就变成了平衡二叉树。我们这里讲解左左,其余自学。
右右:只需要做一次左旋就变成了平衡二叉树。
左右:先做一次分支的左旋,再做一次树的右旋,才能变成平衡二叉树。
右左:先做一次分支的右旋,再做一次数的左旋,才能变成平衡二叉树。
左左:就是向当前平衡二叉树的左子树的左子树插入了新的数据,导致不平衡了。
如何将左左变为平衡二叉树呢?
只需要一次右旋就可以了。
2.红黑树(理解)
红黑树的特性:
1. 每一个节点或是红色的,或者是黑色的。
2. 根节点必须是黑色
3. 每个叶节点(Nil)是黑色的;(如果一个节点没有子节点,则该节点相应的指针属性值为Nil,这些 Nil视为叶节点)
4. 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
5. 对每一个节点,从该节点到其所有后代叶节点的路径上,均包含相同数目的黑色节点
说明:如果一个树之前是一个红黑树数据结构,插入新的数据之后,不再是红黑树。会通过变颜色和左旋 右旋变为红黑树。
红黑树好处:提高查找效率。
小结:
1.红黑树的根节点必须是黑色
2.每个节点不能出现连续的红色
3.从某个节点开始到下面的叶子节点的路径的黑色节点的个数一定是相同的
3.List接口介绍(掌握)
1.List接口集合位于java.util 包下的,需要导包
2.他的父接口是Collection
3.特点:
- 存取有序
- 可以重复
- 可以使用索引操作
4.List集合接口中特有的常用方法:
public void add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上。public E get(int index)
:返回集合中指定位置的元素。public E remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
package com.itheima.sh.list_01;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/*
1.public void add(int index, E element)`: 将指定的元素,添加到该集合中的指定位置上。
2.public E get(int index)`:返回集合中指定位置的元素。
3.public E remove(int index)`: 移除列表中指定位置的元素, 返回的是被移除的元素。
4.public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
5. boolean addAll(int index, Collection<? extends E> c)
参数:
index :向哪个位置添加的索引
c:新添加的集合
*/
public class ListDemo01 {
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//指定位置添加
// list.add(0,"ddd");
// System.out.println(list);
// String s = list.get(1);
// System.out.println(s);//bbb
//4.public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
/* String name = list.set(2, "锁哥");
System.out.println("list = " + list);//[aaa, bbb, 锁哥]
System.out.println("name = " + name);//ccc*/
/*
5. boolean addAll(int index, Collection<? extends E> c)
参数:
index :向哪个位置添加的索引
c:新添加的集合
*/
//创建集合
List<String> list2 = new ArrayList<>();
list2.add("柳岩");
//需求:将list集合中所有的数据添加到柳岩的前面
list2.addAll(0, list);
System.out.println("list = " + list);//list = [aaa, bbb, ccc]
System.out.println("list2 = " + list2);//list2 = [aaa, bbb, ccc, 柳岩]
}
}
小结:
1.public void add(int index, E element)`: 将指定的元素,添加到该集合中的指定位置上。
2.public E get(int index)`:返回集合中指定位置的元素。
3.public E remove(int index)`: 移除列表中指定位置的元素, 返回的是被移除的元素。
4.public E set(int index, E element)`:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
5. boolean addAll(int index, Collection<? extends E> c)
参数:
index :向哪个位置添加的索引
c:新添加的集合
4.ArrayList集合(掌握)
底层是数组:查询快,增删慢。
开发中查询的时候使用ArrayList
5.LinkedList集合(掌握)
介绍
1.位于java.util 包下的,需要导包
2.LinkedList集合底层是双链表数据结构
说明:
1.双向链表相对比单向链表查询快,但是增删相对慢了。
2.双向链表查询没有数组快,增删也比数组快。
方法
-
public void addFirst(E e)
:将指定元素插入此列表的开头。 -
public void addLast(E e)
:将指定元素添加到此列表的结尾。 -
public E getFirst()
:返回此列表的第一个元素。 -
public E getLast()
:返回此列表的最后一个元素。 -
public E removeFirst()
:移除并返回此列表的第一个元素。 -
public E removeLast()
:移除并返回此列表的最后一个元素。 -
public E pop()
:从此列表所表示的堆栈处弹出一个元素。 -
public void push(E e)
:将元素推入此列表所表示的堆栈。 -
public boolean isEmpty()
:如果列表不包含元素,则返回true。代码演示:
package com.itheima.sh.linkedlist_02; import java.util.LinkedList; /* 1.public void addFirst(E e)`:将指定元素插入此列表的开头。 2.public void addLast(E e)`:将指定元素添加到此列表的结尾。等同于add,就是换个名字 3.public E getFirst()`:返回此列表的第一个元素。 4.public E getLast()`:返回此列表的最后一个元素。 5.public E removeFirst()`:移除并返回此列表的第一个元素。 6.public E removeLast()`:移除并返回此列表的最后一个元素。 7.public E pop()`:从此列表所表示的堆栈处弹出一个元素。集合中的第一个元素 8.public void push(E e)`:将元素推入此列表所表示的堆栈。 9.public boolean isEmpty()`:如果列表不包含元素,则返回true。 */ public class LinkedListDemo01 { public static void main(String[] args) { //创建LinkedList集合对象 LinkedList<String> list = new LinkedList<>(); /* public boolean add(E e) { linkLast(e); return true; } */ //向集合添加数据 list.add("aaa"); list.add("bbb"); list.add("ccc"); //使用特有方法:2.public void addLast(E e)`:将指定元素添加到此列表的结尾。 /* public void addLast(E e) { linkLast(e); } */ /* list.addLast("ddd"); // 3.public E getFirst()`:返回此列表的第一个元素。 String first = list.getFirst(); System.out.println(first);//aaa*/ //5.public E removeFirst()`:移除并返回此列表的第一个元素。 // String s = list.removeFirst(); // System.out.println("s = " + s);//s = aaa // System.out.println(list);//[bbb, ccc] // 7.public E pop()`:从此列表所表示的堆栈处弹出一个元素。集合中的第一个元素 /* public E pop() { return removeFirst(); } */ /* String pop = list.pop(); System.out.println("pop = " + pop);//pop = aaa System.out.println(list);//[bbb, ccc]*/ //8.public void push(E e)`:将元素推入此列表所表示的堆栈。先进后出或者后进先出 list.push("ddd"); System.out.println(list); } }
小结:
public void addFirst(E e)
:将指定元素插入此列表的开头。public void addLast(E e)
:将指定元素添加到此列表的结尾。public E getFirst()
:返回此列表的第一个元素。public E getLast()
:返回此列表的最后一个元素。public E removeFirst()
:移除并返回此列表的第一个元素。public E removeLast()
:移除并返回此列表的最后一个元素。public E pop()
:从此列表所表示的堆栈处弹出一个元素。public void push(E e)
:将元素推入此列表所表示的堆栈。public boolean isEmpty()
:如果列表不包含元素,则返回true。
6.LinkedList源码分析(掌握)
代码演示:
package com.itheima.sh.linkedlist_02;
import java.util.LinkedList;
/*
LinkedList源码剖析
*/
public class LinkedListDemo02 {
public static void main(String[] args) {
//创建集合对象
LinkedList<String> list = new LinkedList<>();
//添加数据
list.add("柳岩");
list.add("杨幂");
list.add("冰冰");
list.add("刘诗诗");
System.out.println(list);
}
}
小结:
1.每次添加数据,底层都会创建Node节点类的对象,存储数据和上一个节点地址值
2.扩展:get(index)方法,使用比较少,以后使用迭代器迭代集合。
链表没有索引,数组是有索引。
ArrayList底层操作的是数组的索引:
transient Object[] elementData;
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
LinkedList集合:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
小结:LinkedList通过指针来根据索引获取数据
7.Collections类(掌握)
1.属于java.util包下的,使用时需要导包
2.表示集合工具类,主要用来操作List集合
3.构造方法私有化,都是静态方法,使用类名调用即可
4.方法:
public static void shuffle(List<?> list)
:打乱集合顺序。public static <T> void sort(List<T> list)
:将集合中元素按照默认规则排序(从小到大)。public static <T> void sort(List<T> list,Comparator<? super T> c)
:将集合中元素按照指定规则排序。
代码演示1:
package com.itheima.sh.collections_03;
import java.util.ArrayList;
import java.util.Collections;
/*
集合工具类:
1.public static void shuffle(List<?> list) `:打乱集合顺序。
2.public static <T> void sort(List<T> list)`:将集合中元素按照默认规则排序(从小到大)。
- `public static <T> void sort(List<T> list,Comparator<? super T> c)`:将集合中元素按照指定规则排序。
*/
public class CollectionsDemo01 {
public static void main(String[] args) {
method_2();
}
/*
在java中字符串如何大小排序的呢?
1)每两个字符串之间先比较首字母,如果首字母相同在比较第二个字母,依次类推,按照编码值(字典顺序)
ABC < abc --->这里a大于A则不会在向后比较了,直接出结果:ABC < abc
abcdef < uqywywy
2)如果从字符串左向右看,一个短的字符串是长的字符串的子字符串,那么按照字符串长度比较
abc < abcdef
最后结果:ABC < abc < abcdef < uqywywy
*/
private static void method_2() {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
//添加数据
list.add("abc");
list.add("ABC");
list.add("abcdef");
list.add("uqywywy");
//对上述集合进行排序
Collections.sort(list);
//排序之后的集合:[ABC, abc, abcdef, uqywywy]
System.out.println("排序之后的集合:"+list);
}
//ctrl+alt+M抽取方法快捷键
private static void method_1() {
//创建集合对象
ArrayList<Integer> list = new ArrayList<>();
//添加数据
list.add(10);
list.add(5);
list.add(123);
list.add(8);
// System.out.println("打乱之前的集合:"+list);//打乱之前的集合:[10, 5, 123, 8]
// //1.public static void shuffle(List<?> list) `:打乱集合顺序。
// Collections.shuffle(list);
// System.out.println("打乱之后的集合:"+list);
//2.public static <T> void sort(List<T> list)`:将集合中元素按照默认规则排序(从小到大)。
Collections.sort(list);
System.out.println("排序之后的集合:"+list);
}
}
小结:
1.字符串比较大小:
/*
在java中字符串如何大小排序的呢?
1)每两个字符串之间先比较首字母,如果首字母相同在比较第二个字母,依次类推,按照编码值(字典顺序)
ABC < abc --->这里a大于A则不会在向后比较了,直接出结果:ABC < abc
abcdef < uqywywy
2)如果从字符串左向右看,一个短的字符串是长的字符串的子字符串,那么按照字符串长度比较
abc < abcdef
最后结果:ABC < abc < abcdef < uqywywy
*/
private static void method_2() {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
//添加数据
list.add("abc");
list.add("ABC");
list.add("abcdef");
list.add("uqywywy");
//对上述集合进行排序
Collections.sort(list);
//排序之后的集合:[ABC, abc, abcdef, uqywywy]
System.out.println("排序之后的集合:"+list);
}
在java中字符串如何大小排序的呢?
1)每两个字符串之间先比较首字母,如果首字母相同在比较第二个字母,依次类推,按照编码值(字典顺序)
ABC < abc --->这里a大于A则不会在向后比较了,直接出结果:ABC < abc
abcdef < uqywywy
2)如果从字符串左向右看,一个短的字符串是长的字符串的子字符串,那么按照字符串长度比较
abc < abcdef
2.Collections集合工具类中的方法:
1.public static void shuffle(List<?> list) `:打乱集合顺序。
2.public static <T> void sort(List<T> list)`:将集合中元素按照默认规则排序(从小到大)。
8.比较器接口Comparator讲解(理解)
package com.itheima.sh.collections_03;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
public static <T> void sort(List<T> list,Comparator<? super T> c)`:将集合中元素按照指定规则排序。
参数:
list:要排序的集合
c:自定义的比较器规则,属于Comparator接口类型,该接口称为自定义比较器接口,该接口中具有一个抽象方法:
int compare(T o1, T o2) 比较用来排序的两个参数。
说明:该方法是抽象方法,我们需要自定义类实现该接口并实现该方法,在方法体中完成比较规则
底层原理:
参数:
o1: o1表示后加入的值
o2: o2表示已经存在的值
返回值:属于int类型:
如果返回值是正数,那么后加入的值需要向后移动
如果返回值是负数,那么后加入的值需要向前移动
如果返回值是0,那么后加入的值不需要移动
总结:
如果是 o1 - o2 那么就是升序排序
如果是 o2 - o1 那么就是降序排序
*/
public class CollectionsDemo02 {
public static void main(String[] args) {
//调用方法
method_2();
}
private static void method_2() {
//创建集合对象
ArrayList<Integer> list = new ArrayList<>();
//向集合中添加数据
list.add(123);
list.add(456);
list.add(111);
list.add(10);
//调用排序方法进行排序
Collections.sort(list, new Comparator<Integer>() {
/*
参数:
o1是后加入的数据 456
o2是已经存在的数据 123
返回值:
如果是正数,后加入的数据向后移动
如果是负数,后加入的数据向前移动
如果是0,后加入的数据不需要移动
说明:
o1 - o2 升序
第一次比较:
123(o2) 456(o1)----> o1 - o2--->456- 123>0--->结果:123 456
第二次比较:
1) 123 456(o2) 111(o1)--->o1 - o2--->111-456<0--->111 456
2) 123(o2) 111(o1) 456--->o1 - o2--->111-123<0--->111 123
最后结果:111 123 456
第三次比较:
1) 111 123 456(o2) 10(o1)--->o1 - o2--->10-456<0--->111 123 10 456
2) 111 123(o2) 10(o1) 456--->o1 - o2--->10-123<0---> 111 10 123 456
3)111(o2) 10(o1) 123 456--->o1 - o2--->10-111<0--->10 111 123 456
o2 - o1 降序
1)123(o2) 456(o1) ---》123-456<0--->456 123
2)456 123(o2) 111(o1)--->123 - 111>0--->456 123 111
3)456 123 111(o2) 10(o1)--->111-10>0--->456 123 111 10
*/
@Override
public int compare(Integer o1, Integer o2) {
// System.out.println("o1 = " + o1);
// System.out.println("o2 = " + o2);
// return o1 - o2;
return o2 - o1;
}
});
System.out.println(list);
}
/*
需求:将数据 123 456 111 10 存储到集合中并排序
*/
private static void method_1() {
//创建集合对象
ArrayList<Integer> list = new ArrayList<>();
//向集合中添加数据
list.add(123);
list.add(456);
list.add(111);
list.add(10);
/*
使用方法 public static <T> void sort(List<T> list,Comparator<? super T> c)对上述集合进行升序排序
*/
//创建自定义比较器类的对象
/* MyComparator myComparator = new MyComparator();
Collections.sort(list,myComparator);*/
/*
上述自定义比较器类虽然可以实现排序,但是每次还必须定义一个类,比较麻烦
这里我们可以使用匿名内部类简化代码开发:
匿名内部类格式:
new 父类或者父接口(){
重写父类或者父接口中的方法
};
整体是子类对象
*/
//使用匿名内部类方式实现排序
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//升序:o1 - o2
return o1 - o2;
}
});
//升序的结果:[10, 111, 123, 456]
System.out.println("升序的结果:"+list);
}
}
小结:
1.属于Comparator接口类型,该接口称为自定义比较器接口,该接口中具有一个抽象方法:
int compare(T o1, T o2) 比较用来排序的两个参数。
说明:该方法是抽象方法,我们需要自定义类实现该接口并实现该方法,在方法体中完成比较规则
底层原理:
参数:
o1: o1表示后加入的值
o2: o2表示已经存在的值
返回值:属于int类型:
如果返回值是正数,那么后加入的值需要向后移动
如果返回值是负数,那么后加入的值需要向前移动
如果返回值是0,那么后加入的值不需要移动
- 如果是 o1 - o2 那么就是升序排序
如果是 o2 - o1 那么就是降序排序
9.Comparator接口练习
1.存储自定义类的对象:按照年龄从小到大排列,升序
package com.itheima.sh.collections_03;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
按照年龄从小到大排列,升序
*/
public class CollectionsDemo03 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 19);
Student s2 = new Student("李四", 25);
Student s3 = new Student("王五", 21);
Student s4 = new Student("张三", 19);
//2.添加数据
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
/*
产生问题原因:
如果对自定义类进行排序,这里不能使用带一个参数的sort方法,即不能使用
默认排序,之前使用非自定义类(String Integer)不报错原因是
这些类底层实现了一个默认排序的接口Comparable
而自定义类没有实现默认排序接口
解决问题:
我们直接使用带两个参数sort方法排序即使用自定义比较器Comparator接口
需求:
按照年龄从小到大排列,升序
*/
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
});
System.out.println("list = " + list);
}
}
小结:
- 如果对自定义类进行排序,这里不能使用带一个参数的sort方法,即不能使用 默认排序,之前使用非自定义类(String Integer)不报错原因是这些类底层实现了一个默认排序的接口Comparable。
而自定义类没有实现默认排序接口
2.解决问题:
我们直接使用带两个参数sort方法排序即使用自定义比较器Comparator接口
使用方法 public static <T> void sort(List<T> list,Comparator<? super T> c)对上述集合进行升序排序
2.按照年龄从小到大排列,如果年龄相同,姓名短的在前,姓名长的在后(就是按照名字长度升序排序)
package com.itheima.sh.collections_03;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
按照年龄从小到大排列,如果年龄相同,姓名短的在前,姓名长的在后(就是按照名字长度升序排序)
*/
public class CollectionsDemo04 {
public static void main(String[] args) {
//1.创建集合对象
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 19);
Student s2 = new Student("李四", 25);
Student s3 = new Student("王五", 21);
Student s4 = new Student("张小三", 19);
//2.添加数据
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
//3.对上述集合进行排序
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 按照年龄从小到大排列,如果年龄相同
// 姓名短的在前,姓名长的在后(就是按照名字长度升序排序)
//判断年龄年龄是否相同,如果不同直接返回结果
if(o1.age!=o2.age){
//年龄不相等,直接返回结果
return o1.age - o2.age;
}
//如果程序能够执行到这里,说明没有执行return o1.age - o2.age; 也就是说年龄相同,在按照姓名的长度升序排序
/*
o1.name.length() 表示获取o1对象的姓名的长度
o2.name.length() 表示获取o2对象的姓名的长度
*/
return o1.name.length() - o2.name.length();
}
});
//[Student{name='张三', age=19}, Student{name='张小三', age=19}, Student{name='王五', age=21}, Student{name='李四', age=25}]
System.out.println(list);
}
}
3.使用Comparator比较字符串大小
package com.itheima.sh.collections_03;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionsDemo05 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
//添加数据
list.add("abc");
list.add("ABC");
list.add("abcdef");
list.add("uqywywy");
//对上述集合进行排序
// Collections.sort(list);//升序
// //排序之后的集合:[ABC, abc, abcdef, uqywywy]
// System.out.println("排序之后的集合:"+list);
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//降序:o2 - o1 大小降序
/*
如果是大小降序,并且是对字符串进行大小排序,这里可以String类的方法:
int compareTo(String anotherString) 按字典顺序比较两个字符串。
*/
// return o2 - o1;
return o2.compareTo(o1);//降序
// return o1.compareTo(o2);//升序
}
});
//[uqywywy, abcdef, abc, ABC] 降序
System.out.println(list);
}
}
10.可变参数(掌握)
从jdk5之后诞生技术,为了简化方法参数的。格式:
方法修饰符 方法返回值类型 方法名(参数类型 ... 参数名){
}
说明:
1.可变参数只能使用在参数这里,可以接收0个到无限个数据
2.接收的数据类型必须一致
代码演示:
package com.itheima.sh.exchange_args_04;
/*
方法修饰符 方法返回值类型 方法名(参数类型 ... 参数名){
}
*/
public class Demo01 {
public static void main(String[] args) {
//调用方法
// method(10,20,30,40);
//定义数组
int[] arr = {100,200,300};
method(1.2,arr);//600
}
/*
1.可变参数原理是一个数组,将接收的数据都存储到数组中了
int ... num = {10,20};等同于int[] num={10,20};
2.由于可变参数是一个数组,所以在同一个类中不能定义方法参数是数组的方法
可变参数操作起来比数组更灵活
3.可变参数可以接收数组
4.一个方法的参数中只能有一个可变参数,可变参数后面不允许在写内容
*/
private static void method(double d,int ... num) {
//定义变量保存和值
int sum = 0;
//遍历数组取出每个数据
/*for (int x : num) {
sum = sum + x;
}*/
for (int i = 0; i < num.length; i++) {
sum = sum +num[i];
}
System.out.println("sum = " + sum);
}
/*private static void method(int[] num1) {
}*/
}
小结:
1.可变参数原理是一个数组,将接收的数据都存储到数组中了
int … num = {10,20};等同于int[] num={10,20};
2.由于可变参数是一个数组,所以在同一个类中不能定义方法参数是数组的方法
可变参数操作起来比数组更灵活
3.可变参数可以接收数组
4.一个方法的参数中只能有一个可变参数,可变参数后面不允许在写内容
5.变参数只能使用在参数这里,可以接收0个到无限个数据
6.接收的数据类型必须一致
7.在Collections工具类中有一个方法的参数就是可变参数:
package com.itheima.sh.exchange_args_04;
import java.util.ArrayList;
import java.util.Collections;
/*
static <T> boolean addAll(Collection<? super T> c, T... elements)
将所有指定元素添加到指定 collection 中。
参数:
c:集合
elements:是一个可变参数,可以接收多个数据放到集合c中
*/
public class Demo02 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list = new ArrayList<>();
//向集合添加数据
/* list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("aaa");*/
//使用集合工具类快速添加
Collections.addAll(list,"aaa","bbb","ccc");
System.out.println("list = " + list);
}
}