Leetcode每日一题 —— 3740. 三个相等元素之间的最小距离 I

2026-04-13 13:032阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:
力扣 LeetCode

3740. 三个相等元素之间的最小距离 I - 力扣(LeetCode)

3740. 三个相等元素之间的最小距离 I - 给你一个整数数组 nums。 如果满足 nums[i] == nums[j] == nums[k],且 (i, j, k) 是 3 个不同下标,那么三元组 (i, j, k) 被称为有效三元组。 有效三元组的距离被定义为 abs(i - j) + abs(j - k) + abs(k - i),其中 abs(x) 表示 x 的绝对值。 返回一个整数,表示 有效三元组的最小可能距离。如果不存在有效三元组,返回...

PS
昨天的题放弃了。看了解答,眼睛说我会了,脑子说你不会!看着好像会了,一用就稀里糊涂。还得学习啊。

思路
要求的距离肯定是连续三个相同数,因为如果中间隔一个相同数那么一定比连续的三个相同数更长。实质就是求最后一个数的位置减第一个数的位置的两倍。
应该可以用int[]来代替Map,不过今天数据量小,先这样。

代码

public int minimumDistance(int[] nums) { int n = nums.length; Map<Integer, List<Integer>> list = new HashMap<>(); for (int i = 0; i < n; i++) { list.compute(nums[i], (k, v) -> v == null ? new ArrayList<>() : v).add(i); } int ans = Integer.MAX_VALUE; for (Map.Entry<Integer, List<Integer>> pair : list.entrySet()) { if (pair.getValue().size() < 3) { continue; } for (int i = 2; i < pair.getValue().size(); i++) { ans = Math.min(ans, 2 * (pair.getValue().get(i) - pair.getValue().get(i - 2))); } } return ans == Integer.MAX_VALUE ? -1 : ans; } 网友解答:


--【壹】--:

直接用一个滑动窗口的思想可以做到O(N), 这样的话就一直i < j < k把上面的式子化简一下就是(k - i) * 2
然后惊喜的发现明天的题目也可以过

class Solution: def minimumDistance(self, nums: List[int]) -> int: def cc(dq): return (dq[-1] - dq[0]) << 1 ans = inf n = len(nums) cnt = defaultdict(lambda : [0, deque()]) for i, x in enumerate(nums): cnt[x][0] += 1 cnt[x][1].append(i) if cnt[x][0] == 3: ans = min(ans, cc(cnt[x][1])) cnt[x][1].popleft() cnt[x][0] -= 1 return ans if ans != inf else -1


--【贰】--:

昨天的题有点变态了,真整不出来,就写 Hot100 去了…

今天这题还行,暴力能过,也能用哈希表。用哈希表的话维护相邻的两个相等数字的下标即可。

class Solution { public: int minimumDistance(vector<int>& nums) { // 这题的规模其实暴力就足够了 // 如果规模变大了可能就得用到哈希表 // 找相邻的三个相同数字对应的位置 // 记录数字对应上一次出现和上上次出现的下标 unordered_map<int, pair<int, int>> nMap; int res = INT_MAX; for (int i = 0; i < nums.size(); i++) { if (nMap.count(nums[i]) == 0) { // 还没有见过 nums[i] // first 为上次出现,second 为上上次出现 nMap[nums[i]] = make_pair(i, -1); } else { // 否则更新 int lastLastAppear = nMap[nums[i]].second; int lastAppear = nMap[nums[i]].first; nMap[nums[i]].second = lastAppear; nMap[nums[i]].first = i; if (lastLastAppear != -1) { res = min(res, i - lastAppear + lastAppear - lastLastAppear + i - lastLastAppear); } } } return res == INT_MAX ? -1 : res; } };


接下来继续复习 Hot100。

238. 除了自身以外数组的乘积 - 力扣(LeetCode)

  • 这题之前做过,用了前缀积和后缀积。但其实不用额外开数组去存后缀积,后缀积可以在计算结果的过程中一起动态构造。这样以来是符合题目要求的 O(1) 空间的(输出数组不算空间)。

class Solution { public: vector<int> productExceptSelf(vector<int>& nums) { int n=nums.size(); vector<int> res(n); res[0]=nums[0]; // 输出数组不算空间,先生成前缀 for(int i=1;i<n;i++){ res[i]=res[i-1]*nums[i]; } // 然后动态构造后缀,填回结果 res[n-1]=nums[n-1]; int postfix=1; for(int i=n-1;i>=1;i--){ res[i]=postfix*res[i-1]; postfix*=nums[i]; } res[0]=postfix; return res; } };


73. 矩阵置零 - 力扣(LeetCode)

  • 这种题如果要原地操作,八成需要利用输入数组做标记。这题注意首行首列单独处理即可。

class Solution { public: void setZeroes(vector<vector<int>>& matrix) { // 仅用常量空间的话,必然要在原数组上做标记 // 无非就是要多几道扫描 // 可以利用首行首列来标记 // 但处理的时候首行首列要单独处理,不然标记后不知道首行首列要不要置 0 bool firstRowZero=false,firstColZero=false; // 先扫描首行首列 int m=matrix.size(),n=matrix[0].size(); for(int i=0;i<m;i++){ if(matrix[i][0]==0){ firstColZero=true; break; } } for(int j=0;j<n;j++){ if(matrix[0][j]==0){ firstRowZero=true; break; } } // 再扫描内层,进行标记 for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ if(matrix[i][j]==0){ matrix[i][0]=0; matrix[0][j]=0; } } } // 处理行列,进行置 0 for(int i=1;i<m;i++){ for(int j=1;j<n;j++){ if(matrix[i][0]==0||matrix[0][j]==0){ matrix[i][j]=0; } } } // 最后处理首行首列 if(firstColZero){ for(int i=0;i<m;i++){ matrix[i][0]=0; } } if(firstRowZero){ for(int j=0;j<n;j++){ matrix[0][j]=0; } } } };


160. 相交链表 - 力扣(LeetCode)

  • 首先是 408 资料上提出的解法,两个链表可能共享后缀,这样的话只需要统计二者长度,让较长链表的指针先走,对齐两个指针后再齐头并进即可。

class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { if(headA==nullptr||headB==nullptr){ return nullptr; } // 先找到两个链表的长度 int m=0,n=0; ListNode* ptr1=headA,*ptr2=headB; while(ptr1!=nullptr){ m++; ptr1=ptr1->next; } while(ptr2!=nullptr){ n++; ptr2=ptr2->next; } // 两个链表后缀可能有相同的部分,让较长的链表的指针先走 ptr1=headA; ptr2=headB; if(m>n){ for(int i=0;i<m-n;i++){ ptr1=ptr1->next; } }else if(m<n){ for(int i=0;i<n-m;i++){ ptr2=ptr2->next; } } // 两个指针齐头并进 while(ptr1!=nullptr){ if(ptr1==ptr2){ break; } ptr1=ptr1->next; ptr2=ptr2->next; } return ptr1; } };

  • 还有类似链表求环思想的更简洁的思路,但还是和链表长度有关。让两个指针循环扫描各自的链表。链表长度一致,二者要不相遇要不最终一起到达 null;链表长度不一致,二者会错开,但最终总会有相遇(要不相遇在相交节点,要不相遇于虚无的 null)。

class Solution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { if(headA==nullptr||headB==nullptr){ return nullptr; } // 这题 O(1) 空间解法有难度,和链表求环那题有相似的思路 // 很巧妙利用了两个遍历指针循环扫描时的特性,每个指针扫描到头就回到链表头 // 如果两个指针距离相交处距离一致,那么必然会同时指向相交节点; // 如果没有相交且链表长度一致,则两个指针会同时指向 null // 否则两个指针会逐渐错开进行循环,直至再次相交,再次相交时要不都是 null,要不是同一个节点 ListNode *ptr1=headA,*ptr2=headB; while(ptr1!=ptr2){ if(ptr1!=nullptr){ ptr1=ptr1->next; }else{ ptr1=headA; } if(ptr2!=nullptr){ ptr2=ptr2->next; }else{ ptr2=headB; } } return ptr1; } };


101. 对称二叉树 - 力扣(LeetCode)

  • 同时先序遍历根节点的左右子树,左子树和右子树的遍历流向相反。

class Solution { public: bool isSymmetric(TreeNode* root) { // 其实就是分成左子树和右子树分别遍历 // 左子树和右子树先序遍历方向相反,检查是否一致 if(root==nullptr){ return true; } auto dfs=[&](auto&& self,TreeNode* n1,TreeNode* n2)->bool{ if(n1==nullptr&&n2==nullptr){ return true; } if(n1==nullptr||n2==nullptr){ return false; } if(n1->val!=n2->val){ return false; } return self(self,n1->left,n2->right)&&self(self,n1->right,n2->left); }; return dfs(dfs,root->left,root->right); } };


--【叁】--:

昨天拼尽全力无法办到喵(今天抚慰受伤的心灵

class Solution { public: int minimumDistance(vector<int>& nums) { std::vector<std::vector<int>> v(101,std::vector<int>()); for(int i = 0; i < nums.size(); i++) { v[nums[i]].push_back(i); } const int inf = std::numeric_limits<int>::max(); int res = inf; for(int i = 0; i < v.size(); i++) { if(v[i].size() < 3) { continue; } for(int j = 0; j + 2 < v[i].size(); j++) { res = min(res,2 * (v[i][j + 2] - v[i][j])); } } return (res == inf ? -1 : res); } };