归纳常见的数据结构
栈
数据结构:数据的存储格式以及它的组成格式。
面试的时候,一般会问不用的数据结构都有哪些特点?给你一个场景让你设计不同的数据结构
1、首先先回答涉及的数据结构是什么样子的
2、然后再问答涉及到的数据结构它的有缺点分别是什么,怎么去弥补缺点。
需求:我想把A,B,C三个元素插入到栈数据结构中,怎么做?
存放的顺序:A、B、C
取出的顺序:C、B、A
栈的数结构特点:先进后出
举例现实生活中有哪些栈的实例:
1、子弹夹
2、煤炉烧煤球
队列
需求:我想把A、B、C三个元素放入队列中,怎么做呢?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-muoWEhEN-1644584699752)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211202132069.png)]
存放的顺序:A、B、C
取出的顺序:A、B、C
队列的数据结构的特点:先进先出
举例现实生活中有哪些队列的案例:
1、排队做核酸
数组
长度是固定的,存储的元素数据类型是一直的,拥有下表索引,方便我们通过索引获取对应位置上的元素值,怎么定义一个数组?
静态数组:int [] arr = {11,22,33,44,55};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SXVTHyNt-1644584699753)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211202510717.png)]
数组的特点:查询快,增删慢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kjZTypz2-1644584699754)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211202605992.png)]
链表
一个链条有多个节点组成的数据结构
结点:是有两部分组成,是由数据域和指针域组成,包含了数据和指针。需求:将11,22,33,44,55存放到链表中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBR3KKVi-1644584699754)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211202754166.png)]
链表的特点:查询慢,增删快
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qLHvj8l-1644584699754)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211202906075.png)]
树
Tree 它是由根和叶子节点组成的数据结构
叶子节点,当结点下面没有任何分支的时候,我们称之为叶子结点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CzAKGNfx-1644584699755)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211203711285.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DSwdc5xp-1644584699755)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211203744941.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fx0tZoXc-1644584699755)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211203730924.png)]
二叉树的遍历顺序 :前序遍历,中序遍历,后序遍历,注意:这里的前中后指的是根的顺序
前序遍历:根左右,从最顶层的根开始遍历:11,22,44,33,55,66
中序遍历:左根右,从最顶层的根开始遍历:22,44,11,55,33,66
后序遍历:左右根:44,22,55,66,33,11
工作面试必备
哈弗曼树(最小生成二叉树)
当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。
哈夫曼树相关的几个名词
路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径。图 1 中,从根结点到结点 a 之间的通路就是一条路径。
路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。图 1 中从根结点到结点 c 的路径长度为 3。
结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权。例如,图 1 中结点 a 的权为 7,结点 b 的权为 5。
结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。例如,图 1 中结点 b 的带权路径长度为 2 * 5 = 10 。
树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL” 。例如图 1 中所示的这颗树的带权路径长度为:
WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3
B+树 红黑树 自平衡二叉树
需要掌握红黑树的生成过程,红代表什么,黑代表什么?
有什么特点?
- 单一节点存储更多的元素,使得查询的IO次数更少。
- 所有查询都要查找到叶子节点,查询性能稳定。
- 所有叶子节点形成有序链表,便于范围查询。
大数据中有哪些红黑树的案例?
需要掌握参数的变导致红黑树的变化,怎么去变化的?
红黑树的目的是什么?
将来有哪些优化方式?
图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50syFDlY-1644584699756)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211205259577.png)]
哈希表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9xJWnLJ-1644584699756)(C:\Users\stu\AppData\Roaming\Typora\typora-user-images\image-20220211205333318.png)]
集合ArrayList
需求:使用ArrayList存储字符串并遍历(如果字符串有重复的需要去除)
public class ArrayListDemo1 {
public static void main(String[] args) {
//创建集合对象
ArrayList list = new ArrayList();
//向集合中添加元素
list.add("hello");
list.add("world");
list.add("java");
list.add("hadoop");
list.add("Hive");
list.add("hello");
list.add("world");
System.out.println("去重之前的集合为:" + list);
//创建新的集合对象
ArrayList list2 = new ArrayList();
//创建迭代器
Iterator iterator = list.iterator();
//遍历旧的集合
while(iterator.hasNext()){
String s = (String) iterator.next();
//获取到元素之后,拿着这个元素去新集合中查找,如果找到说明重复,就不添加,反之添加到新集合中
if(!list2.contains(s)){
list2.add(s);
}
}
System.out.println("去重之后的集合为:" + list2);
}
}
使用ArrayList存储自定义对象并遍历(并去重)
学生对象:姓名和年龄都一样的时候,表示是同一个人。
我们按照处理字符串重复的逻辑来处理自定义对象重复,发现结果并没有进行去重
为什么呢?
要想知道为什么,就得知道问题出现在了哪一行代码中。
经过简单地分析后发现,问题出现了在判断是否包含的那一行代码中
因为只有当if语句为true的时候,才会将元素添加到新集合中
说明我们这里判断,一直都是为true的,换句话contains这个方法并没有生效
要搞清楚为什么不会生效,就得搞清楚contains这个方法底层是怎么做的。contains方法底层调用的是equals方法,但是呢,Student类中并没有重写equals方法
所以调用的是Object类中的equals方法,而Object类中的equals方法比较的是地址值,学生对象都是new出来的
地址值必然不一样,equals比较的结果永远是false,加上我们代码中的!,结果永远是为true,所以无论如何都会添加到
新集合中,最终结果没有去重。解决方法:元素类重写equals()方法 自动生成即可
public class ArrayListDemo2 {
public static void main(String[] args) {
//创建集合对象
ArrayList list1 = new ArrayList();
//向集合中添加数据
Student stu1 = new Student("张飞",18);
Student stu2 = new Student("马超",19);
Student stu3 = new Student("关羽",20);
Student stu4 = new Student("刘备",21);
Student stu5 = new Student("张飞",18);
list1.add(stu1);
list1.add(stu2);
list1.add(stu3);
list1.add(stu4);
list1.add(stu5);
System.out.println(list1);
//创建新的集合对象
ArrayList list2 = new ArrayList();
//迭代器
Iterator iterator = list1.iterator();
while(iterator.hasNext()){
Student student = (Student) iterator.next();
//注意这里contains里面包含的是equals方法,若Student类中没有重写equals方法的时候,运用的是Object的equals的方法
//相当于 ==
if(!list2.contains(student)){
list2.add(student);
}
}
System.out.println(list2);
}
}
集合Vector
Vector:
底层数据结构是数组,查询快,增删慢
线程安全,效率低(虽然它是线程安全的,我们今后也不会去使用它,后面会说一个线程安全的类代替它)Vector特有的方法:
public void addElement(Object obj)
将元素添加到集合的末尾 效果上和add()一样
public Object elementAt(int index)
获取指定索引处的元素 get(int index)
public Enumeration elements()
返回此向量的组件的枚举。
public class VectorDemo {
public static void main(String[] args) {
//创建Vector集合对象
Vector vector = new Vector();
//向集合中添加元素
//public void addElement(Object obj) 将元素添加到集合的末尾 效果上和add()一样
vector.addElement("hello");
vector.addElement("java");
vector.add("hello");
vector.add("java");
System.out.println(vector);
//public Object elementAt(int index) 获取指定索引处的元素 get(int index)
System.out.println(vector.elementAt(3));
System.out.println(vector.get(3));
System.out.println("=============================================");
//public Enumeration elements() 返回此向量的元素的枚举。
//简单记忆 你就把这个对象看作成一个迭代器
Enumeration elements = vector.elements();
while (elements.hasMoreElements()) {
Object o1 = elements.nextElement();
String s = (String) o1;
System.out.println(s);
}
}
}
集合LinkedList
LinkedList:
1、底层数据结构是双链表,查询慢,增删快
2、线程是不安全的,效率高LinkedList特有的方法:
1、添加功能:
public void addFirst(Object e) 在集合的开头添加元素
addLast(Object e) 在结合末尾添加元素,等同于add()方法
2、获取功能:
public Object getFirst() 获取集合中的第一个元素
getLast() 获取集合中的最后一个元素
3、删除功能:
public Object removeFirst() 从集合中删除第一个元素并返回
public Object removeLast() 从集合中删除最后一个元素并返回
public class LinkedListDemo1 {
public static void main(String[] args) {
//创建LinkedList集合对象
LinkedList linkedList = new LinkedList();
//向集合中添加元素
//public void addFirst(Object e) 在集合的开头添加元素
//addLast(Object e) 在结合末尾添加元素,等同于add()方法
linkedList.add("hadoop");
linkedList.addFirst("hello");
linkedList.addLast("java");
System.out.println(linkedList);
System.out.println("========================");
//public Object getFirst() 获取集合中的第一个元素
//getLast() 获取集合中的最后一个元素
System.out.println(linkedList.getFirst());
System.out.println(linkedList.getLast());
System.out.println("========================");
//public Object removeFirst() 从集合中删除第一个元素并返回
//public Object removeLast() 从集合中删除最后一个元素并返回
System.out.println(linkedList.removeFirst());
System.out.println(linkedList.removeLast());
System.out.println(linkedList);
}
}
面试题:
请用LinkedList模拟栈数据结构的集合,并测试
栈的特点:先进后出
题目真正的意思是,自己写一个集合类,底层是LinkedList,调用自己定义的方法实现栈数据结构。
class MyStack{
private LinkedList linkedList;
//构造无参的构造方法,初始化LinkedList
MyStack(){
linkedList = new LinkedList();
}
//自定义的添加方法
public void MyAdd(Object obj){
//想要先进后出,所有得将新加入的放在前面
linkedList.addFirst(obj);
}
//自定义的获取元素的方法
public Object MyGet(){
//如果使用linkedList.getLast()的话、获取的都是同一个元素
// return linkedList.getLast();
return linkedList.removeFirst();
}
//判断是否为空
public boolean myIsEmpty(){
return linkedList.isEmpty();
}
}
public class LinkedListTest1 {
public static void main(String[] args) {
//创建自定义的集合
MyStack myStack = new MyStack();
myStack.MyAdd("hello");
myStack.MyAdd("java");
myStack.MyAdd("bigdata");
while(!myStack.myIsEmpty()){
Object o = myStack.MyGet();
System.out.println(o);
}
// //创建集合对象
// LinkedList linkedList = new LinkedList();
//
linkedList.add("hello");
linkedList.add("world");
linkedList.add("java");
// linkedList.addFirst("hello");
// linkedList.addFirst("world");
// linkedList.addFirst("java");
//
// //遍历
// Iterator iterator = linkedList.iterator();
// while (iterator.hasNext()){
// Object next = iterator.next();
// System.out.println(next);
// }
//
// //如果面试的时候,遇到这样的题目
// //按照上面的做法,一分都没有
}
}
泛型
泛型:
把明确数据类型的工作,提前到了编译时期,在创建集合的时候明确存储元素的数据类型。
这样的做法有点向把数据类型当作参数一样传递,所以泛型还有一个叫法:参数化类型泛型的语句定义格式:
<引用数据类型>
注意:尖括号中的数据类型只能是引用数据类型泛型的好处:
1、将我们运行时期出现的问题,提前到编译时期
2、不需要做强制类型转换
3、优化了代码。消除了不必要的黄色警告线,使代码看起来更简洁通过观察API发现,泛型可以出现在类,接口,方法上面,我们看到的类似于这样的都叫泛型,一般来说,泛型出现大多使用的集合中。
public class GenericDemo {
public static void main(String[] args) {
//创建一个List集合对象
//在JDK1.7之后,泛型会进行自动类型推断
ArrayList<String> list = new ArrayList<>();
//向集合中添加元素
list.add("hello");
list.add("hello");
list.add("hello");
list.add("hello");
list.add("hello");
// list.add(20);
// list.add(12.34);
// list.add(new Student());
//遍历
// Iterator iterator = list.iterator();
// while (iterator.hasNext()) {
// Object next = iterator.next();
//
// String s = (String) next; //ClassCastException
//
// System.out.println(next);
// }
//遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
}
/遍历
// Iterator iterator = list.iterator();
// while (iterator.hasNext()) {
// Object next = iterator.next();
//
// String s = (String) next; //ClassCastException
//
// System.out.println(next);
// }
//遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
}
}
}