)
LeetCode 760 Find Anagram Mappings 全解暴力、哈希表与位运算三种解法含多语言实现【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode导读本篇基于 find-anagram-mappings.md 深入讲解 LeetCode 760「寻找变位词映射」给定两个互为 anagram 的数组nums1与nums2为nums1中每个元素找到其在nums2中的下标。文章完整覆盖三种解法——暴力双重循环O(N²)、哈希表预处理O(N)以及位运算 排序O(N log N)并提供 Python、Java、C、JavaScript、Go、Kotlin、Swift、Rust 共 8 种语言的完整实现。读完你将掌握「值 → 下标」映射建模、位打包编码技巧及其适用边界并能据此迁移解决同类数组映射类问题。前置知识在尝试该题之前建议先熟悉以下基础哈希表Hash Maps用于存储「值 → 下标」映射实现 O(1) 查询是本题最优解的核心数据结构数组遍历Array Traversal在两个数组之间构建映射关系的基本迭代能力位运算Bit Manipulation可选进阶解法利用位左移把下标直接编码进元素值中从而省去哈希表的额外空间。本题与仓库中另外两篇 anagram 主题文章同属「哈希 数组」体系变位词分组见 anagram-groups.md用排序串或字符计数做 key判断两串是否为变位词见 is-anagram.md。三题的核心建模思路一脉相承可对照学习。问题本质把「值」翻译成「下标」题目要求对nums1的每个元素找出它在nums2中的任意一个出现位置输出与nums1等长的下标数组。由于两个数组互为 anagram元素多重集相同nums1中的每个值必然能在nums2中找到因此无需处理「查不到」的情况——这一点是三种解法的共同前提。从仓库源码结构看本仓库为每个 LeetCode 题目在 python、java、cpp、javascript、go、kotlin、swift、rust 等目录下各维护一份独立解法文件如 python/0001-two-sum.py 对应 Two Sum而 articles 目录则存放配套讲解。下面按复杂度从低到高给出三种解法。1. 暴力解法双重循环逐个匹配思路Intuition问题要求的是对于nums1中的每个元素在nums2中找到该值出现的下标。最朴素的做法是对nums1的每个元素遍历nums2的全部位置一旦找到相等元素就记录下标并停止。由于穷举了所有可能性正确性有保证。算法步骤创建与nums1等长的结果数组mappings对nums1的每个下标i用下标j遍历nums2当nums1[i] nums2[j]时将j存入mappings[i]并break返回mappings。多语言实现class Solution: def anagramMappings(self, nums1: List[int], nums2: List[int]) - List[int]: # List to store the anagram mappings. mappings [0] * len(nums1) for i in range(len(nums1)): for j in range(len(nums2)): # Store the corresponding index of number in the second list. if nums1[i] nums2[j]: mappings[i] j break return mappingsclass Solution { public int[] anagramMappings(int[] nums1, int[] nums2) { // List to store the anagram mappings. int[] mappings new int[nums1.length]; for (int i 0; i nums1.length; i) { for (int j 0; j nums2.length; j) { // Store the corresponding index of number in the second list. if (nums1[i] nums2[j]) { mappings[i] j; break; } } } return mappings; } }class Solution { public: vectorint anagramMappings(vectorint nums1, vectorint nums2) { // List to store the anagram mappings. vectorint mappings; for (int num : nums1) { for (int i 0; i nums2.size(); i) { // Store the corresponding index of number in the second list. if (num nums2[i]) { mappings.push_back(i); break; } } } return mappings; } };class Solution { /** * param {number[]} nums1 * param {number[]} nums2 * return {number[]} */ anagramMappings(nums1, nums2) { // Array to store the anagram mappings. const mappings new Array(nums1.length); for (let i 0; i nums1.length; i) { for (let j 0; j nums2.length; j) { // Store the corresponding index of number in the second array. if (nums1[i] nums2[j]) { mappings[i] j; break; } } } return mappings; } }func anagramMappings(nums1 []int, nums2 []int) []int { // Slice to store the anagram mappings. mappings : make([]int, len(nums1)) for i : 0; i len(nums1); i { for j : 0; j len(nums2); j { // Store the corresponding index of number in the second slice. if nums1[i] nums2[j] { mappings[i] j break } } } return mappings }class Solution { fun anagramMappings(nums1: IntArray, nums2: IntArray): IntArray { // Array to store the anagram mappings. val mappings IntArray(nums1.size) for (i in nums1.indices) { for (j in nums2.indices) { // Store the corresponding index of number in the second array. if (nums1[i] nums2[j]) { mappings[i] j break } } } return mappings } }class Solution { func anagramMappings(_ nums1: [Int], _ nums2: [Int]) - [Int] { // Array to store the anagram mappings. var mappings Int for i in 0..nums1.count { for j in 0..nums2.count { // Store the corresponding index of number in the second array. if nums1[i] nums2[j] { mappings[i] j break } } } return mappings } }impl Solution { pub fn anagram_mappings(nums1: Veci32, nums2: Veci32) - Veci32 { let mut mappings vec![0i32; nums1.len()]; for i in 0..nums1.len() { for j in 0..nums2.len() { if nums1[i] nums2[j] { mappings[i] j as i32; break; } } } mappings } }复杂度分析时间复杂度O(N²)—— 最坏情况下nums1每个元素都要扫描完整的nums2空间复杂度O(1)—— 除结果数组外只使用常数级额外空间。其中 N 为数组nums1和nums2的元素个数。暴力法的性能瓶颈在于「重复扫描」nums2被反复遍历 N 次。下一节用哈希表把查询降到常数时间。2. 哈希表解法一次预处理O(1) 查询思路Intuition与其反复扫描nums2不如先把它预处理成一张「值 → 下标」的哈希表此后对nums1的任意元素都能在常数时间内取到对应下标。由于两个数组互为 anagramnums1中的每个元素都保证存在于nums2中查找不会落空。算法步骤构建哈希表valueToPos遍历nums2以值为 key、下标为 value创建结果数组mappings遍历nums1的每个元素从哈希表中查出其下标存入mappings返回mappings。多语言实现class Solution: def anagramMappings(self, nums1: List[int], nums2: List[int]) - List[int]: # Store the index corresponding to the value in the second list. valueToPos {} for i in range(len(nums2)): valueToPos[nums2[i]] i # List to store the anagram mappings. mappings [0] * len(nums1) for i in range(len(nums1)): mappings[i] valueToPos[nums1[i]] return mappingsclass Solution { public int[] anagramMappings(int[] nums1, int[] nums2) { // Store the index corresponding to the value in the second list. HashMapInteger,Integer valueToPos new HashMap(); for (int i 0; i nums2.length; i) { valueToPos.put(nums2[i], i); } // List to store the anagram mappings. int[] mappings new int[nums1.length]; for (int i 0; i nums1.length; i) { mappings[i] valueToPos.get(nums1[i]); } return mappings; } }class Solution { public: vectorint anagramMappings(vectorint nums1, vectorint nums2) { // Store the index corresponding to the value in the second list. unordered_mapint, int valueToPos; for (int i 0; i nums2.size(); i) { valueToPos[nums2[i]] i; } // List to store the anagram mappings. vectorint mappings; for (int num : nums1) { mappings.push_back(valueToPos[num]); } return mappings; } };class Solution { /** * param {number[]} nums1 * param {number[]} nums2 * return {number[]} */ anagramMappings(nums1, nums2) { // Store the index corresponding to the value in the second list. const valueToPos new Map(); for (let i 0; i nums2.length; i) { valueToPos.set(nums2[i], i); } // List to store the anagram mappings. const mappings new Array(nums1.length); for (let i 0; i nums1.length; i) { mappings[i] valueToPos.get(nums1[i]); } return mappings; } }func anagramMappings(nums1 []int, nums2 []int) []int { // Store the index corresponding to the value in the second slice. valueToPos : make(map[int]int) for i : 0; i len(nums2); i { valueToPos[nums2[i]] i } // Slice to store the anagram mappings. mappings : make([]int, len(nums1)) for i : 0; i len(nums1); i { mappings[i] valueToPos[nums1[i]] } return mappings }class Solution { fun anagramMappings(nums1: IntArray, nums2: IntArray): IntArray { // Store the index corresponding to the value in the second array. val valueToPos HashMapInt, Int() for (i in nums2.indices) { valueToPos[nums2[i]] i } // Array to store the anagram mappings. val mappings IntArray(nums1.size) for (i in nums1.indices) { mappings[i] valueToPos[nums1[i]]!! } return mappings } }class Solution { func anagramMappings(_ nums1: [Int], _ nums2: [Int]) - [Int] { // Store the index corresponding to the value in the second array. var valueToPos [Int: Int]() for i in 0..nums2.count { valueToPos[nums2[i]] i } // Array to store the anagram mappings. var mappings Int for i in 0..nums1.count { mappings[i] valueToPos[nums1[i]]! } return mappings } }impl Solution { pub fn anagram_mappings(nums1: Veci32, nums2: Veci32) - Veci32 { let mut value_to_pos HashMap::new(); for (i, num) in nums2.iter().enumerate() { value_to_pos.insert(num, i as i32); } let mut mappings vec![0i32; nums1.len()]; for (i, num) in nums1.iter().enumerate() { mappings[i] value_to_pos[num]; } mappings } }复杂度分析时间复杂度O(N)—— 构建哈希表一次 O(N)查询 N 次每次 O(1)空间复杂度O(N)—— 哈希表存储 N 个「值 → 下标」键值对。其中 N 为数组nums1和nums2的元素个数。这是面试中最推荐给出的解法思路直观与 two-integer-sum.md 中「值 → 下标」哈希表的建模方式同源且达到线性复杂度下界。唯一代价是需要 O(N) 的额外空间下一节介绍如何用位运算把这部分空间也省掉。3. 位运算 排序解法把下标编码进元素里思路Intuition哈希表解法的额外空间来自那张valueToPos表。能否把「原始下标」直接保存在元素自身、从而省去哈希表可以——利用位运算把每个值左移若干位再叠加自己的下标这样一个整数里同时保留了「原值」和「原下标」两份信息。排序两个数组后原值相同的元素必然对齐到相同位置此时只需用掩码提取下标并配对即可。算法步骤对每个下标i编码两个数组nums[i] (nums[i] 7) i。左移位数此处取 7 位必须足够容纳最大下标分别排序nums1与nums2原值相等的元素此时出现在相同位置创建结果数组mappings对每个位置i用掩码提取原始下标mappings[nums1[i] mask] nums2[i] mask返回mappings。为什么左移 7 位numToGetLastBits (1 7) - 1 127即低 7 位全部为 1 的掩码。左移 7 位后低 7 位被腾空用于存放下标因此下标取值范围为 0127即最多支持长度 128 的数组LeetCode 该题约束下 N ≤ 100 时可安全使用若数组更大需要相应增大bitsToShift。提取下标时用 127只取低 7 位原值信息则完整保留在高位排序时天然按「原值 → 下标」的字典序排列保证相同原值聚在一起且顺序一致。多语言实现class Solution: def anagramMappings(self, nums1: List[int], nums2: List[int]) - List[int]: bitsToShift 7 numToGetLastBits (1 bitsToShift) - 1 # Store the index within the integer itself. for i in range(len(nums1)): nums1[i] (nums1[i] bitsToShift) i nums2[i] (nums2[i] bitsToShift) i # Sort both lists so that the original integers end up at the same index. nums1.sort() nums2.sort() # List to store the anagram mappings. mappings [0] * len(nums1) for i in range(len(nums1)): # Store the index in the second list corresponding to the integer index in the first list. mappings[nums1[i] numToGetLastBits] (nums2[i] numToGetLastBits) return mappingsclass Solution { final int bitsToShift 7; final int numToGetLastBits (1 bitsToShift) - 1; public int[] anagramMappings(int[] nums1, int[] nums2) { // Store the index within the integer itself. for (int i 0; i nums1.length; i) { nums1[i] (nums1[i] bitsToShift) i; nums2[i] (nums2[i] bitsToShift) i; } // Sort both lists so that the original integers end up at the same index. Arrays.sort(nums1); Arrays.sort(nums2); // List to store the anagram mappings. int[] mappings new int[nums1.length]; for (int i 0; i nums1.length; i) { // Store the index in the second list corresponding to the integer index in the first list. mappings[nums1[i] numToGetLastBits] (nums2[i] numToGetLastBits); } return mappings; } }class Solution { public: const int bitsToShift 7; const int numToGetLastBits (1 bitsToShift) - 1; vectorint anagramMappings(vectorint nums1, vectorint nums2) { // Store the index within the integer itself. for (int i 0; i nums1.size(); i) { nums1[i] (nums1[i] bitsToShift) i; nums2[i] (nums2[i] bitsToShift) i; } // Sort both lists so that the original integers end up at the same index. sort(nums1.begin(), nums1.end()); sort(nums2.begin(), nums2.end()); // List to store the anagram mappings. vectorint mappings(nums1.size()); for (int i 0; i nums1.size(); i) { // Store the index in the second list corresponding to the integer index in the first list. mappings[nums1[i] numToGetLastBits] (nums2[i] numToGetLastBits); } return mappings; } };class Solution { /** * param {number[]} nums1 * param {number[]} nums2 * return {number[]} */ anagramMappings(nums1, nums2) { const bitsToShift 7; const numToGetLastBits (1 bitsToShift) - 1; // Store the index within the integer itself. for (let i 0; i nums1.length; i) { nums1[i] (nums1[i] bitsToShift) i; nums2[i] (nums2[i] bitsToShift) i; } // Sort both arrays so that the original integers end up at the same index. nums1.sort((a, b) a - b); nums2.sort((a, b) a - b); // Array to store the anagram mappings. const mappings new Array(nums1.length); for (let i 0; i nums1.length; i) { // Store the index in the second array corresponding to the integer index in the first array. mappings[nums1[i] numToGetLastBits] (nums2[i] numToGetLastBits); } return mappings; } }func anagramMappings(nums1 []int, nums2 []int) []int { bitsToShift : 7 numToGetLastBits : (1 bitsToShift) - 1 // Store the index within the integer itself. for i : 0; i len(nums1); i { nums1[i] (nums1[i] bitsToShift) i nums2[i] (nums2[i] bitsToShift) i } // Sort both slices so that the original integers end up at the same index. sort.Ints(nums1) sort.Ints(nums2) // Slice to store the anagram mappings. mappings : make([]int, len(nums1)) for i : 0; i len(nums1); i { // Store the index in the second slice corresponding to the integer index in the first slice. mappings[nums1[i] numToGetLastBits] nums2[i] numToGetLastBits } return mappings }class Solution { fun anagramMappings(nums1: IntArray, nums2: IntArray): IntArray { val bitsToShift 7 val numToGetLastBits (1 shl bitsToShift) - 1 // Store the index within the integer itself. for (i in nums1.indices) { nums1[i] (nums1[i] shl bitsToShift) i nums2[i] (nums2[i] shl bitsToShift) i } // Sort both arrays so that the original integers end up at the same index. nums1.sort() nums2.sort() // Array to store the anagram mappings. val mappings IntArray(nums1.size) for (i in nums1.indices) { // Store the index in the second array corresponding to the integer index in the first array. mappings[nums1[i] and numToGetLastBits] nums2[i] and numToGetLastBits } return mappings } }class Solution { func anagramMappings(_ nums1: [Int], _ nums2: [Int]) - [Int] { var nums1 nums1 var nums2 nums2 let bitsToShift 7 let numToGetLastBits (1 bitsToShift) - 1 // Store the index within the integer itself. for i in 0..nums1.count { nums1[i] (nums1[i] bitsToShift) i nums2[i] (nums2[i] bitsToShift) i } // Sort both arrays so that the original integers end up at the same index. nums1.sort() nums2.sort() // Array to store the anagram mappings. var mappings Int for i in 0..nums1.count { // Store the index in the second array corresponding to the integer index in the first array. mappings[nums1[i] numToGetLastBits] nums2[i] numToGetLastBits } return mappings } }impl Solution { pub fn anagram_mappings(mut nums1: Veci32, mut nums2: Veci32) - Veci32 { let bits_to_shift 7; let num_to_get_last_bits (1 bits_to_shift) - 1; for i in 0..nums1.len() { nums1[i] (nums1[i] bits_to_shift) i as i32; nums2[i] (nums2[i] bits_to_shift) i as i32; } nums1.sort(); nums2.sort(); let mut mappings vec![0i32; nums1.len()]; for i in 0..nums1.len() { mappings[(nums1[i] num_to_get_last_bits) as usize] nums2[i] num_to_get_last_bits; } mappings } }复杂度分析时间复杂度O(N log N)—— 两次排序占主导编码与配对均为 O(N)空间复杂度O(log N)—— 排序所需的递归栈空间无需额外哈希表。其中 N 为数组nums1和nums2的元素个数。该解法的适用前提与限制编码是原地修改输入数组如 Swift 版本需先拷贝为var排序也会打乱原始顺序若后续还需原数组则需额外拷贝左移位数必须按数组长度上限选取7 位只支持下标 0127。若 N 更大需要增大bitsToShift并同步更新掩码该解法牺牲了时间O(N log N)换取空间O(1) 级属于「空间换时间」的反向权衡理解其位打包思想的价值大于实际工程用途。常见陷阱Common Pitfalls陷阱一重复值的下标被覆盖使用哈希表时每个值只存一个下标重复出现的值会全部映射到同一个下标。如果题目要求重复值各自映射到不同下标就不能用「单值 → 单下标」的表而应改为存储下标列表如MapInteger, ListInteger或使用下标栈并逐个弹出pop保证每个下标只被使用一次。本题标准版本对重复值不做区分、任意合法下标均可但面试时应主动和面试官确认重复值的语义。陷阱二位运算的 off-by-one 与下标碰撞用位运算编码下标时如果左移位数选得太少较大的下标会「溢出」到高位导致不同元素编码后碰撞、排序后无法正确对齐。选取移位位数时必须保证2^bitsToShift maxIndex即 bits 足够容纳可能出现的最大下标例如 N100 时需要至少 7 位因为 2^7128 100。三种解法速览与选型建议解法时间空间是否修改输入适用场景暴力双重循环O(N²)O(1)否仅用于理解题意或极小数据哈希表O(N)O(N)否面试首选通用性最强位运算 排序O(N log N)O(log N)是考察位运算技巧、追求省内存扩展阅读同类「值 → 下标」哈希建模two-integer-sum.md、two-integer-sum-ii.md变位词家族其他题目anagram-groups.md、is-anagram.md、find-all-anagrams-in-a-string.md哈希表设计的工程实践design-hashmap.md、design-hashset.md【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考