算法通过村——Hash和队列问题解析

news2024/11/20 14:30:27

算法的备胎Hash和找靠山的队列

备胎Hash

        Hash,不管是算法,还是在工程中都会大量使用。很多复杂的算法问题都用Hash能够轻松解决,也正是如此,在算法例就显得没什么思维含量,所以Hash是应用里的扛把子,但在算法里就是备胎的角色,只要有其他方式,一般就不会考虑队列了。这也是面试算法和应用算法的一个区别。

Hash的重要性

        Hash在技术面试中也频繁出现,常见问题有三个:

                1.对象比较为什么要计算hashCode 

                2.HashMap的实现原理;ConcurrentHashMap的实现原理,特别是并发和扩容方面的问题。

                3.ThreadLocal里的Map工作原理

找靠山的队列

        直接考察队列的算法题几乎没有,大部分场景是作为高级算法的一个工具。经典问题是树里的层次遍历相关问题和图 等高级主题中 与 广度优先相关的问题。所以说队列需要找一个靠山才行。 

队列的重要性

        对于Java程序员来说,队列真正的大热门是作为技术面试,考察JUC里的阻塞队列、AQS等的实现原理等。这个一般在多线程相关的课程里讲解。 

Hash基础

Hash的概念和基本特征 

概念

        哈希(Hash)也称为散列,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,这个输出值就是散列值。

基本特征

映射:

        假设数组array存放的是1到15这些数,现在要存在一个大小是7的Hash表中,该如何存储呢?

        存储(如下图所示):

                存储位置计算公式

                        index = number % 7

        读取:

                         index = number % 7

存储案例

将1至6存入的时候,图示如下:

image.png

 将7至13存入的时候,图示如下:

image.png

最后存14 和 15

image.png

读取案例

        假如我们要测试13在不在这个结构中,同样使用上面的公式进行计算。通过计算,

13 % 7 = 6。则可以直接访问array[6]这个位置,很明显是存在的,所以返回true。

        

        假如我们要测试20在不在这个结构中,同样使用上面的公式进行计算。通过计算,

20 % 7 = 6。则可以直接访问array[6]这个位置,但这个位置上只有6和13,没有20,所以返回false。

 碰撞处理方法

碰撞

        在上面例子中,有些在Hash中的位置可能要存储两个甚至多个元素,很明显单纯的数组是不行的(会出现元素覆盖)。这种由       两个不同的输入值,根据同一散列函数计算出的散列值相同的现象  就叫做 碰撞。

碰撞解决方法

  • 开放地址法(Java里的ThreadLocal)
  • 链地址法(Java里的ConcurrentHashMap)
  • 哈希法(布隆过滤器)
  • 建立公共溢出区 

开放定址法

        开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将数据存入其中。

图例

image.png

         例如上面要继续存7,8,9的时候,7没问题,可以直接存到索引为0位置。8本来应该存到索引为1的位置,但是已经满了,所以继续向后找,索引3的位置是空的,所以8存到3位置。同理9存到索引6位置。

疑惑解释

疑惑:             

        这样鸠占鹊巢的方法会不会引起混乱? 比如再存3 和6的话,本来自己的位置好好的,但是被外来户占领了,该如何处理呢?

解释:

        这个问题学习Java里的ThreadLocal后能解开。其基本思想如下:

        ThreadLocal有一个专门存储元素的TheadLocalMap,每次在get 和set元素的时候,会先将目标位置前后的空间搜索一下,将标记为null的位置回收掉,这样大部分不用的位置就收回来了。

        这就像假期后你到公司,每个人都将自己的位子附近打扫干净,结果整个工作区就很干净了。当然Hash处理该问题的整个过程非常复杂,涉及弱引用等等,这些都是Java技术面试里的高频考点。

链地址法

         将哈希表的每个单元作为链表的头节点,所有哈希地址为 i 的元素构成一个同义词链表。即发生Hash冲突时,就把该关键字链在以该单位为头节点的链表的尾部,如下图所示:

image.png

        这种处理方法的问题是处理起来代价还是比较高的。要落地还要进行很多优化

        例如在Java里的ConcurrentHashMap中就使用了这种方式,其中涉及元素尽量均匀、访问和操作速度要快、线程安全、扩容等很多问题 

 错误的Hash结构

看一下下面这个Hash结构,下面的图有两处非常明显的错误

image.png

 错误解释

        首先是数组的长度必须是2的n次幂,这里长度是9,明显有错,然后是entry 的个数不能大于数组长度的75%,如果大于就会触发扩容机制进行扩容,这里明显是大于75%

原因

总:        

        在许多哈希表的实现中,选择2的n次幂作为哈希表的大小,可以提高散列函数的计算速度、解决哈希冲突的效率,并可以更好地利用内存。这些因素都有助于提高哈希表的性能。

分:

  1. 散列函数计算索引:哈希表使用散列函数将键(key)映射到索引,然后将值(value)存储在该索引处。对于2的n次幂大小的哈希表,散列函数可以使用位操作,而不需要执行较慢的模运算。例如,可以使用按位与运算(bitwise AND)操作,通过掩码来获取索引。这样可以提高散列函数的计算速度。

  2. 哈希冲突的解决:在哈希表中,不同的键可能会被散列到相同的索引位置,这称为哈希冲突。为了解决冲突,通常使用开放定址法、链表法或者其他方法。当哈希表的大小为2的n次幂时,使用位移操作(bitwise shift)可以快速计算出下一个索引位置,这样可以加快解决哈希冲突的速度。

  3. 内存分配的优化:许多现代计算机体系结构中,内存是以块(block)的形式进行分配的,其中每个块的大小通常是2的n次幂。如果哈希表的大小与内存块的大小匹配,可以更好地利用内存,减少内存分配的碎片化。

正确的Hash结构

image.png

 解释

        数组的长度即是2的n次幂,而他的size又不大于数组长度的75%。 HashMap的实现原理是先要找到要存放数组的下标,如果是空的就存进去,如果不是空的就判断key值是否一样,如果一样就替换,如果不一样就以链表的形式存在链表中(从JDK8开始,根据元素数量选择使用链表还是红黑树存储)。

队列基础 

队列的概念和基本特征

概念

        队列(Queue)是一种常见的数据结构,它是一种先进先出(First-In-First-Out,FIFO)的线性数据结构。

基本特征

先进先出:节点的排排队次序和出队次序按入队时间先后确定 

实现方式

  • 数组

        队列使用一个固定大小的数组来存储元素,并使用两个指针来标记队列的头部和尾部

  • 链表 

        对于基于链表,因为链表的长度是随时都可以变的,实现起来比较简单。

实现队列

链表实现

package org.example.queue;

public class LinkQueue {
    /**
     * 构建节点
     */
    static class Node{
        public int data;
        public Node next;

        public Node(int data) {
            this.data = data;
        }
    }
    /*
    创建队列头和尾
     */
    private Node front;
    private Node rear;
    private int size;
    // 初始化节点
    public LinkQueue() {
        this.front = new Node(0);
        this.rear = new Node(0);
    }

    /**
     * 入队
     * @param value 入队数据
     */
    public void push(int value){
        Node newNode = new Node(value);
        Node temp = front;
        while (temp.next != null){
            temp = temp.next;
        }
        temp.next = newNode;
        rear = newNode;
        size++;
    }

    /**
     * 出队
     * @return 出队的值
     */
    public int pull(){
        if (front.next == null){
            System.out.println("队列已空,无法出队");
        }
        Node firstNode = front.next;
        front.next = firstNode.next;
        size--;
        return firstNode.data;
    }

    /**
     * 遍历队列
     */
    public void traverse(){
        Node temp = front.next;
        while (temp != null){
            System.out.println(temp.data + "\t");
            temp = temp.next;
        }
    }

    public static void main(String[] args) {
        LinkQueue linkQueue = new LinkQueue();
        linkQueue.push(1);
        linkQueue.push(2);
        linkQueue.push(3);

        System.out.println("The first Node = " + linkQueue.pull());
        System.out.println("队列遍历结果为");
        linkQueue.traverse();
    }
}

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/830377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink State 和 Fault Tolerance详解

有状态操作或者操作算子在处理DataStream的元素或者事件的时候需要存储计算的中间状态,这就使得状态在整个Flink的精细化计算中有着非常重要的地位: 记录数据从某一个过去时间点到当前时间的状态信息。以每分钟/小时/天汇总事件时,状态将保留…

录屏功能怎么录声音?这些方法都能很好的录制

在现代社交媒体的时代,录制视频已成为了人们记录生活和分享经验的重要方式之一。而在录制视频的过程中,声音也是至关重要的一个方面。因此,在手机上使用录屏功能时,如何录制声音成为了一个常见的问题。本文将介绍如何在手机上使用…

Maven可选依赖和排除依赖简单使用

可选依赖 可选依赖指对外隐藏当前所依赖的资源 在maven_04_dao的pom.xml,在引入maven_03_pojo的时候&#xff0c;添加optional <dependency><groupId>com.rqz</groupId><artifactId>maven_03_pojo</artifactId><version>1.0-SNAPSHOT&…

python-Excel数据模型文档转为MySQL数据库建表语句(需要连接数据库)-工作小记

将指定Excel文档转为create table 建表语句。该脚本适用于单一且简单的建表语句 呈现效果 代码 # -*- coding:utf-8 -*- # Time : 2023/8/2 17:50 # Author: 水兵没月 # File : excel_2_mysql建表语句.py import reimport pandas as pd import mysql.connectordb 库名mydb m…

Excel·VBA表格横向、纵向相互转换

如图&#xff1a;对图中区域 A1:M6 横向表格&#xff0c;转换成区域 A1:C20 纵向表格&#xff0c;即 B:M 列转换成每2列一组按行写入&#xff0c;并删除空行。同理&#xff0c;反向操作就是纵向表格转换成横向表格 目录 横向转纵向实现方法1转换结果 实现方法2转换结果 纵向转横…

Socket层代码重构

src/utils新建socket.js import {io} from "socket.io-client"class Socket{constructor(){this.socket io("http://127.0.0.1:5000");}// 连接socketconnect(){this.socket.connect()}login(username,callback){// emit发送this.socket.emit(login,{&quo…

list交并补差集合

list交并补差集合 工具类依赖 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version> </dependency><dependency><groupId>commons-collections&…

并发编程Part 2

1. JMM 问题&#xff1a;请你谈谈你对volatile的理解? volitile 是 Java 虚拟机提供的一种轻量级的同步机制 &#xff0c;三大特性&#xff1a; 保证可见性 不保证原子性 禁止指令重排 线程之间如何通信&#xff1f; 通信是指线程之间以如何来交换信息。一般线程之间的通信…

HTML+CSS+JavaScript:随机点名案例

一、需求 1、点击开始按钮&#xff0c;姓名随机切换 2、点击结束按钮&#xff0c;姓名停止切换&#xff0c;此时显示的姓名即为被抽中者 3、同一个人不能被重复抽中 二、代码素材 以下是缺失JS部分的代码&#xff0c;感兴趣的小伙伴可以先自己试着写一写 <!DOCTYPE htm…

Scrum.org-ScrumMaster认证课-PSM培训

在敏捷学习的道路上继续前行&#xff0c;Leangoo领歌的PSM课程已经开启&#xff0c;认证全球认可&#xff0c;还不用续证&#xff0c;可以了解一下。 Scrum是目前运用最为广泛的敏捷开发方法&#xff0c;是一个轻量级的项目管理和产品研发管理框架&#xff0c;旨在最短时间内交…

Elasticsearch 商业启示

上月的“红帽事件”&#xff0c;说明开源软件的“客服模式”行不通&#xff0c;那么&#xff0c;开源软件如何赚钱呢&#xff1f;既不能卖软件&#xff0c;又不能卖支持服务&#xff0c;该怎么办呢&#xff1f;我现在的看法是&#xff0c;只剩下一种模式是可行的&#xff0c;开…

Windows用户如何安装新版本cpolar内网穿透超详细教程

Windows用户如何安装新版本cpolar内网穿透 文章目录 Windows用户如何安装新版本cpolar内网穿透 在科学技术高度发达的今天&#xff0c;我们身边充斥着各种电子产品&#xff0c;这些电子产品不仅为我们的工作带来极大的便利&#xff0c;也让生活变得丰富多彩。我们可以使用便携的…

方法论揭秘|研发数字化转型,这家保险企业做对了什么?

7月27日&#xff0c;FCS 2023 第 7 届中国金融 CIO 峰会&#xff08;深圳站&#xff09;如期举行。大会以「洞见智慧金融」为主题&#xff0c;深度解读中国金融行业数字化转型现状&#xff0c;探讨金融行业信息化趋势、数字供应链金融服务、金融科技创新等问题&#xff0c;期望…

js:使用LetterAvatar通过canvas实现浏览器中生成字母头像

实现效果 LetterAvatar的原理就是利用了浏览器对象canvas 在线体验&#xff1a;https://mouday.github.io/tools/pages/letter-avatar/index.html LetterAvatar.js 完整代码 /** LetterAvatar* * Artur Heinze* Create Letter avatar based on Initials* based on https:/…

环形链表的进一步探究

茕茕白兔&#xff0c;东走西顾&#xff0c;衣不如新&#xff0c;人不如故 往期回顾&#xff1a; 数据结构——双向链表 数据结构——单链表 数据结构——顺序表 文章目录 如何判断一个链表是否为环形链表 环形链表的判断的深入探究 例1&#xff1a;沸羊羊追美羊羊 例…

多线程案例(3)

文章目录 多线程案例三三、 定时器 大家好&#xff0c;我是晓星航。今天为大家带来的是 多线程案例三 相关的讲解&#xff01;&#x1f600; 多线程案例三 三、 定时器 定时器是什么 定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就…

AgileBoot - 全栈项目启动

AgileBoot-Back-End: 基于Ruoyi做了大量重构优化的基础快速开发框架。采用Springboot Vue 3 Mybatis Plus 更面向对象的业务建模 面向生产的项目。&#xff08;非玩具项目&#xff09; 首先克隆代码&#xff0c;同是克隆前端和后端的代码。 前端代码启动&#xff1a; np…

机器学习---概述(二)

文章目录 1.模型评估1.1 分类模型评估1.2 回归模型评估 2. 拟合2.1 欠拟合2.2 过拟合2.3 适当拟合总结&#xff1a; 3.深度学习3.1层次&#xff08;Layers&#xff09;&#xff1a;3.2 神经元&#xff08;Neurons&#xff09;&#xff1a;3.3 总结 1.模型评估 模型评估是机器学…

【2种方法,jmeter用一个正则提取器提取多个值!】

jmeter中&#xff0c;用json提取器&#xff0c;一次提取多个值&#xff0c;这个很多人都会。但是&#xff0c;用正则提取器一次提取多个&#xff0c;是否可以呢&#xff1f; 肯定&#xff0c;很多人都自信满满的说&#xff0c;可以&#xff01;形如&#xff1a;token":&q…

MC0111配速MC0112白日梦Ⅰ

MC0111配速 难度&#xff1a; 白银 时间限制&#xff1a;1秒 占用内存&#xff1a;128M 小码哥参加了学校的定向越野比赛&#xff0c;赛完后&#xff0c;他踌躇满志地拿着自己的成绩单&#xff0c;看着一段段的数据&#xff0c;想算一下自己整场比赛的平均配速是多少。…