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

算法的备胎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.mzph.cn/news/22333.shtml

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

相关文章

C#实现端口扫描和执行cmd命令、调用摄像头

C#端口扫描 using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading;namespace PortScanner {class Program{static void Main(string[] args){// 设置扫描参数string host "localho…

【安全测试】安全测试威胁建模设计方法STRIDE

目录 背景 TM(ThreatModeling) 实践 具体流程 资料获取方法 背景 目前安全测试一般都存在如下问题: 安全测试人员不懂业务,业务测试人员不懂安全,安全测试设计出现遗漏是无法避免的安全测试点繁多复杂,单点分析会导致风险暴…

构建高效读写分离MySQL主从复制架构,应对高可用挑战!

前言 在现代数据库架构中,MySQL主从复制技术扮演着重要角色。它不仅可以提升数据库性能和可扩展性,还赋予系统卓越的高可用性和灾难恢复能力。本文将深入剖析MySQL主从复制的内部机制,同时通过一个实际案例,展示其在实际场景中的…

selenium 截屏

当前环境: Windows 10 Python 3.7 selenium 3.141.0 Google Chrome 115.0.5790.110 (64 位) from selenium import webdriver import base64if __name__ __main__:#driver webdriver.Chrome()driver.get(https://www.baidu.com/)# 1.…

torch.multiprocessing

文章目录 张量共享torch.multiprocessing.spawnmultiprocessing.Pool与torch.multiprocessing.Pool阻塞非阻塞map阻塞非阻塞 starmap torch.multiprocessing是具有额外功能的multiprocessing,其 API 与multiprocessing完全兼容,因此我们可以将其用作直接…

简单游戏截图_可控截取内容2

一个需求 我需要在场景中截取不同层级的截图(如只截模型或只截UI或只截外部相加看到的画面 或全都截或和Shader配合呈现人眼夜视仪热成像的画面切换) 将截图排到列表中,在场景UI中展示出来 如何做 相机要能够看到不同的画面 将当前帧画面存储下来 将存储的画面展示出…

【数字IC基础】时序违例的修复

时序违例的修复 建立时间违例保持时间违例Buffer 插入位置参考资料 建立时间违例 基本思路是减少数据线的延时、减少 Launch clock line 的延时、增加capture clock line的delay 加强约束,重新进行综合,对违规的路径进行进一步的优化,但是一…

【C++】万能引用、完美转发

万能引用 万能引用的格式如下&#xff1a; template<typename T> void PerfectForward(T&& t) {Fun(t); }虽然写的是&&和右值引用类似&#xff0c;但是它可以接收左值引用和右值引用 当传过来的是左值&#xff0c;那么T&&会折叠为T&。 引用折…

【2023年电赛国一必备】E题报告模板--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本文使用K210芯片设计了一个运动目标控制与自动追踪系统。系统包括使用深度学习进行识别激光位置&#xff0c;其中红色激…

Oracle表段中的高水位线HWM

在Oracle数据的存储中&#xff0c;可以把存储空间想象为一个水库&#xff0c;数据想象为水库中的水。水库中的水的位置有一条线叫做水位线&#xff0c;在Oracle中&#xff0c;这条线被称为高水位线&#xff08;High-warter mark, HWM&#xff09;。在数据库表刚建立的时候&…

Emacs之将.el编译成bin(一百二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Mac 卸载 IntelliJ IDEA 方法

Mac 系统下 IDEA 没有一键卸载程序&#xff0c;也没有完全卸载的插件&#xff0c;若要彻底删除&#xff0c;除了在应用&#xff08;Application&#xff09;里删除 IDEA 到垃圾桶外&#xff0c;还需要在终端&#xff08;Terminal&#xff09;执行删除相应的文件及文件夹。 1 分…

vscode如何退出/切换 github 账号

退出/切换 github 账号 左下角点击头像按钮&#xff0c;选择注销&#xff0c;然后再重新登录

if条件语句

if语句是一种在编程中常用的条件控制语句&#xff0c;用于根据条件来执行不同的代码块。下面是if语句的使用及用法&#xff1a; 基本语法&#xff1a; if 条件: 执行代码块 如果条件为真&#xff0c;则执行代码块&#xff0c;否则跳过。 可选的else语句&#xff1a; if 条件…

JS进阶-Day3

&#x1f954;&#xff1a;永远做自己的聚光灯 JS进阶-Day1——点击此处&#xff08;作用域、函数、解构赋值等&#xff09; JS进阶-Day2——点击此处&#xff08;深入对象之构造函数、实例成员、静态成员等&#xff1b;内置构造函数之引用类型、包装类型等&#xff09; 更多JS…

boost beast http server 测试

boost beast http client boost http server boost beast 是一个非常好用的库&#xff0c;带上boost的协程&#xff0c;好很多东西是比较好用的&#xff0c;以下程序使用四个线程开启协程处理进入http协议处理。协议支持http get 和 http post #include <boost/beast/cor…

Java与Kotline Funcation函数与参数函数的详解

一.介绍 在现在以IDE为开发工具的时代&#xff0c;各种开发语言都有&#xff0c;kotlin的语法势头比较强&#xff0c;今天我们将介绍在项目中出现比较多的两种函数&#xff0c;一种是参数函数&#xff0c;还有一种就是Function函数 如果你不了匿名函数请阅读以下文档&#xff…

ISC 2023︱诚邀您参与赛宁“安全验证评估”论坛

​​8月9日-10日&#xff0c;第十一届互联网安全大会&#xff08;简称ISC 2023&#xff09;将在北京国家会议中心举办。本次大会以“安全即服务&#xff0c;开启人工智能时代数字安全新范式”为主题&#xff0c;打造全球首场AI数字安全峰会&#xff0c;赋予安全即服务新时代内涵…

【华为OD机试】数字最低位排序【2023 B卷|100分】

【华为OD机试】-真题 !!点这里!! 【华为OD机试】真题考点分类 !!点这里 !! 题目描述 给定一个非空数组(列表) 起元素数据类型为整型 请按照数组元素十进制最低位从小到大进行排序 十进制最低位相同的元素,相对位置保持不变 当数组元素为负值时,十进制最低为等同于去除符号…

常见的设计模式(超详细)

文章目录 单例模式饿汉式单例模式懒汉式单例模式双重检索单例模式 工厂模式简单工厂模式工厂&#xff08;方法&#xff09;模式抽象工厂模式 原型模式代理模式 单例模式 确保一个类只有一个实例&#xff0c;并且自行实例化并向整个系统提供这个实例。 饿汉式单例模式 饿汉式单…