• 数据结构八:二叉搜索树,Map,Set,哈希


    前言:

     

    目录

    1.二叉搜索树

    1.1:概念

     1.2:模拟实现二叉树的操作

     1.2.1:插入

    1.2.2:查找 

     1.2.3:删除

    2.概念和模型

    2.1:概念

    2.2:模型

    3.Map

    3.1:Map.Entry的说明,v>

    3.2:Map的常用方法说明

    4.Set的说明书

    5.oj题练习

    5.1.复制待随机指针的链表

    5.2:前K个高频单词

    6.哈希表

    6.1:冲突

    6.1.1:概念

    6..2:冲突-避免-负载因子调节

    6.2.3:冲突解决--闭散列

    6.3.3:冲突解决-开散列/哈希桶

    7.HashCode()和equals()的区别 

    7.1:面试题:


    这是数据结构的最后一篇,这篇会讲到Map和set还有哈希。学了这些,遇到莫些题,你会觉得很简单。

    1.二叉搜索树

    1.1:概念

    二叉搜索树又称二叉排序树---二叉树节点的值都是唯一的,不重复的

    1.若它的左子树不为空。则左子树的所有节点的值都小于根节点的值

    2,若他的右子树不为空,则右子树的所有节点的值都大于根节点的值。

    94604c7ea6f0466a991b885f5bf893b8.png

     1.2:模拟实现二叉树的操作

     1.2.1:插入

    插入是在二叉树的叶子节点进行插入的

    1. public boolean insert(int key) {
    2. TreeNode ret=new TreeNode(key);
    3. //判断此树是不是空树
    4. if(root==null){
    5. root=ret;
    6. return true;
    7. }
    8. TreeNode parent=null;
    9. TreeNode cur=root;
    10. while(cur!=null){
    11. //判断它是不是大于此节点的值
    12. if(cur.key//大于根节点,往右边插
    13. parent=cur;//记录父亲节点
    14. cur=cur.right;
    15. }else if(cur.key>key) {//小于根节点的,往左边插
    16. parent =cur;
    17. cur=cur.left;
    18. }
    19. }//此时到了叶子节点
    20. if(ret.key< parent.key){
    21. parent.left=ret;
    22. }else {
    23. parent.right=ret;
    24. }
    25. return true;
    26. }

    30301d4dfaf348dba69dc7f338d3a341.png     


    1.2.2:查找 

    若根节点不为空

    如果根节点key==查找的key返回true

    如果根节点key>查找的key,在左子树查找。

    如果根节点key<查找的key,在右子树查找。

    1. public TreeNode search(int key) {
    2. //查找根节点是否为null
    3. if(root==null){
    4. return null;
    5. }
    6. TreeNode cur=root;
    7. while(cur!=null){
    8. if(cur.key>key){//比根节点小
    9. cur=cur.left;
    10. }else if(cur.key//比根节点大
    11. cur=cur.right;
    12. }else{
    13. return cur;//找到
    14. }
    15. }
    16. return null;
    17. }

     1.2.3:删除

    删除比较麻烦,要分为三大类。设待删除节点为cur .待删除的父亲节点为parent.

    1. //删除节点
    2. public void removeChild(TreeNode cur,TreeNode parent){
    3. //分三种情况
    4. //1 cur.left==null
    5. if(cur.left==null){
    6. //判断是不是根节点
    7. if(cur.equals(root)){
    8. root=root.right;
    9. }
    10. if(cur.equals(parent.left)){
    11. parent.left=cur.left;
    12. }
    13. if(cur.equals(parent.right)){
    14. parent.left=cur.right;
    15. }
    16. }else if(cur.right==null){ //2.cur.right==null
    17. if(cur.equals(root)){
    18. root=root.left;
    19. }
    20. if(cur.equals(parent.right)){
    21. parent.right=cur.left;
    22. }
    23. if(cur.equals(parent.left)){
    24. parent.left=cur.left;
    25. }
    26. }else{//3.cur.left!=null&&cur.right!=null
    27. //用一个叫做替罪羊
    28. //可以找左树的最右边,就是左边最大的,也小于根节点
    29. //可以找右边的最左边,就是右边最小的,也大于根节点
    30. TreeNode target=cur.left;//左树
    31. TreeNode targetParent=cur;
    32. while(target.right!=null){//左树的最右边
    33. targetParent=target;
    34. target=target.right;
    35. }
    36. cur.key=target.key;//将替罪羊的值赋值给cur
    37. //将替罪羊删除
    38. if(target.equals(targetParent.left)){
    39. targetParent.left=target.left;
    40. }else{
    41. targetParent.right=target.right;
    42. }
    43. }
    44. }
    45. }

    2.概念和模型

    2.1:概念

    Map和Set是一种专门用来进行搜索的容器或者数据结构,其底层是一棵二叉搜索树。其搜索的效率与其具体的实际化子类有关。可能在查找时进行一些插入和删除的操作,即动态查找。

    本节介绍的Map和Set是一种适合动态查找的集合容器。
     

    2.2:模型

    把搜索的数据称为关键字(Key)和关键字对应的称为值(Value),将其称为Key-Value的键值对

    1.纯Key模型----快速查找莫个数据是否存在

    2.Key-Value模型---统计文件中每个单词出现的次数。<单词,单词出现的次数>

    3.Map

    Map是一个接口类,没有继承Collection(Set和List都继承了),该类存储的是结构的键值对,并且K一定是唯一的。

    3.1:Map.Entry的说明

    Map.Entry是Map内部实现的用来存放键值对映射关系的内部类

    方法解释
    K getKey()返回entry中的Key
    V getValue()

    返回Key中的Value

     

     

     

     

    1. public class Test {
    2. public static void main(String[] args) {
    3. TreeMapmap=new TreeMap<>();
    4. map.put("hello",4);//h-104
    5. map.put("world",1);//w-119
    6. map.put("abc",7);//a-97
    7. //返回所以的Key-Value的映射关系
    8. Set< Map.Entry>entrySet=map.entrySet();
    9. for (Map.Entryentry:entrySet) {
    10. System.out.println("Key:"+entry.getKey()+" "+
    11. "Value"+entry.getValue());
    12. }
    13. }
    14. }

    74ff0e42c2214654b460977901bab1ba.png

     从中,看出了什么吗?

    Key的是安找升序排序的,故:Key一定要比较,不然会报错。

    3.2:Map的常用方法说明

    方法解释
    V get(Object Key)返回Key对应的Value
    V getOrDefault(Object key ,v defaultValue)返回key对应的Value,key不存在,返回默认值
    V put(K key V value)设置key对应的Value//Key一定可以比较
    V remove(Object key)删除Key对应的映射关系
    Set keySet()返回所有Key的不重复集合
    Collectionvalues()返回所有Value的可重复集合
    boolean containsKey(Object key)判断是否包含Key
    boolean containsKey(Object value)判断是否包含value

     

     

     

     

     

     

     

     

     

     

    注意:

    1.Map是一个接口,不能直接实例化对象。

    2.Map中存放键值对的Key是唯一的,Value是可以重复的。

    3.Key不能为空,否则就会包空指针异常。

    4.Map中键值对的Key不能直接修改(如果要修改,只能先删除,再进行重新插入!)


    4.Set的说明书

    Set继承Collection的接口类,Set中存储Key

    方法解释
    boolean add(E e)添加元素 重复元素不会添加
    void clear ()清空集合
    boolean contains(Object 0)判断o是否存在集合中
    lterator iterator()返回迭代器
    boolean remove(Object o)删除集合中的o
    int size()返回set中元素的个数
    boolean isEmpty()检测set是否为空,空返回true 不空返回false
    object [] toArray

    将set中的元素转换为数组返

     

     

     

     

     

     

     

     

     

     

    注意:

    1.set中只存储了key,并且要求key一定要唯一。

    2.Set的底层是使用map来实现的。

    3.set最大的功能就是对集合中的元素去重

    4.set中不能插入null的key。

    5.oj题练习

    5.1.复制待随机指针的链表

    在笔试中出现过。可以注意下哟

    138. 复制带随机指针的链表 - 力扣(LeetCode)

    7ee7b138f2104331942682b363e65366.png

    bf2c2eebf8ff48ba9b35f0c677bb5abb.png

    6452c56968d44024a1d2bf19f639d8bf.png

    1. class Solution {
    2. public Node copyRandomList(Node head) {
    3. HashMap map=new HashMap<>();
    4. Node cur=head;//将链表都存到map中
    5. while(cur!=null){
    6. Node node=new Node(cur.val);//创建一个节点
    7. map.put(cur,node);
    8. cur=cur.next;
    9. }
    10. //第二次遍历链表
    11. cur=head;
    12. while(cur!=null){
    13. map.get(cur).next=map.get(cur.next);
    14. map.get(cur).random=map.get(cur.random);
    15. cur=cur.next;
    16. }
    17. return map.get(head);
    18. }
    19. }

    5.2:前K个高频单词

     692. 前K个高频单词 - 力扣(LeetCode)

     这道题我就说说思路就不画图了。

    1.我们用HashMap 来存放单词。key是单词。value 是单词出现的次数

    2.Top-k问题。将前K个单词按照频率来建立小堆。

    3.K后面的单词,依次和堆顶元素相比较,单词出现频率大于堆顶的。该单词入堆。堆顶元素出堆。如果频率出现相同。比堆顶单词首字母大。该单词入堆,堆顶元素出堆。按照单词大小建立大堆。

    4.用列表保存堆中元素,之后逆转。

    1. class Solution {
    2. public List topKFrequent(String[] words, int k) {
    3. HashMap map = new HashMap<>();
    4. //保存字符串数组到map
    5. for (String x : words) {
    6. if (map.containsKey(x)) {
    7. int val = map.get(x);
    8. map.put(x, val + 1);
    9. } else {
    10. map.put(x, 1);
    11. }
    12. }
    13. //Top-K问题
    14. PriorityQueue> q1 =
    15. new PriorityQueue<>(new Comparator>() {
    16. @Override
    17. public int compare(Map.Entry o1,
    18. Map.Entry o2) {
    19. if (o1.getValue().compareTo(o2.getValue()) == 0) {
    20. //频率相同,按照单词首字母大小建立大堆
    21. return o2.getKey().compareTo(o1.getKey());
    22. }//频率不同,按照频率大小建立小堆
    23. return o1.getValue().compareTo(o2.getValue());
    24. }
    25. });
    26. //遍历map当中的entry
    27. for (Map.Entry entry : map.entrySet()) {
    28. //把前k个元素建成小堆
    29. if (q1.size() < k) {
    30. q1.offer(entry);
    31. } else {//后k个元素,逐渐和堆顶元素进行比较
    32. Map.Entry s1 = q1.peek();
    33. //频率大
    34. if (entry.getValue().compareTo(s1.getValue()) > 0) {
    35. //比堆顶元素大
    36. //将堆顶元素出堆
    37. q1.poll();
    38. //再将这个元素入堆
    39. q1.offer(entry);
    40. }//和堆顶元素频率相等
    41. else {
    42. if (entry.getValue().compareTo(s1.getValue()) == 0) {
    43. //比较单词首字母的大小
    44. if (s1.getKey().compareTo(entry.getKey()) > 0) {
    45. //比堆顶元素的首字母大,堆顶元素出堆,
    46. q1.poll();
    47. q1.offer(entry);
    48. }
    49. }
    50. }
    51. }
    52. }
    53. //将堆的元素赋值给List
    54. List list = new ArrayList<>();
    55. for (int i = 0; i < k; i++) {
    56. Map.Entry m1 = q1.poll();
    57. list.add(m1.getKey());
    58. }
    59. Collections.reverse(list);
    60. return list;
    61. }
    62. }

    6.哈希表

    使元素存储位置与它的关键码之间能够建立一一映射的关系。

    6.1:冲突

    6.1.1:概念

    不同关键字通过相同哈希计算出相同的哈希地址,这种现象称为哈希冲突或哈希碰撞。

    冲突的发生使必然的,我们只能尽量降低冲突。

    6..2:冲突-避免-负载因子调节

    载荷因子:填入表中的元素个数/散列表的长度(严格限制在0.7-0.8)之间

    6.2.3:冲突解决--闭散列

    闭散列:也叫开放定址法。可以将key存放到冲突位置中的‘下一个'空位置中。

    1.线性探测:

    从发送冲突的位置开始,依次向后探测,直至找到下一个空位置。---冲突数据堆积在一起

    2.二次探测:

    找下一个空位置的方法:Hi=(Ho+i^2)%m

    Hi:要插入的位置

    Ho:hash(key)=key%capacity--长度。计算出来的位置

    i:第几次冲突。m:长度。

    6.3.3:冲突解决-开散列/哈希桶

    b3985830d70643e986b1000be07e1d86.png

    7.HashCode()和equals()的区别 

    hashCode() 方法的作用是获取哈希码。返回的是一个整数,作用是:确定对象在哈希表的索引下标。

    equals()判断两个对象是否相等。

    1.两个对象的hashcode一样,equals一定一样?

    不一定

    2.两个对象的equals一样,hashcode一定一样?

    一定

    7.1:面试题:

    重写equals()方法就可以比较两个对象是否相等,为什么还要重写hashcode()方法?

    HashSet和HashMap在判断两个元素是否相等时,会先判断hashCode是否相等,如果

    hashCode相等,才会用equals()方法比较是否相等。


    总结:以上就是我总结的Map和Set,如果有错,请各位铁子留言改正,若感觉不错,一键三连。

     

     

     

     


     

     

     

     

     

  • 相关阅读:
    导出excel单元格时实现换行
    借助 Terraform 功能协调部署 CI/CD 流水线-Part2
    【C++】函数指针 ④ ( 函数指针做函数参数 | 使用函数指针间接调用函数 | 函数指针做参数 | 函数指针类型的本质 | 函数指针做参数意义 )
    Xception学习笔记
    5分钟理透LangChain的Chain
    软件复杂性的来源与应对
    Java 实现视频Mov转Mp4
    使用Golang调用摄像头
    【LeetCode热题100】--169.多数元素
    1095 Cars on Campus
  • 原文地址:https://blog.csdn.net/qq_56701924/article/details/127714495