队列和Hash的特征-算法通关村

本文详细介绍了哈希表的基础概念,包括其时间复杂度优势和碰撞处理方法,如开放地址法和链地址法。同时,还探讨了队列的基本特征和实现,特别是基于链表的队列操作。
摘要由CSDN通过智能技术生成

队列和Hash的特征-算法通关村


1.Hash基础

1.1Hash的概念和基本特征

  • 哈希(Hash)也称为散列,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,这个输出值就是散列值。
  • 很多人可能想不明白,这里的映射到底是啥意思,为啥访问的时间复杂度为O(1)?我们只要看存的时候和读的时候分别怎么映射的就知道了。
  • 我们现在假设数组array存放的是1到15这些数,现在要存在一个大小是7的Hash表中,该如何存呢?我们存储的位置计算公式是:
    index=number 模 7
  • 这时候我们将1到6存入的时候,图示如下:
  • 然后继续存7到13:
  • 最后存14到15
  • 这时候我们会发现有些数据被存到同一个位置了,我们后面再讨论。接下来,我们看看如何取。
    假如我要测试13在不在这里结构里,则同样使用上面的公式来进行,很明显13模7=6,我们直接访问array[6]这个位置,很明显是在的,所以返回true。
    假如我要测试20在不在这里结构里,则同样使用上面的公式来进行,很明显20模7=6,我们直接访问arrayl6J这个位置,但是只有6和13,所以返回false。
    理解这个例子我们就理解了Hash是如何进行最基本的映射的,还有就是为什么访问的时间复杂度为O(1)。

1.2碰撞处理方法

  • 在上面的例子中,我们发现有些在Hash中很多位置可能要存两个甚至多个元素,很明显单纯的数组是不行的,这种两个不同的输入值,根据同一散列函数计算出的散列值相同的现象叫做碰撞。
    那该怎么解决呢?常见的方法有:开放定址法(Java里的Threadlocal)链地址法(Java里的ConcurrentHashMap)、再哈希法(布隆过滤器)、建立公共溢出区。后两种用的比较少,我们重点看前两个。
1.2.1开放地址法
  • 开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
  • 例如上面要继续存7,8,9的时候,7没问题,可以直接存到索引为0位置。8本来应该存到索引为1的位置,但是已经满了,所以继续向后找,索引3的位置是空的,所以8存到3位置。同理9存到索引6位置。
  • 这里你是否有一个疑惑:这样鸠占鹊巢的方法会不会引起混乱?比如再存3和6的话,本来自己的位置好好的,但是被外来户占领了,该如何处理呢?这个问题直到我在学习Java里的ThreadLocal才解开。具体过程可以学习一下相关内容,我们这里只说一下基本思想。
  • ThreadLocal有一个专门存储元素的TheadLocalMap,每次在get 和set元素的时候,会先将目标位置前后的空间搜索一下,将标记为null的位置回收掉,这样大部分不用的位置就收回来了。这就像假期后你到公司,每个人都将自己的位子附近打扫干净,结果整个工作区就很干净了。当然Hash处理该问题的整个过程非常复杂,涉及弱引用等等,这些都是Java技术面试里的高频考点。

1.2.2链地址法
  • 将哈希表的每个单元作为链表的头结点,所有哈希地址为i的元素构成一个同义词链表。即发生冲突时就把该关键字链在以该单元为头结点的链表的尾部。例如:
  • 这种处理方法的问题是处理起来代价还是比较高的,要落地还要进行很多优化,例如在Java里的ConcurrentHashMap中就使用了这种方式,其中涉及元素尽量均匀、访问和操作速度要快、线程安全、扩容等很多问题。
    我们来看一下下面这个Hash结构,下面的图有两处非常明显的错误,请你先想想是啥。
  • 首先是数组的长度必须是2的n次幂,这里长度是9,明显有错,然后是entry 的个数不能大于数组长度的75%,如果大于就会触发扩容机制进行扩容,这里明显是大于75%,正确的图应该是这样的:
  • 数组的长度即是2的n次幂,而他的size又不大于数组长度的75%。HashMap的实现原理是先要找到要存放数组的下标,如果是空的就存进去,如果不是空的就判断key值是否一样,如果一样就替换,如果不一样就以链表的形式存在链表中(从JDK8开始,根据元素数量选择使用链表还是红黑树存储)。

2.队列基础知识

2.1队列的概念和基本特征

  • 队列的特点是节点的排队次序和出队次序按入队时间先后确定,即先入队者先出队,后入队者后出队,即我们常说的FIFO(first in first out)先进先出。队列实现方式也有两种形式,基于数组和基于链表。对于基于链表,因为链表的长度是随时都可以变的,实现起来比较简单。如果是基于数组的,会有点麻烦,这里只看一下基于链表实现的方法。

2.2实现队列

  • 基于链表实现队列,只要在尾部后插入元素,在 front 删除元素就行了。

  •   public class Queue {
          public static void main(String[] args) {
              LinkQueue linkQueue = new LinkQueue();
              linkQueue.push(1);
              linkQueue.push(2);
              linkQueue.push(3);
              linkQueue.push(4);
              System.out.println("第一个出队元素为:" + linkQueue.pop());
              System.out.println("遍历队列为:");
              linkQueue.traverse();
          }
      }
      
      class LinkNode {
          public int val;
          public LinkNode next;
      
          public LinkNode(int val) {
              this.val = val;
          }
      }
      
      class LinkQueue {
          private LinkNode front;
          private LinkNode rear;
          private int size;
          public LinkQueue(){
              this.front = new LinkNode(0);
              this.rear = new LinkNode(0);
          }
      
      
          //入队
          public void push(int value){
              LinkNode newNode = new LinkNode(value);
              LinkNode temp = front;
              while(temp.next != null){
                  temp = temp.next;
              }
              temp.next = newNode;
              rear = newNode;
              size++;
          }
      
          //出队
          public int pop(){
              if(front.next == null){
                  System.out.println("队列已空...");
              }
              LinkNode firstNode = front.next;
              front.next = firstNode.next;
              size--;
              return firstNode.val;
          }
      
          //遍历队列
          public void traverse(){
              LinkNode temp = front.next;
              while(temp != null){
                  System.out.print(temp.val + "\t");
                  temp = temp.next;
              }
          }
      }
    
    
    
    
  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

种一棵树leng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值