目录
常见数据结构:
数据结构:数据的存储格式,以及它的组成格式
栈:
栈的数据结构特点:先进后出
图解:
队列:
队列的数据结构特点:先进先出
图解:
数组:
数组长度是固定的,存储的元素数据类型是一致的,拥有下标索引,方便我们通过索引获取对应位置上的元素值
定义一个数组:int[] arr={11,22,33,44,55}
数组的数据结构特点:查询快,增删慢
图解:
链表:
链表就是有多个结点组成的数据结构
结点:是由两部分组成,是由数据域和指针域组成,包含了数组和指针。
图解:
链表的数据结构特点:查询慢,增删快
树:
树,Tree,它是由根和叶子结点组成的数据结构
叶子结点:当结点下面没有任何分支的时候,我们称该结点为叶子结点
图解:这是一个树:
当有多个树存在时,我们称这些树总体为森林。
图解:
二叉树:
一个树中每个结点的子结点的个数最多只有两个的时候,这样的树,我们称之为二叉树
图解:
二叉树的遍历顺序
以上图为例,二叉树的遍历顺序分为三种:前序遍历,中序遍历,后序遍历
注意:这里的前中后指的是根的顺序
前序遍历(根左右,从最顶层的根开始区分左右): 11,22,44,33,55,66
中序遍历(左根右,从最顶层的根开始区分左右): 22,44,11,55,33,66
后序遍历(左右根,从最顶层的根开始区分左右): 44,22,55,66,33,11
其他的一些树:
哈夫曼树:最小生成二叉树
B+树:又称红黑树,自平衡二叉树
哈希表:
哈希表是由哈希函数和Hashtable组成,其中哈希函数可以自定义
图解:
上述的哈希表的案例中,使用的元素都是数字,但是在实际开发中,数据肯定不止一种数据类型,可能会有其他的数据类型,比如字符串等。这种情况一般都会结合一个哈希函数去存储,可以自己去定义给出哈希函数,也可以不用自己定义,使用源码中给出的哈希函数逻辑,但一般情况下适用,特殊情况下不适用,需要自己修改。
例如将"abc"存放到哈希表中,一般情况下是默认将字符串的每一个字符转换成对应的ASCII码值相加再与哈希表的长度取模求出该字符串存放的位置。
即(97+98+99)%10 = 4,故存放在索引4位置。
虽然解决了存放问题,但若是此时有一个字符串"bac"也要存放进哈希表中,逻辑上来说"bac"字符串应当存放在一个不同的位置上,但是结果是同字符串"abc"存放在同一个索引位置4上。这就形成了一个问题:哈希碰撞
解决办法通常是加上一个随机数,但也只是增大了他们不存放在同一位置上的可能性,因为随机数也有可能两次的随机数相同。
于是在实际开发中,哈希表通常不会单独使用,一般情况下,会与链表结合使用(双链表)
图解:
图:
图是一组由边连接结点的一个图
图解:
图的相关知识:
顶点:图的基本单位,也就是上图中的结点
边:指的是顶点之间的关联关系,也就是上图中的线
相邻顶点:由一条边连接在一起的顶点
度:指的是一个顶点包含的相邻顶点的数量
权重:边上的数值,也可以理解为两个顶点之间的“距离”
一个图是由G=(V,E)组成,V表示的是一组顶点,E表示的是一组边,用邻接表表示法表示为 :
图的分类:
图分为有向图和无向图,具体看图解:
图的遍历
遍历图有两种方式:广度优先搜索(BFS),深度优先搜索(DFS)
无论是哪种搜索方式,搜索出来的答案不绝对,这里给出一个例子:
上图对图进行遍历:
广度优先搜索:1,2,4,8,3,5,7,6
深度优先搜索:1,2,3,4,8,5,6,7
需求:
给出邻接矩阵,画出对应的图并遍历
图解:
List接口的子类
ArrayList类
ArrayList类的底层逻辑是数组。具有查询快,增删慢的特点,线程不安全,效率高
ArrayList案例:
需求:使用ArrayList存储字符串并遍历(如果有重复的需要去除)
代码实现:
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayTest1 {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("hello");
arrayList.add("world");
arrayList.add("java");
arrayList.add("bigdata");
arrayList.add("hive");
arrayList.add("spark");
arrayList.add("bigdata");
System.out.println("去重之前的集合:"+arrayList);
// 创建第二个集合对象存放去重之后的集合
ArrayList list2 = new ArrayList();
// 创建迭代器对象
Iterator iterator = arrayList.iterator();
// 遍历
while(iterator.hasNext()){
String s=(String)iterator.next();
if(!list2.contains(s)){
list2.add(s);
}
// 如果list2中没有该元素,就添加该元素到list2集合中
}
System.out.println("去重之后的集合:"+list2);
}
}
这里集合中bigdata元素重复了一次,需要去除,输出结果:
需求二:使用ArrayList存储自定义对象并遍历(并去重)
学生对象,姓名和年龄都一样的时候,表示的是同一个人
代码实现:
先创建学生类:
public class Students {
private String name;
private int age;
public Students() {
}
public Students(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
再定义ArrayList类进行进一步操作
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayTest2 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
Students s1 = new Students("张翼德", 20);
Students s2 = new Students("赵云", 17);
Students s3 = new Students("张翼德", 20);
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println("去重前的集合:");
System.out.println(list);
ArrayList list2 = new ArrayList();//定义第二个集合存储去重后的集合
Iterator iterator = list.iterator();
// 遍历
while(iterator.hasNext()){
Students s=(Students)iterator.next();
if(!list2.contains(s)){
list2.add(s);
}
}
System.out.println("去重后的集合:");
System.out.println(list2);
}
}
输出结果:
根据输出结果来看,虽然实现了遍历,但是并没有实现去重效果,通过分析发现可能是再if判断中出现了问题。
分析:if语句行,只有当if语句为true时,才会执行添加功能,根据输出结果来看,if语句始终是true,也就是说contains方法并没有生效。这是需要进入到contains方法底层实现中去查看
以下为contains的底层实现:
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))//调用的是equals方法
return i;
}
return -1;
}
通过观察底层实现发现contains底层调用的是equals方法,而上述Students类中并没有重写equals方法,因此这里调用的是Object类中的equals方法,故比较的是地址值,学生对象都是new出来的,所以对象地址值必然不一样。故equals结果永远为false,回到if语句中加上!就永远为true,所以无论如何都会添加到新集合中,最终没有实现去重。
解决办法:在元素类Students加入重写equals方法即可,可自动生成。其他代码不变。
//重写的equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Students students = (Students) o;
return age == students.age && Objects.equals(name, students.name);
}
此时输出结果即可实现去重:
Vector类
底层数据结构是数组,查询快,增删慢,线程安全,效率低(虽然安全,但实际开发不使用)
Vector类特有的方法
public void addElement(Object obj) 将元素添加到集合的末尾 效果上和add()一样
public Object elementAt(int index) 获取指定索引处的元素
get(int index) public Enumeration elements() 返回此向量的组件的枚举。
Vector类举例实现与ArrayList类基本相同,只是方法名不同而已。
代码举例:
import java.util.Enumeration;
import java.util.Vector;
public class VectorDemo {
public static void main(String[] args) {
//创建Vector集合对象
Vector vector = new Vector();
//向集合添加元素
vector.addElement("hello");
vector.addElement("java");
vector.add("world");
vector.addElement("java");
// System.out.println(vector);
//public Object elementAt(int index)获取指定索引处的元素 get(int index)
Object o = vector.elementAt(0);
System.out.println(o);
System.out.println(vector.elementAt(1));
System.out.println(vector.elementAt(2));
System.out.println(vector.elementAt(3));
// System.out.println(vector.elementAt(4));
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、添加功能:
public void addFirst(Object e) 在集合的开头添加元素
addLast(Object e) 在结合末尾添加元素,等同于add()方法
2、获取功能:
public Object getFirst() 获取集合中的第一个元素
getLast() 获取集合中的最后一个元素
3、删除功能:
public Object removeFirst() 从集合中删除第一个元素并返回
public Object removeLast() 从集合中删除最后一个元素并返回
具体功能实现代码举例:
import java.util.LinkedList;
public class LinkedTest1 {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("hello");
list.add("world");
list.add("java");
System.out.println(list);
// 在集合开头添加元素
list.addFirst("bigdata");
System.out.println("开头添加元素:");
System.out.println(list);
// 在集合末尾添加元素
list.addLast("superman");
System.out.println("末尾添加元素:");
System.out.println(list);
// 获取集合第一个元素
System.out.println("获取第一个元素:"+list.getFirst());
System.out.println(list);
// 获取集合最后一个元素
System.out.println("获取最后一个元素:"+list.getLast());
System.out.println(list);
// 删除第一个元素
System.out.println("删除第一个元素"+list.removeFirst());
System.out.println("删除后的集合:"+list);
// 删除最后一个元素
System.out.println("删除最后一个元素:"+list.removeLast());
System.out.println("删除后的集合:"+list);
}
}
输出结果:
案例:
需求:
请使用LinkedList模拟栈数据结构的集合,并测试
分析:题意是需要我们自己设计一个集合类,它的底层逻辑实现是LinkedList类,再调用自己的方法去实现栈数据结构
代码实现:
自己设计的集合类MineStack:
import java.util.LinkedList; public class MineStack { private LinkedList linkedList; //无参构造,底层为LinkedList无参构造创建对象 MineStack(){ LinkedList linkedList = new LinkedList(); } // 底层调用linkedList中的添加第一个元素 void Mineadd(Object obj){ linkedList.addFirst(obj); } public boolean ismyEmpty(){ return linkedList.isEmpty(); } // 底层调用为删除第一个元素并返回 public Object mineGet(){ return linkedList.removeFirst(); } }
由于栈数据结构的特点是先进后出,所以我们需要使用LinkedList类中的removeFirst方法删除第一个元素并返回,这样第二个元素就变成了第一个元素,在下一次遍历时就可以输出该元素,最终实现先进后出,直到集合为空停止
测试类MineStackTest
public class MIneStackTest { public static void main(String[] args) { // 创建对象 MineStack stack = new MineStack(); // 添加数据到集合 stack.Mineadd("hello"); stack.Mineadd("world"); stack.Mineadd("java"); stack.Mineadd("bigdata"); // 遍历判断是否含有元素.含有就删除第一个元素并输出该元素 while(!stack.ismyEmpty()){ Object o = stack.mineGet(); System.out.println(o); } } }
输出结果: