目录
1. 树型结构(了解)
1.1 概念
为什么会存在二叉树?
树:高效的查找与搜索语义。
当你看到某个场景使用的二叉树,百分之99都是用在查找和搜索。
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看 起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
1. 有一个特殊的节点,称为根节点,根节点没有前驱节点(如下图的A就是根节点)
2. 除根节点外,其余节点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合 Ti (1 <= i <= m) 又是一棵与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继
注释:从根节点出发分成的M个子集,彼此之间不能相交,若相交,就不是树结构。且只能有一个父节点。
3. 树是递归定义的。
现实生活中,企业中员工的分类其实就是一个"树"结构
把图倒过来看,也有点像金字塔结构。
但如果是一个线性结构会怎么样?
所有人大家都在一个"目录"中,比如当前公司中一共有300号员工,要找到一个特定的员工最坏情况下得找300次,复杂度:O(n)
可如果现在是一个树结构,按照员工的职级进行分类,当前300个员工一共分为五级,搜索一个特定员工只需要5次即可找到。复杂度为:O(logn)——树的高度
300可能看不出有多快,但如果是1亿人呢?树结构只需要10次不到就够了,快了不止一星半点儿
再比如操作系统中OS的文件系统就是基于树结构进行文件管理的
就是电脑硬盘
节点概念结论
1.子树不相交
2.除了根节点没有父节点之外,每个节点有且只有一个父节点。
3.树,边的个数为x,和树中节点的个数为n, 它们之间想关系为:x = n -1 (每个节点都有一个父节点,只有根节点没有父节点)
4.节点和树的"度":
节点的度:一个节点含有的子树的个数称为该节点的度;(说白了就是该节点有多少分支)
树的度:一棵树中,最大的节点的度称为树的度;(最大分支的节点度为树的度)
比如:上图中企业管理图中
5.叶子结点: 度为0的节点,如上图的:人事、财务、研发等等;都没有子树,都是叶子节点
非叶子节点: 度不为0的节点,还存在子树,如上图中:副总1、2、3等;都是非叶子节点
6.根节点:没有父节点的结点,树中有且只有一个,上图中:董事长;就是根节点
7.节点的层次(高度):从根节点开始计算,根节点为第一层,上图中:总经理为第二层,副总为第三层,人事、研发等为第四层。
这里就还有个概念,树的高度: 上图中当前树中节点层次最大的即为树的高度,4
树的以下概念只需了解,在看书时只要知道是什么意思即可:
对应的图:
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>=0)棵互不相交的树的集合称为森林
1.3 树的表示形式(了解)
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法, 孩子表示法、孩子兄弟表示法等等。
我们这里就简单的了解其中最常用的孩子兄弟表示法。
1.4 树的应用
文件系统管理(目录和文件)
2. 二叉树(重点)
2.1 概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉 树组成。
二叉树的特点:
1. 每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点。
2. 二叉树的子树有左右之分,其子树的次序不能颠倒,因此二叉树是有序树。
注释:一个树型结构的最大节点度为N,那么这就是N叉树。
2.2 二叉树的基本形态
上图给出了几种特殊的二叉树形态,从左往右依次是:空树、只有根节点的二叉树、节点只有左子树、节点只有右 子树、节点的左右子树均存在,一般二叉树都是由上述基本形态结合而形成的。
2.3 两种特殊的二叉树
1. 满二叉树: 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果 一个二叉树的层数为K,且结点总数是 2^k - 1 ,则它就是满二叉树。
2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n 个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为 完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
注释:完全二叉树和满二叉树存在的节点编号必须一一对应。
完全二叉树用在:"堆"-优先级队列
2.4 二叉树的性质
1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i - 1) (i>0)个结点
2. 若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k - 1 (k>=0)
3. 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有 n0=n2+1
4. 具有n个结点的完全二叉树的深度k为 上取整.
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i 的结点有:
解析举例:拿上图中的满二叉树举例。
是深度为 4 的满二叉树。全部的节点个数为:1 + 2 + 4 + 8 = 2^4 - 1 = 15个,对应上面的性质2
第三层还有第四层的最大节点个数为: 2^(3 - 1) 和 2^(4 - 1) == 4 和 8 对应性质 1
常见二叉树介绍:
满二叉树:
完全二叉树:
从视觉上来看,完全二叉树就是满二叉树缺了一个"右一下角"而已。
完全二叉树和满二叉树存在的节点编号一一对应。(编号顺序是上到下,左到右)
注释:就算上图第二幅完全二叉树中没有最下面编号为 10的节点,任然还是完全二叉树,因为他满足只缺右下角,存在的节点编号和满二叉树一一对应。
什么样不算完全二叉树?
这就不是完全二叉树了。
一:还缺了个左下角。
二:本身编号为10的节点在满二叉树中是 11,没有一一对应了。
还有一种:
该图中少了一个原本编号为 7的节点。虽然看上去还是少了右下角,但一样的,编号跟满二叉树对不上了,没有做到一一对应。
注释:主要是看编号是否对应!!!
完全二叉树官方定义:完全二叉树的节点编号和满二叉树完全一致。
也可以这么理解:完全二叉树只可能最深的一层结点没有拉满没有拉满的这一层,结点都是靠左排列
注释:在完全二叉树中不存在有右子树却没有左子树的情况。不然就不是完全二叉树
再深度理解一下:在完全二叉树中,若存在度为1的节点,这个节点必然只有左树没有右树而且这个节点有且只有1个! !
如图:有且只有一个。
关于完全二叉树编号的问题:
从1开始编号
从0开始编号
二分搜索树:节点的值之间有一个大小关系
平衡二叉树:该树中任意一个节点的左右子树高度差 <= 1
2.5 二叉树的存储
二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
顺序存储下次讲。
二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:
示例代码:
// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
// 孩子双亲表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}
孩子双亲表示法后序在平衡树位置再说,这次采用孩子表示法来构建二叉树。
2.6 二叉树的基本操作
2.6.1 二叉树的遍历
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作 依赖于具体的应用问题(比如:打印节点内容、节点内容加1)。 遍历是二叉树上最重要的操作之一,是二叉树上进 行其它运算之基础。
遍历:按照一定的顺序访问这个集合的所有元素,做到不重复,不遗漏。
二叉树的遍历问题:所有二叉树的基础操作,其实所有二叉树问题的解决思路都是遍历问题的衍生。
在遍历二叉树时,如果没有进行某种约定,每个人都按照自己的方式遍历,得出的结果就比较混乱,如果按照某种 规则进行约定,则每个人对于同一棵树的遍历结果肯定是相同的。
如果N代表根节点,L代表根节点的左子树,R代 表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:
上面三种叫:深度优先遍历 dfs
还有一种,广度优先遍历 :层序遍历(BFS),将二叉树一层层的访问。
对于二叉树来说,一共有四种遍历方式。
上面的三种深度优先遍历是以递归的形式进行的,比如访问左子树时,是把左子树全部访问完毕了才会去范围右子树。
举例:前序遍历
前序遍历总结三个字:根、左、右。有根先执行根等等。。。
打印结果:ABDEC
线条执行顺序:
相当于递归思想一样,一个节点没走到底,就一直往下走,其他都是停着。等走到不能再走时,再一个个的返回。
一般来说,深度优先遍历借助 " 栈 " 观察; 广度优先遍历借助 " 队列 " 观察
比如说栈:一个节点的根入栈,当一个根节点的左子树和右子树都执行完毕时出栈。
举例:中序遍历
注释:当根节点第二次走到时才能"访问"
当我们第一次走到A的时候,因为是中序遍历,不能马上打印这个A,先压入栈中。
然后递归范围左子树B,B是左子树的根节点,也是第一次访问,不能马上打印,先压入栈中。
再去访问以B为根节点的左子树D,D也一样是第一次出现不能打印,压入栈。
当以D为根节点去访问左子树时,发现为空返回。回到D后此时D是第二次访问了,可以打印了D
然后访问以D为根节点的右子树,访问为空就返回。当左右子树都访问完了以后,把D弹出栈,然 后返回到B节点。此时B是第二次访问了就打印B
以B为根节点的左子树已经执行完了就去执行右子树。E,E下面都空返回打印E。
后面的都以此类推~结果:DBEAC
举例:后续遍历 ——左右根
注释:当第三次走到根节点才能访问。
跟中序遍历差不多,不过是第三次才能被打印。打印:DEBCA
注释:前面三种怎么走?看跟在第几位就需要访问几次才能被打印。
法则:按这个方法画图,无论什么二叉树都不会错。
1. 前序遍历:根,左,右 。 在第一位,所以第一次访问就可以打印。
2. 中序遍历:左,根,右 。 在第二位,所以第二次访问才可以打印。
3. 前序遍历:左,右 ,根。 在第三位,所以第三次访问才可以打印。
举例:层序遍历
最简单:从上到下,从左到右。一层走完再去走下一层。
打印顺序:ABCDE
思考题:按上面讲的方法,写出下面二叉树的四种遍历顺序。
前序遍历:ABDEGHCF
中序遍历:DBGHEACF
后续遍历:DHGEBFCA
层序遍历:ABCDEFGH
遍历特性:
1. 前序遍历的第一个节点一定是根节点。
2. 中序遍历的结果中:左子树在该根节点的左边,右子树在该根节点的右边。
如:看上面中序的结果,A节点的左边DBGHE都是左子树,CF是右子树。
当以B为根节点情况也是同样 左 E 右 GHE
3. 后续遍历的结果一翻转:reverse
ACFBEGHD——根右左。
相当于前序遍历的镜像,后续遍历逆序的结果就是先走右子树了。
后序遍历的最后—个结果为根节点
代码实现:
1. 先创建节点类
//二叉树的节点
class TreeNode<E> {
//当前节点的值
E val;
//左子树的跟
TreeNode<E> left;
//右子树的跟
TreeNode<E> right;
//构造方法
public TreeNode(E val) {
this.val = val;
}
}
2. 前序遍历的实现
public class MyBinTree<E> {
public TreeNode<Character> root;//根节点
//建立一个测试二叉树
public void build(){
//假设有8个节点
TreeNode<Character> node1 = new TreeNode<>('A');
TreeNode<Character> node2 = new TreeNode<>('B');
TreeNode<Character> node3 = new TreeNode<>('C');
TreeNode<Character> node4 = new TreeNode<>('D');
TreeNode<Character> node5 = new TreeNode<>('E');
TreeNode<Character> node6 = new TreeNode<>('F');
TreeNode<Character> node7 = new TreeNode<>('G');
TreeNode<Character> node8 = new TreeNode<>('H');
//节点的连接
//A
node1.left = node2;
node1.right = node3;
//B
node2.left = node4;
node2.right = node5;
//E
node5.left = node7;
//G
node7.right = node8;
//C
node3.right = node6;
//连接根节点
root = node1;
}
/**
* 传入一颗二叉树的根节点root。就能按照前序遍历根左右的方式进行输出
*/
public void preOrder(TreeNode root){
//终止条件
if(root == null){
return;
}
//我们只负责打印跟
System.out.print(root.val + " ");
//左子树的输出交给子函数
preOrder(root.left);
//右子树的输出交给子函数
preOrder(root.right);
}
}
测试:看看我们写的前序遍历结果是否正确
//二叉树测试模块
public class TreeTest {
public static void main(String[] args) {
MyBinTree binTree = new MyBinTree();
binTree.build();
System.out.print("前序遍历结果为:");
binTree.preOrder(binTree.root);
}
}
运行结果:正确
3.实现后续遍历的方法
跟前序的实现差不多,输出的位置不一样。
/**
* 传入一颗二叉树的根节点root。就能按照中序遍历左根右的方式进行输出
*/
public void inOrder(TreeNode root){
//终止条件
if(root == null){
return;
}
//左子树的输出交给子函数
inOrder(root.left);
//我们只负责打印跟
System.out.print(root.val + " ");
//右子树的输出交给子函数
inOrder(root.right);
}
测试:
//二叉树测试模块
public class TreeTest {
public static void main(String[] args) {
//前序
// MyBinTree binTree = new MyBinTree();
// binTree.build();
// System.out.print("前序遍历结果为:");
// binTree.preOrder(binTree.root);
//中序
MyBinTree binTree1 = new MyBinTree();
binTree1.build();
System.out.print("中序遍历结果为:");
binTree1.inOrder(binTree1.root);
}
}
运行结果:DBGHEACF
/**
* 传入一颗二叉树的根节点root。就能按照中序遍历左根右的方式进行输出
*/
public void inOrder(TreeNode root){
//终止条件
if(root == null){
return;
}
//左子树的输出交给子函数
inOrder(root.left);
//我们只负责打印跟
System.out.print(root.val + " ");
//右子树的输出交给子函数
inOrder(root.right);
}
4. 后续遍历也差不多
/**
* 传入一颗二叉树的根节点root。就能按照后序遍历左右根的方式进行输出
* @param root
*/
public void postOrder(TreeNode root) {
if (root == null) {
return;
}
// 先打印左子树,交给子函数
postOrder(root.left);
// 再打印右子树
postOrder(root.right);
// 最后打印根
System.out.print(root.val + " ");
}
测试
//后续
MyBinTree binTree2 = new MyBinTree();
binTree2.build();
System.out.print("后序遍历结果为:");
binTree2.postOrder(binTree2.root);
运行结果:DHGEBFCA
前、中、后排序总代码:
package bintree;
/**
* 二叉树的基本操作
*/
//二叉树的节点
class TreeNode<E> {
//当前节点的值
E val;
//左子树的跟
TreeNode<E> left;
//右子树的跟
TreeNode<E> right;
//构造方法
public TreeNode(E val) {
this.val = val;
}
}
public class MyBinTree<E> {
public TreeNode<Character> root;//根节点
//建立一个测试二叉树
public void build(){
//假设有8个节点
TreeNode<Character> node1 = new TreeNode<>('A');
TreeNode<Character> node2 = new TreeNode<>('B');
TreeNode<Character> node3 = new TreeNode<>('C');
TreeNode<Character> node4 = new TreeNode<>('D');
TreeNode<Character> node5 = new TreeNode<>('E');
TreeNode<Character> node6 = new TreeNode<>('F');
TreeNode<Character> node7 = new TreeNode<>('G');
TreeNode<Character> node8 = new TreeNode<>('H');
//节点的连接
//A
node1.left = node2;
node1.right = node3;
//B
node2.left = node4;
node2.right = node5;
//E
node5.left = node7;
//G
node7.right = node8;
//C
node3.right = node6;
//连接根节点
root = node1;
}
/**
* 传入一颗二叉树的根节点root。就能按照前序遍历根左右的方式进行输出
*/
public void preOrder(TreeNode root){
//终止条件
if(root == null){
return;
}
//我们只负责打印跟
System.out.print(root.val + " ");
//左子树的输出交给子函数
preOrder(root.left);
//右子树的输出交给子函数
preOrder(root.right);
}
/**
* 传入一颗二叉树的根节点root。就能按照中序遍历左根右的方式进行输出
*/
public void inOrder(TreeNode root){
//终止条件
if(root == null){
return;
}
//左子树的输出交给子函数
inOrder(root.left);
//我们只负责打印跟
System.out.print(root.val + " ");
//右子树的输出交给子函数
inOrder(root.right);
}
/**
* 传入一颗二叉树的根节点root。就能按照后序遍历左右根的方式进行输出
* @param root
*/
public void postOrder(TreeNode root) {
if (root == null) {
return;
}
// 先打印左子树,交给子函数
postOrder(root.left);
// 再打印右子树
postOrder(root.right);
// 最后打印根
System.out.print(root.val + " ");
}
}
测试总代码:
package bintree;
//二叉树测试模块
public class TreeTest {
public static void main(String[] args) {
//前序
MyBinTree binTree = new MyBinTree();
binTree.build();
System.out.print("前序遍历结果为:");
binTree.preOrder(binTree.root);
System.out.println();
System.out.print("中序遍历结果为:");
binTree.inOrder(binTree.root);
System.out.println();
System.out.print("后序遍历结果为:");
binTree.postOrder(binTree.root);
}
}
二叉树其他基本操作的实现
1.写一个方法统计当前二叉树中的节点个数
/**
* 传入一颗以root为根的二叉树,就能求出节点个数为多少
*/
public int getNodes(TreeNode root){
//1.边界
if(root == null){
return 0;
}
//此时二叉树不为空,root != null,根节点存在,所以至少存在 1
//总节点个数 = 根节点 + 左子树的所用节点 + 右子树的所有节点
return 1 + getNodes(root.left) + getNodes(root.right);
}
⒉.统计一颗二叉树中叶子结点的个数
/**
* 传入一颗以root为根的树,就能求出叶子节点的个数
*/
public int getLeafNodes(TreeNode root){
//1. 边界
if(root == null){
return 0;//此时节点都为空了,哪来什么子树
}
//到了这里说明该节点不为空,看看它的子树
//2.判断是否为叶子节点
if(root.left == null && root.right == null){
return 1;
}
//3.到了这里说明该节点不是叶子节点,且有子树。
//看看它的子树里面是否存在叶子节点。
//总叶子节点个数 = 左树中的叶子节点个数 + 右树中的叶子节点个数
return getLeafNodes(root.left) + getLeafNodes(root.right);
}
3.求出第k层的节点个数 k <= height(root)
/**
* 传入一颗以root为根的二叉树,就能求出第k层的节点个数 k <= height(root)
*/
public int getKLevelNodes(TreeNode root,int k){
//1.边界
if(root == null || k <= 0){
return 0;
}
//当k == 1时,说明求的是输出的根节点这层
if(k == 1){
//第一层为根节点,肯定只有一个。
return 1;
}
//当k != 1时,说明改层不是要求的层次,交给子方法去求
// 求以root为根的第k层结点个数 =
// 以root.left为根的第k - 1层结点个数 + 以root.right为根的第k - 1层结点个数
return getKLevelNodes(root.left, k - 1) + getKLevelNodes(root.right, k - 1);
}
4.求二叉树的高度
/**
* 传入一颗以root为根的二叉树,就能求出该树的高度是多少
*/
public int height(TreeNode root){
//1.边界
if(root == null){
return 0;
}
// //2.该节点下面没有子树了,这就是它的最高高度了。
// if(root.left == null && root.right == null){
// return 1;
// }
// int left = height(root.left);//左子树里面最深的高度
// int right = height(root.right);//右子树里面最深的高度
// return 1 + (left > right ? left : right);
//也可以这么写:
// 1就是当前树根所在的第一层
// 树高 = 根节点这一层 + max(左子树的树高,右子树的树高)
return 1 + Math.max(height(root.left), height(root.right));
}
5.判断—颗二叉树中是否包含指定的值val
/**
* 判断以root为根的二叉树中是否包含指定值val
*/
public boolean contains(TreeNode<E> root,E val){
if (root == null) {
return false;
}
if (root.val.equals(val)) {
return true;
}
// 继续在左子树和右子树中寻找是否包含指定值
return contains(root.left,val) || contains(root.right,val);
}
2.7 基础面试题
注释:在leetcode和牛客网上,若定义了全局变量,千万不要加static! ! ! !
假设此时有100个人都在做这个题
每个人其实就是一个该类的对象,若你定义的是static数组,这个数组就被这100个人共享,别人把这个数组修改对你是可见。
Loading Question... - 力扣(LeetCode)101. 对称二叉树 - 力扣(LeetCode)Loading Question... - 力扣(LeetCode)
2.8 二叉树的层序遍历
因为二叉树天然的"递归"结构,因此二叉树的递归代码结合语义可以很方便的写出。
反而二叉树的迭代写法就比递归难一些。
二叉树的层序遍历―借助队列实现
为什么要用队列?因为要利用 先入先出的特性。
执行顺序:
1.树根先入队列,队列:A
然后队列里面的第一位数我们这简称为:根
2.如果根的左节点存在,就让根的左节点入栈
3.如果根的右节点存在,就让根的右节点入栈
队列:A、B、C
4.第一位数左右节点指向完毕就弹出。
队列:B、C
5. 开始执行队列的第一位数,B现在作为根节点。
执行左右节点入队列,有就入。
队列:B、C、D、E
6. B弹出
队列:C、D、E
后面的步骤都一样,直到队列为空结束
注释:这里的弹出就表示打印这个节点值
结论:
1. 每当遍历—层结点结束,队列中恰好存储了下一层要遍历的结点。
2. 当整个队列为空,二叉树的层序遍历就结束了~
代码实现:
/**
* 借住队列,实现二叉树的层序遍历
*/
public void levelOrder(TreeNode<E> root){
//创建一个队列,用双端队列
Deque<TreeNode<E>> queue = new LinkedList<>();
queue.offer(root);
//循环的终止条件就是队列为空
while(!queue.isEmpty()){
//当前队列里面的节点个数,就是当前需要执行的循环次数。
int n = queue.size();
for (int i = 0; i < n; i++) {
TreeNode<E> node = queue.poll();//出队列
System.out.print(node.val + " ");//打印弹出的节点
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
}
}
有了层序遍历这个方法就可以改写很多我们之前写的方法,不再使用递归去写。
举例:用层序遍历统计节点个数
/**
* 使用层序遍历统计二叉树的节点个数
*/
public int getNodesNonRecursion(TreeNode root){
//1.边界
if(root == null){
return 0;
}
int size = 0;
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode cur = queue.poll();
//此处的遍历指的是讲节点个数统计
size ++;
if(cur.left != null){
queue.offer(cur.left);
}
if(cur.right != null){
queue.offer(cur.right);
}
}
return size;
}
拓展知识Map接口
Map接口:存储一个键值对数据
key = value
定义方法:
Map< K , V > map = new HashMap<>( );
//常用方法,put方法:录入
map.put(key,value);
containsKey方法:判断key值是否存在
containsValue方法:判断value值是否存在
现实生活中,名字经常重复,用身份证号对应名字。
map(身份证号,名字);
代码示例:
import java.util.HashMap;
import java.util.Map;
//Map接口——存储一个键值对数据
public class MapTest {
public static void main(String[] args) {
//key不重复,value可以重复
Map<Integer,Integer> map = new HashMap<>();
//录入
map.put(1,66);
map.put(2,99);
//value可以重复,用一个key会得到一个value值
//相当于key是一个编号,一个编号对应一个物品,X,Y坐标轴一个意思
map.put(3,66);
System.out.println(map.containsKey(3));//是否存在编号3
System.out.println(map.containsValue(66));//是否存在value值66的值
System.out.println(map.containsValue(77));//是否存在value值77的值
}
}