力扣-《剑指offer》-哈希表-刷题笔记
目录
第一题:03.数组中重复的数字
第二题:39.数组中出现次数超过一半的数字
第三题:50.第一个只出现一次的字符
第四题:53. (0-n-1)中缺失的数字
第一题:03.数组中重复的数字
我的答案:
法一:普通数组
class Solution {public int findRepeatNumber(int[] nums) {int len=nums.length;//数组长度int num=-1;//暂存重复值,用于返回int[] nums2=new int[len];//创建一个新数组,长度与旧数组一样for(int i=0;i<len;i++){//新数组的下标为旧数组的元素值,新数组的元素值即为对应的下标值在旧数组中出现的次数nums2[nums[i]]++;}for(int i=0;i<len;i++){//遍历新数组,如果出现元素值大于1,即为其下标值在旧数组中重复出现if(nums2[i]>1) {num=i;break;}}return num;}
}
法二:哈希表
class Solution {public int findRepeatNumber(int[] nums) {Map<Integer,Integer> count=new HashMap<>();//新建一个哈希表int num=-1;//用于存储重复出现的元素for(int i=0;i<nums.length;i++){//重点在于如何让哈希表键值对的值实现自增Integer n=count.get(nums[i]);//因为n可能为null,所以类型设为Integer,而不是intif(n==null){//如果n为null,证明这次的元素是第一次出现count.put(nums[i],1);}else{//如果n不为null,则由键取到它原来的旧值,在旧值上加1count.put(nums[i],n+1);}}for(Map.Entry<Integer,Integer> e:count.entrySet()){//遍历哈希表if(e.getValue()>1){num=e.getKey();break;//找到一个重复的元素后,直接结束循环,然后返回num}}return num;}
}
官方答案:
法一:哈希集合
class Solution {public int findRepeatNumber(int[] nums) {Set<Integer> set = new HashSet<Integer>();//新建一个哈希集合int repeat = -1;for (int num : nums) {//遍历数组if (!set.add(num)) {//Hashset不允许有重复项,所以如果num已经被加进过set里,那后面再遇到时,就加不进set里面了//此时set.add(num)返回false,取反!后就为true,返回此时的numrepeat = num;break;}}return repeat;}
}
第二题:39.数组中出现次数超过一半的数字
我的答案:
法一:排序函数sort()
class Solution {public int majorityElement(int[] nums) {Arrays.sort(nums);int len=nums.length;return nums[(int)Math.floor(len/2)];//Math.floor(len/2)向下取整,得到数组中间位置的值//但此时是double值,所以要强转为int值}
}
//len/2已经是取整了,不必再写前面的数学函数了
法二:哈希表
class Solution {public int majorityElement(int[] nums) {Map<Integer,Integer> count=new HashMap<>();int n=(int)Math.floor(nums.length/2);int num=0;for(int i=0;i<nums.length;i++){Integer m=count.get(nums[i]);if(m==null){count.put(nums[i],1);}else{count.put(nums[i],m+1);}if(count.get(nums[i])>n){num=nums[i];}}return num;}
}
官方答案:
法一:哈希表
class Solution {private Map<Integer, Integer> countNums(int[] nums) {Map<Integer, Integer> counts = new HashMap<Integer, Integer>();//新建一个哈希表for (int num : nums) {//遍历数组if (!counts.containsKey(num)) {//如果哈希表中不包含数组中的元素num,则把它添加进去//元素的值为键,出现的次数为值counts.put(num, 1);} else {//此时的num已经被加进过哈希表了,所以这里的值实现自增counts.put(num, counts.get(num) + 1);}}return counts;}public int majorityElement(int[] nums) {Map<Integer, Integer> counts = countNums(nums);//Map.Entry<Integer, Integer> majorityEntry = null;//用打擂台的方式去维护一个最大值for (Map.Entry<Integer, Integer> entry : counts.entrySet()) {//遍历哈希表//entry是对象名//Map.Entry是对象类型//<Integer, Integer>分别是键、值的数据类型//counts.entrySet()该方法返回的是键值对的集合if (majorityEntry == null || entry.getValue() > majorityEntry.getValue()) {//majorityEntry是用来记录值最大的键majorityEntry = entry;//变更最大值}}return majorityEntry.getKey();}
}
法二:排序
将数组
nums
中的所有元素按照单调递增或单调递减的顺序排序,那么下标为n/2的元素(下标从0
开始)一定是众数。
class Solution {public int majorityElement(int[] nums) {Arrays.sort(nums);return nums[nums.length / 2];}
}
法三:随机化
因为超过n/2的数组下标被众数占据了,这样我们随机挑选一个下标对应的元素并验证,有很大的概率能找到众数。
class Solution {private int randRange(Random rand, int min, int max) {//取随机数做下标return rand.nextInt(max - min) + min;}private int countOccurences(int[] nums, int num) {//验证该数是否是众数int count = 0;for (int i = 0; i < nums.length; i++) {if (nums[i] == num) {count++;}}return count;}public int majorityElement(int[] nums) {Random rand = new Random();int majorityCount = nums.length / 2;while (true) {int candidate = nums[randRange(rand, 0, nums.length)];if (countOccurences(nums, candidate) > majorityCount) {//验证return candidate;}}}
}
法四:分治
分治算法:
分:递归地求解更小的问题,直到再也分割不了
治:子问题的解决方案形成了原问题的解决方案
class Solution {private int countInRange(int[] nums, int num, int lo, int hi) {//统计元素num在数组nums下标为[lo,hi]范围内出现的次数int count = 0;for (int i = lo; i <= hi; i++) {if (nums[i] == num) {count++;}}return count;}private int majorityElementRec(int[] nums, int lo, int hi) {// 长度为 1 的子数组中唯一的数显然是该子数组的众数,直接返回即可if (lo == hi) {return nums[lo];}// 递归对半分割数组,直到所有的子问题都是长度为 1 的数组int mid = (hi - lo) / 2 + lo;int left = majorityElementRec(nums, lo, mid);int right = majorityElementRec(nums, mid + 1, hi);// if (left == right) {return left;}// 否则,调用方法,计算子数组的众数int leftCount = countInRange(nums, left, lo, hi);int rightCount = countInRange(nums, right, lo, hi);return leftCount > rightCount ? left : right;//从两个子数组的两个众数中选出它们合并后的数组的众数}public int majorityElement(int[] nums) {return majorityElementRec(nums, 0, nums.length - 1);}
}
//【lo,hi】是子数组的下标范围
//left是左子数组的众数,right是右子数组的众数
//leftCount是左子数组的众数出现的次数,用于与右的比较,在这两个众数中选出它们子数组合并后的众数
法五:Boyer-Moore 投票算法(摩尔投票法)
class Solution {public int majorityElement(int[] nums) {int count = 0;//众数出现的次数Integer candidate = null;//候选众数for (int num : nums) {//遍历数组if (count == 0) {candidate = num;}count += (num == candidate) ? 1 : -1;//如果num和candidate相等,则计数器count的值加1//如果num和candidate不等,则计数器count的值减1}return candidate;}
}
第三题:50.第一个只出现一次的字符
我的答案:哈希表
class Solution {public char firstUniqChar(String s) {char[] chars=s.toCharArray();//把字符串转成字符数组Map<Character,Integer> hashmap=new HashMap<>();//新建一个哈希表,用于统计字符出现的次数 for(int i=0;i<s.length();i++){//遍历字符数组if(!hashmap.containsKey(chars[i])){//如果哈希表中不包含字符c,就把它添进去,次数为1次hashmap.put(chars[i],1);}else{//字符c已经出现过了,那在它原来出现的次数上加1hashmap.put(chars[i],hashmap.get(chars[i])+1);}}for(int i=0;i<s.length();i++){//遍历哈希表,找到值为1的元素,返回它的键if(hashmap.get(chars[i])==1){return chars[i];}}return ' ';}
}
官方答案:
法一:哈希表
class Solution {public char firstUniqChar(String s) {Map<Character, Integer> frequency = new HashMap<Character, Integer>();for (int i = 0; i < s.length(); ++i) {char ch = s.charAt(i);frequency.put(ch, frequency.getOrDefault(ch, 0) + 1);}for (int i = 0; i < s.length(); ++i) {//遍历哈希表,找到值为1的键,然后返回if (frequency.get(s.charAt(i)) == 1) {return s.charAt(i);}}return ' ';}
}
法二:使用哈希表存储索引
class Solution {public char firstUniqChar(String s) {Map<Character, Integer> position = new HashMap<Character, Integer>();//新建一个哈希表int n = s.length();//字符串长度for (int i = 0; i < n; ++i) {char ch = s.charAt(i);if (position.containsKey(ch)) {position.put(ch, -1);//重复出现的字符,键值对的值都赋为-1} else {position.put(ch, i);//只出现一次的字符,键值对的值大小与字符出现的先后顺序一致,存的是字符的索引//值越小,出现得越早}}int first = n;//用打擂台的方式去维护一个最小值for (Map.Entry<Character, Integer> entry : position.entrySet()) {int pos = entry.getValue();if (pos != -1 && pos < first) {//找到哈希表里不为-1的最小值,即为第一个不重复字符的索引first = pos;}}return first == n ? ' ' : s.charAt(first);//没有找到索引最小值,first的值不变,就返回空格}
}
法三:队列
class Solution {public char firstUniqChar(String s) {Map<Character, Integer> position = new HashMap<Character, Integer>();Queue<Pair> queue = new LinkedList<Pair>();//新建一个队列,存储的元素类型是Pairint n = s.length();for (int i = 0; i < n; ++i) {char ch = s.charAt(i);if (!position.containsKey(ch)) {//哈希表不包含字符ch,则按照出现顺序存储每一个字符及它们第一次出现的位置,哈希表和队列都各存一份position.put(ch, i);queue.offer(new Pair(ch, i));//队列是先进先出} else {position.put(ch, -1);//字符重复出现,把它在哈希表中的值修改为-1while (!queue.isEmpty() && position.get(queue.peek().ch) == -1) {//如果队列的队首在哈希表中存储的值为-1且队列不为空,证明这个元素是重复出现的元素//queue.peek()取出一个元素对象//queue.peek().ch取出该对象的ch属性queue.poll();//就把这个元素弹出队列,丢掉}}}return queue.isEmpty() ? ' ' : queue.poll().ch;//把重复出现的元素都弹出队列,如果最后队列不为空,那队列的队首就是所求}class Pair {//定义了Pair类,属性有ch,pos,分别存放字符和字符索引的char ch;int pos;Pair(char ch, int pos) {this.ch = ch;this.pos = pos;}}
}
第四题:53. (0-n-1)中缺失的数字
我的答案:直接遍历
class Solution {public int missingNumber(int[] nums) {for(int i=0;i<nums.length;i++){if(nums[i]!=i){//数组的元素值恰好等于它的索引值,如果不等,则该位置上的元素是错的return nums[i]-1;}}return nums.length;//缺失的元素在数组的末尾,比如【0】缺失的元素是1,恰好等于数组的长度}
}
官方答案:
法一:哈希集合
class Solution {public int missingNumber(int[] nums) {Set<Integer> set = new HashSet<Integer>();int n = nums.length + 1;for (int i = 0; i < n - 1; i++) {//把数组内的元素全部添加进哈希表set.add(nums[i]);}int missing = -1;//用于存储缺失的数字for (int i = 0; i <= n - 1; i++) {//检查【0,n-1】的每个整数是否在哈希集合中if (!set.contains(i)) {missing = i;break;//找到缺失的元素就退出循环}}return missing;}
}
法二:直接遍历
class Solution {public int missingNumber(int[] nums) {int n = nums.length + 1;//数组长度加上缺失的数字,一共应该是n个,for (int i = 0; i < n - 1; i++) {if (nums[i] != i) {//数组的元素值恰好等于它的索引值,如果不等,则该位置上的元素是错的return i;}}return n - 1;//这属于缺失的元素位于数组末尾的情况}
}
//+1是算上了缺失的数字
法三:位运算
在数组nums的n-1个数后面再添加0-n-1的每个整数,至此共有2n-1个整数,缺失的数字出现了一次,其余数字都出现了两次。对这2n-1个整数进行按位异或运算,结果即为缺失的数字。
class Solution {public int missingNumber(int[] nums) {int xor = 0;//用于存储缺失的数字int n = nums.length + 1;for (int i = 0; i < n - 1; i++) {//对缺失了数字的数组的n-1个数做异或运算xor ^= nums[i];}for (int i = 0; i <= n - 1; i++) {//接着上面,对完整的n个整数做异或运算xor ^= i;}return xor;//二进制数做异或运算,两个相同的数会相互抵消,变成0//最后剩下的是就是只出现了一次的缺失了的数字}
}
法四:高斯求和公式
高斯求和的故事指的是年仅10岁的高斯解出来数学教师布特纳出一道难题,布纳特要求学生将1到100以内的所有整数进行相加,本来布纳特是为了为难学生的,没想到高斯很快算出了答案为5050。高斯用来快速解出答案的办法就被后人称为高斯求和。
class Solution {public int missingNumber(int[] nums) {int n = nums.length + 1;int total = n * (n - 1) / 2;//0~n-1个数的总和,完整的数组int arrSum = 0;for (int i = 0; i < n - 1; i++) {//缺失了数字的数组元素值总和arrSum += nums[i];}return total - arrSum;//差值即为缺失的数字}
}
网友答案:二分查找
class Solution {public int missingNumber(int[] nums) {int lo = 0, hi = nums.length - 1;while(lo <= hi) {int mid = lo + (hi - lo >> 1);//对半分数组if(mid == nums[mid]) {lo = mid + 1;} else {hi = mid - 1;}}return lo;}
}
相关文章:

力扣-《剑指offer》-哈希表-刷题笔记
目录 第一题:03.数组中重复的数字 第二题:39.数组中出现次数超过一半的数字 第三题:50.第一个只出现一次的字符 第四题:53. (0-n-1)中缺失的数字 第一题:03.数组中重复的数字 我的答案&…...

【SpringBoot】| 邮箱发送验证码,你会了吗?
目录🦁 题外话🦁 提前准备2.1 配置邮箱第三方登录2.1.1 点击设置——账户2.1.2 开启POP3/SMTP服务2.2 添加依赖2.3 yaml配置🦁 进入主题🦁 测试使用🦁 尾声3.1 安利一个生成验证码的工具类3.1.1 添加依赖3.1.2 编写配置…...

Linux系统安装部署及配置Grafana
TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…...

Python3 入门教程||Python3 输入和输出||Python3 File 方法
Python3 输入和输出 在前面几个章节中,我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python 两种输出值的方式: 表达式语句和 print() 函数。(第三种方式是使用文件对象的 write() 方法; 标准输出文件可以…...

有效的字母异位词(力扣刷题)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。 …...

73、介绍下 HashMap 的底层数据结构
73、介绍下 HashMap 的底层数据结构 我们现在用的都是 JDK 1.8,底层是由“数组链表红黑树”组成,如下图,而在 JDK 1.8 之前是由“数组链表”组成。 1.Hash Hash叫做”散列表“,就是把任意长度的输入,通过散列算法&am…...

系统集成路由器OSPF动态、综合路由配置
实验任务:动态路由协议RIP、OSPF协议的内容和特点动态路由RIP、OSPF实验,建立拓扑pc1>>R1>>R2>>R3>>pc2,使pc1与pc2能相互通信,并配置PC端静默接口。熟悉配置vlan间路由技术:多层交换机虚拟接…...

【力扣周赛 338】
6354. K 件物品的最大和 - 力扣(Leetcode)袋子中装有一些物品,每个物品上都标记着数字 1、0或 -1。给你四个非负整数 numOnes、numZeros、numNegOnes和 k。袋子最初包含:numOnes 件标记为 1 的物品。numZeroes 件标记为 0 的物品。…...

大数据Flink进阶(八):Apache Flink架构介绍
Apache Flink架构介绍 一、Flink组件栈 在Flink的整个软件架构体系中,同样遵循这分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。...

Mars3d项目启动上的一些坑
前言 最近新入职了一家公司,公司新开了有个未来城市的项目,需要用到3D城市建模,公司老总选了Mars3d作为前端框架,项目分给我了,又是一个全新的领域,开搞吧! 下面是自己遇到的几个小问题&#x…...

通俗易懂【Springboot】 单文件下载和批量下载(多个文件合成一个压缩包下载)
文章目录一.单文件下载1.简单理解文件下载2.单文件下载的具体代码实现3.测试4.单文件下载整体代码二.多文件批量下载(多个文件合成一个压缩包下载)1.多文件下载的实现方式,这里使用了ZipOutputStream2.具体代码实现3.测试4.文件批量下载&…...

CnOpenData中国行政区划shp数据
一、数据简介 中国行政区划数据是重要的基础地理信息数据,目前不同来源的全国行政区划数据非常多,但能够开放获取的高质量行政区域数据少之又少。基于此,锐多宝的地理空间制作一套2013-2023年可开放获取的高质量行政区划数据。该套数据以2022…...

GPT-4零失误通关大厂模拟面试,offer拿到手软?与AGI首次接触
来源: FoxyearMeta “GPT-4可被视作AGI (通用人工智能)的早期版本。” 如若从他人口中说出,或许是无稽之谈—— 但是由微软雷蒙德研究院机器学习理论组负责人万引大神Sbastien Bubeck与2023新视野数学奖得主Ronen Eldan、2023新晋斯隆研究奖得…...

Hardhat 环境搭建及教程示例
一.安装node.js curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash nvm install 18 nvm use 18 nvm alias default 18 npm install npm --global # Upgrade npm to the latest version 二. 安装hardhat 2.1 创建hardhat安装目录 mkdir hard…...

复杂链表的复制-剑指Offer35-java
一、题目描述 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。 示例 1: 输入:head [[7,null],[13,…...

【Linux】进程理解与学习Ⅰ-进程概念
环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹相关文章推荐:【Linux】冯.诺依曼体系结构与操作系统进程概念什么是进程?进程是什么?我们打开任务管理器可以看到有…...

WebKitX ActiveX 6.0 X86 Crack
WebKitX ActiveX将 Chromium Embedded Framework (CEF3) 包装到一个进程外的 ActiveX 组件中,以便与 OLE/COM 语言一起使用。Chromium Embedded Framework 封装了 WebKit Blink HTML5 Renderer 和 Google V8 JavaScript Engine。这是一个用于商业用途的生产级稳定组…...

开源项目:数据库表结构生成文档工具
目录 一、软件介绍 二、技术框架 三、功能介绍 四、代码展示 1、获取数据库信息部分代码 2、导出Html文档代码 五、运行效果 六、项目开源地址 一、软件介绍 今天给大家分享我自己编写的数据库表结构文档生成工具,方便大家在实际开发当中,可以很方便导出…...

spring的两种拦截器HandlerIntercepter和MethodIntercepter
介绍 Spring有两种拦截器提供给我们使用,一种是HandlerIntercepter,另一种是MethodIntercepter。这两种的来源不同,实现方式也不同,具体的下面来看一下。 HandlerIntercepter 来源 来源于spring-webmvc包 HandlerIntercepter拦…...

初级算法-字符串
主要记录算法和数据结构学习笔记,新的一年更上一层楼! 初级算法-字符串一、反转字符串二、反转字符串(二)三、替换空格四、翻转字符串里的单词五、左旋转字符串六、实现 strStr()七、重复的子字符串字符串中元素只能是字符String…...

华为OD机试题 - 寻找目标字符串(JavaScript)| 机考必刷
更多题库,搜索引擎搜 梦想橡皮擦华为OD 👑👑👑 更多华为OD题库,搜 梦想橡皮擦 华为OD 👑👑👑 更多华为机考题库,搜 梦想橡皮擦华为OD 👑👑👑 最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为…...

删除Terminating状态的namespace:cattle-system
这里以cattle-system为例!执行删除命令后namespace(也是用其他k8s object)仍然存在,首先执行 kubectl edit namespace cattle-system 查看是否存在spec.finalizers: kubernetes,如: spec: finalizers:…...

MiniOB 并发B+树实现解析
MiniOB 是 OceanBase 联合华中科技大学推出的一款用于教学的小型数据库系统,希望能够帮助数据库爱好者系统性的学习数据库原理与实战。 B 树介绍 B 树是传统数据库中常见的索引数据结构,比如MySQL、PostgreSQL都实现了B树索引。B 树是一个平衡多叉树&am…...

SpringCloud负载均衡服务调用——Ribbon
Ribbon 本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 简介 Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。 简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算…...

各种邮箱服务软件对比
1.宝塔邮局管理器 特点:简单易用,可视化操作,小白也能搞,还有备份功能,一般足够用了 缺点:稳定性真是差,隔三差五的不能收发.没有接口,不能任意修改邮箱密码,只能管理员修改 注意要点:一定要开启ssl,否则有些邮箱给你发邮件你收不到 建议:不要入坑.坏了之后没法修复,哭都没地方…...

相机单独标定的实现过程[autoware标定]、tmp文件的查看方式
安装了autoware1.13和calibration标定包,发现实现相机单独标定的过程较为坎坷,参考了一些博主的方法,发现下面的过程更加适合自己,做个笔记。 1安装标定箱(与calibration标定包的安装并不冲突) 标定工具箱…...

4.10.1、IP 多播技术的相关基本概念
多播(Multicast,也称为组播)是一种实现 “一对多” 通信的技术,与传统单播“一对一”通信相比,多播可以极大地节省网络资源。 在因特网上进行的多播,称为 IP 多播。 1、单播 & 多播 如下所示…...

PIGOSS BSM监控国产数据库Oscar
前言神通数据库(原OSCAR数据库)是天津神舟通用数据技术有限公司(简称“神舟通用公司”)拥有自主知识产权的企业级、大型通用关系型数据库管理系统。PIGOSS BSM作为网利友联科技完全自主研发的纯国产基础 IT 架构运行状态监测平台软件…...

Spring Boot中文件上传
Spring Boot中文件上传 前言 本篇主要参考Spring官方文档,整理了Spring Boot中文件上传如何实现,以及在代码中使用RestTemplate和HttpClient两种方式实现文件上传。 创建Spring Boot项目 首先创建一个Spring Boot Web项目,使用的Spring B…...

Github上传大文件(>25MB)教程
Github上传大文件(>25MB)教程Github上传大文件(>25MB)教程安装git安装Git Large File Storage实例踩坑点1:failed to push some refs to踩坑点2:main与master踩坑点3:Failed to connect t…...