代码随想录算法训练营第七天| 454. 四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,哈希表总结

本文主要是介绍代码随想录算法训练营第七天| 454. 四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,哈希表总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

454.四数相加II

建议:本题是使用Map巧妙解决的问题,好好体会一下哈希法如何提高程序执行效率,降低时间复杂度,当然使用哈希法会提高空间复杂度,但一般来说我们都是舍空间换时间, 工业开发也是这样。

题目链接/文章讲解/视频讲解:代码随想录

重点:

1. 因为要存储前两个数组的不同下标和的键值对 sum: count,后两个数组需要查找这个键值对,所以我们用Map的哈希表

思路:

1. 先把前两个数组的不同下标的和用Map记录下来,然后用0 - 后两个数组的和来对Map进行查找,找到了就加上Map的value值

public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {// 计算有多少组下标对应的数字的和为0int result = 0;// 存放前两个数组的 sum: countMap<Integer, Integer> records = new HashMap<>();for (int i = 0; i < nums1.length; i++) {for (int j = 0; j < nums2.length; j++) {int sum = nums1[i] + nums2[j];records.put(sum, records.getOrDefault(sum, 0) + 1);}}// 用后两个数组的和的负数来在records中查找是否有匹配的// 找到了result += countfor (int i = 0; i < nums3.length; i++) {for (int j = 0; j < nums4.length; j++) {int sum = nums3[i] + nums4[j];if (records.containsKey(-sum)) {result += records.get(-sum);}}}return result;
}

383. 赎金信

建议:本题和242. 有效的字母异位词是一个思路 ,算是拓展题
题目链接/文章讲解: 代码随想录

重点:

1. 因为只有26个字母,所以使用数组来当哈希表结构

思路;

1. 把magazine中出现的每个字符的次数存放到数组中

2. 把ransomNote中出现的每个字符在对应的index上 --

3. 期间如果有某个index的值小于0,说明magazine的字符无法组成ransomNote

public static boolean canConstruct(String ransomNote, String magazine) {int[] magazineCount = new int[26];// 把magazine的字符出现的次数存放进数组for (int i = 0; i < magazine.length(); i++) {magazineCount[magazine.charAt(i) - 'a']++;}// 把ransomNote中出现的字符的对应的数组的值减1// 期间如果有某个值小于0,说明magazine中的字符无法满足ransomNote的组成for (int i = 0; i < ransomNote.length(); i++) {int temp = --magazineCount[ransomNote.charAt(i) - 'a'];if (temp < 0) {return false;}}return true;
}

15. 三数之和

建议:本题虽然和两数之和很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下双指针法的思路,文章中讲解的,哈希法很麻烦。
题目链接/文章讲解/视频讲解: 代码随想录

重点:

1. 双指针法

思路:

1. 双指针法

(1) 先把数组从小到大排序

(2) 从头开始遍历排序好的数组,如果i = i - 1,直接continue对 i 进行去重,并定义一个left = i + 1, right = nums.length - 1

(3) 不断的移动left和right,当i + left + right小于0时说明sum太小了,需要变大,因为是排序好的数组,直接把left++就可以把left移动到更大的数去了

(4) 不断的移动left和right,当i + left + right大于0时说明sum太大了,需要变小,因为是排序好的数组,直接把right--就可以把right移动到更小的数去了

(5) 当i + left + right等于0时,把这三个值存放到result中,接下来要对left和right分别去重。如果left = left + 1, left++。如果right = right - 1,right--

public static List<List<Integer>> threeSum(int[] nums) {// 先把数组从小到大排序Arrays.sort(nums);List<List<Integer>> result = new ArrayList<>();for (int i = 0; i < nums.length; i++) {// 如果i所指的数都已经大于0了,后面的数又都比它大,直接returnif (nums[i] > 0) {return result;}// 如果i所指的数和它前面的数相等,说明这个数已经被判断过了,continueif (i >= 1 && nums[i] == nums[i - 1]) {continue;}// 两个指针,一个指i + 1,一个指数组末尾int left = i + 1;int right = nums.length - 1;while (left < right) {int sum = nums[i] + nums[left] + nums[right];if (sum > 0) {// 当三数之和大于0时,证明需要减小,把right往前移一个right--;} else if (sum < 0) {// 当三数之和小于0时,证明需要变大,把left往后移一个left++;} else {// 当三数之和等于0时,需要对left和right所指的数分别去重result.add(Arrays.asList(nums[i], nums[left], nums[right]));while (left < right && nums[left + 1] == nums[left]) {left++;}while (left < right && nums[right - 1] == nums[right]) {right--;}// 为什么同时移动两个指针?// 因为如果只移动了right,你前面两数i和left都已经固定了,// 为了三数和为0,你right也相当于是固定的,// 也就是说i和left都已经判断过了,right也相当于判断过了。// 所以当上面的去重操作完成之后,left和right肯定同时移动的left++;right--;}}}return result;
}

18. 四数之和

建议: 要比较一下,本题和454. 四数相加II 的区别,为什么454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题思路整体和三数之和一样的,都是双指针,但写的时候有很多小细节,需要注意,建议先看视频。 

题目链接/文章讲解/视频讲解:代码随想录

重点:

1. 双指针法

思路:

1. 双指针法

(1) 先把数组从小到大排序

(2) 两个for循环,第一个for循环 i 从头开始,第二个for循环从上一个循环 i + 1 开始

(3) 不断的移动left和right,当i + j + left + right小于0时说明sum太小了,需要变大,因为是排序好的数组,直接把left++就可以把left移动到更大的数去了

(4) 不断的移动left和right,当i + j + left + right大于0时说明sum太大了,需要变小,因为是排序好的数组,直接把right--就可以把right移动到更小的数去了

(5) 当i + j + left + right等于0时,把这四个值存放到result中,接下来要对left和right分别去重。如果left = left + 1, left++。如果right = right - 1,right--

(6) 把 j 移动到下一个,接着判断

public static List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> result = new ArrayList<>();// 先从小到大排序数组Arrays.sort(nums);for (int i = 0; i < nums.length - 1; i++) {// 如果nums[i]已经大于target了,并且nums[i]已经大于0// nums[i]已经大于0说明后面的数肯定也都大于0,直接返回if (nums[i] > target && nums[i] > 0) {return result;}// 如果当前nums[i]和前一个数一样,跳过if (i >= 1 && nums[i] == nums[i - 1]) {continue;}for (int j = i + 1; j < nums.length; j++) {// 如果i和j已经相差两个,并且nums[j]和前一个数一样,跳过if (j > i + 1 && nums[j] == nums[j - 1]) {continue;}// 两个指针,一个指j + 1,一个指数组末尾int left = j + 1;int right = nums.length - 1;while (left < right) {long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];if (sum > target) {// 当四数之和大于target时,证明需要减小,把right往前移一个right--;} else if (sum < target) {// 当四数之和小于target时,证明需要变大,把left往后移一个left++;} else {// 当四数之和等于target时,需要对left和right所指的数分别去重result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));while (left < right && nums[left + 1] == nums[left]) {left++;}while (left < right && nums[right - 1] == nums[right]) {right--;}left++;right--;}}}}return result;
}

哈希表总结

哈希表理论知识

一般来说哈希表都是用来快速判断一个元素是否出现集合里

对于哈希表,要知道哈希函数哈希碰撞在哈希表中的作用:

  • 哈希函数是把传入的key映射到符号表的索引上。
  • 哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。

接下来是常见的三种哈希结构:

  • 数组
  • set(集合)
  • map(映射)

哈希经典题目

数组作为哈希表

一些应用场景就是为数组量身定做的。

在242.有效的字母异位词 (opens new window)中,我们提到了数组就是简单的哈希表,但是数组的大小是受限的!这道题目包含小写字母,那么使用数组来做哈希最合适不过。

在383.赎金信 (opens new window)中同样要求只有小写字母,那么就给我们浓浓的暗示,用数组!

一些同学可能想,用数组干啥,都用map不就完事了。

上面两道题目用map确实可以,但使用map的空间消耗要比数组大一些,因为map要维护红黑树或者符号表,而且还要做哈希函数的运算。所以数组更加简单直接有效!

set作为哈希表

在349. 两个数组的交集 (opens new window)中我们给出了什么时候用数组就不行了,需要用set。这道题目没有限制数值的大小,就无法使用数组来做哈希表了。

主要因为如下两点:

  • 数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
  • 如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

所以此时一样的做映射的话,就可以使用set了。

map作为哈希表

在1.两数之和 (opens new window)中map正式登场。

来说一说,使用数组和set来做哈希法的局限:

  • 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  • set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x和y的下标。所以set也不能用。

map是一种<key, value>的结构,本题可以用key保存数值,用value在保存数值所在的下标。所以使用map最为合适。

在454.四数相加 (opens new window)中我们提到了其实需要哈希的地方都能找到map的身影。

本题咋眼一看好像和18. 四数之和 (opens new window),15.三数之和 (opens new window)差不多,其实差很多!

关键差别是本题为四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑重复问题,而18. 四数之和 (opens new window),15.三数之和 (opens new window)是一个数组(集合)里找到和为0的组合,可就难很多了!

所以18. 四数之和,15.三数之和都推荐使用双指针法!

总结

对于哈希表的知识相信很多同学都知道,但是没有成体系。

本篇我们从哈希表的理论基础到数组、set和map的经典应用,把哈希表的整个全貌完整的呈现给大家。

同时也强调虽然map是万能的,详细介绍了什么时候用数组,什么时候用set

相信通过这个总结篇,大家可以对哈希表有一个全面的了解。

这篇关于代码随想录算法训练营第七天| 454. 四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,哈希表总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/704280

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

哈希leetcode-1

目录 1前言 2.例题  2.1两数之和 2.2判断是否互为字符重排 2.3存在重复元素1 2.4存在重复元素2 2.5字母异位词分组 1前言 哈希表主要是适合于快速查找某个元素(O(1)) 当我们要频繁的查找某个元素,第一哈希表O(1),第二,二分O(log n) 一般可以分为语言自带的容器哈希和用数组模拟的简易哈希。 最简单的比如数组模拟字符存储,只要开26个c

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig