LeetCode 961.在长度 2N 的数组中找出重复 N 次的元素:5种语言x5种方法(及其变种) —— All By Hand

【LetMeFly】961.在长度 2N 的数组中找出重复 N 次的元素:5种语言x5种方法(及其变种) ------ All By Hand

力扣题目链接:https://leetcode.cn/problems/n-repeated-element-in-size-2n-array/

给你一个整数数组 nums ,该数组具有以下属性:

  • nums.length == 2 * n.
  • nums 包含 n + 1不同的 元素
  • nums 中恰有一个元素重复 n

找出并返回重复了 n次的那个元素。

示例 1:

复制代码
输入:nums = [1,2,3,3]
输出:3

示例 2:

复制代码
输入:nums = [2,1,2,5,3,2]
输出:2

示例 3:

复制代码
输入:nums = [5,1,5,2,5,3,5,4]
输出:5

提示:

  • 2 <= n <= 5000
  • nums.length == 2 * n
  • 0 <= nums[i] <= 104
  • numsn + 1不同的 元素组成,且其中一个元素恰好重复 n

解题方法一.1:排序+看相邻

nums排个序,有且仅有出现 n n n次的元素会相邻且相同。

  • 时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)

AC代码

C++
cpp 复制代码
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        for (int i = 1; i < nums.size(); i++) {
            if (nums[i] == nums[i - 1]) {
                return nums[i];
            }
        }
        return -1;  // FAKE RETURN
    }
};

竟然和2022.5.21写的几乎一模一样。

Python
python 复制代码
from typing import List

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        nums.sort()
        for i in range(1, len(nums)):
            if nums[i] == nums[i - 1]:
                return nums[i]
Go
go 复制代码
package main

import "sort"

func repeatedNTimes(nums []int) int {
    sort.Ints(nums)
    for i := 1; i < len(nums); i++ {
        if nums[i] == nums[i - 1] {
            return nums[i]
        }
    }
    return -1  // FAKE RETURN
}
Java
java 复制代码
import java.util.Arrays;

class Solution {
    public int repeatedNTimes(int[] nums) {
        Arrays.sort(nums);
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i-1]) {
                return nums[i];
            }
        }
        return -1;
    }
}
Rust
rust 复制代码
impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        nums.sort();
        for i in 1..nums.len() {
            if nums[i] == nums[i - 1] {
                return nums[i];
            }
        }
        -1  // FAKE RETURN
    }
}

解题方法一.2:排序+看中间

有一个数字恰好出现了 n n n次,那么排序后中间位置的两个元素至少有一个是它。

  • 时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)

AC代码

C++
cpp 复制代码
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int mid = nums.size() / 2;
        return nums[mid] == nums[mid + 1] ? nums[mid] : nums[mid - 1];
    }
};
C++. 不能通过版本

不能这样思考:

有一个数字恰好出现了 n n n次,那么排序后要么前半段全是它要么后半段全是它。
排序后看看前两个元素相同的话就是前半段,否则就是后半段。

cpp 复制代码
// THIS CANNOT BE ACCEPTED
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        return nums[0] == nums[1] ? nums[0] : nums.back();
    }
};

反例:[1, 2, 2, 3]

Python
python 复制代码
from typing import List

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        nums.sort()
        mid = len(nums) // 2
        return nums[mid] if nums[mid] == nums[mid + 1] else nums[mid - 1]
Go
go 复制代码
package main

import "sort"

func repeatedNTimes(nums []int) int {
    sort.Ints(nums)
    mid := len(nums) / 2
    if nums[mid] == nums[mid + 1] {
        return nums[mid]
    }
    return nums[mid - 1]
}
Java
java 复制代码
import java.util.Arrays;

class Solution {
    public int repeatedNTimes(int[] nums) {
        Arrays.sort(nums);
        int mid = nums.length / 2;
        return nums[mid] == nums[mid + 1] ? nums[mid] : nums[mid - 1];
    }
}
Rust
rust 复制代码
impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        nums.sort();
        let mid: usize = nums.len() / 2;
        if nums[mid] == nums[mid + 1] {
            return nums[mid];
        }
        nums[mid - 1]
    }
}

解题方法二:哈希表

使用哈希表记录出现过的元素,如果一个元素二次出现则视为找到答案。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( n ) O(n) O(n)

AC代码

C++
cpp 复制代码
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        unordered_set<int> visited;
        for (int t : nums) {
            if (visited.count(t)) {
                return t;
            }
            visited.insert(t);
        }
        return -1;  // FAKE RETURN
    }
};
Python
python 复制代码
from typing import List

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        visited = set()
        for t in nums:
            if t in visited:
                return t
            visited.add(t)
Go
go 复制代码
package main

func repeatedNTimes(nums []int) int {
    visited := map[int]bool{}
    for _, t := range nums {
        if _, ok := visited[t]; ok {
            return t
        }
        visited[t] = true
    }
    return -1  // FAKE RETURN
}
Java
java 复制代码
import java.util.Set;
import java.util.HashSet;

class Solution {
    public int repeatedNTimes(int[] nums) {
        Set<Integer> visited = new HashSet<>();
        for (int t : nums) {
            if (visited.contains(t)) {
                return t;
            }
            visited.add(t);
        }
        return -1;  // FAKE RETURN
    }
}
Rust
rust 复制代码
use std::collections::HashSet;

impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        let mut visited = HashSet::new();
        for t in nums.iter() {
            if visited.contains(&t) {
                return *t;
            }
            visited.insert(t);
        }
        -1  // FAKE RETURN
    }
}

解题方法三:相邻相隔一次遍历

长度为 2 n 2n 2n的数组中一个数出现了 n n n次,那么数组中这个数(假设为 t t t)一定存在一下情况之一:

  1. 两个 t t t相邻
  2. 两个 t t t中间隔一个元素

否则,任何两个 t t t之间都间隔至少两个元素, t t t的数量将会无法达到 n n n。

所以我们只需要遍历一遍数组,找到相邻相同相隔一数且相同的数就好了。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)

AC代码

C++
cpp 复制代码
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        for (int i = 2; i < nums.size(); i++) {
            if (nums[i] == nums[i - 1] || nums[i] == nums[i - 2]) {
                return nums[i];
            }
        }
        return nums[0];  // 前面循环中未判断nums[0]是否等于nums[1],若前面未return则说明nums[0]=nums[1]如[1, 1, 2, 3]
    }
};
Python
python 复制代码
from typing import List

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        for i in range(2, len(nums)):
            if nums[i] == nums[i-1] or nums[i] == nums[i-2]:
                return nums[i]
        return nums[0]
Go
go 复制代码
package main

import "sort"

func repeatedNTimes(nums []int) int {
    sort.Ints(nums)
    for i := 2; i < len(nums); i++ {
        if nums[i] == nums[i-1] || nums[i] == nums[i-2] {
            return nums[i]
        }
    }
    return nums[0]
}
Java
java 复制代码
class Solution {
    public int repeatedNTimes(int[] nums) {
        for (int i = 2; i < nums.length; i++) {
            if (nums[i] == nums[i-1] || nums[i] == nums[i-2]) {
                return nums[i];
            }
        }
        return nums[0];
    }
}
Rust
rust 复制代码
use std::collections::HashSet;

impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        for i in 2..nums.len() {
            if nums[i] == nums[i - 1] || nums[i] == nums[i - 2] {
                return nums[i]
            }
        }
        nums[0]
    }
}

解题方法四:擂台赛(摩尔投票法)

假设某个元素在数组中数量占绝对优势(大于半数),那么可以数组中元素依次上擂台:

  • 如果擂台上没有人,自己成为霸主,生命值为1
  • 如果擂台上有人,且和自己相同,则霸主生命值加1
  • 如果擂台上有人,且和自己不同,则霸主生命值减1(减少至0视为死亡)

这种方法不限制其他数是否有重复,但是与本题相比本题其他数不会重复但是所寻找元素比严格大于半数少1次。

怎么办呢?将数组分为第一个数和剩下其他数两部分:

  • 如果所找元素是第一个数,那么在剩下的数组中一定存在和这个数相同的数,相同则找到
  • 如果所找元素和第一个数不相等,那么所找元素在剩下的数组中出现次数大于半数,可以使用摩尔投票法

这种方法更适合其他元素也有相同的情况 ,用在此处略有牵强。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)

AC代码

C++
cpp 复制代码
class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        if (nums[0] == nums[1]) {
            return nums[0];
        }
        int now = -1, hp = 0;
        for (int i = 1; i < nums.size(); i++) {
            if (nums[i] == nums[0]) {
                return nums[0];
            }
            if (hp == 0) {
                hp = 1;
                now = nums[i];
            } else if (now == nums[i]) {
                hp++;  // 其实也可以直接return了
            } else {
                hp--;
            }
        }
        return now;
    }
};
Python
python 复制代码
from typing import List

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        ans, hp = -1, 0
        for t in nums[1:]:
            if t == nums[0]:
                return t
            if not hp:
                ans, hp = t, 1
            elif ans == t:
                hp += 1
            else:
                hp -= 1
        return ans
GO
go 复制代码
package main

func repeatedNTimes(nums []int) int {
    ans, hp := -1, 0
    for i := 1; i < len(nums); i++ {
        if nums[i] == nums[0] {
            return nums[0]
        }
        if hp == 0 {
            ans, hp = nums[i], 1
        } else if ans == nums[i] {
            hp++
        } else {
            hp--
        }
    }
    return ans
}
Java
java 复制代码
class Solution {
    public int repeatedNTimes(int[] nums) {
        int ans = 0, hp = 0;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[0]) {
                return nums[0];
            }
            if (hp == 0) {
                ans = nums[i];
                hp = 1;
            } else if (ans == nums[i]) {
                hp++;
            } else {
                hp--;
            }
        }
        return ans;
    }
}
Rust
rust 复制代码
use std::collections::HashSet;

impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        let mut ans: i32 = -1;
        let mut hp: i32 = 0;
        for i in 1..nums.len() {
            if nums[i] == nums[0] {
                return nums[0]
            }
            if hp == 0 {
                ans = nums[i];
                hp = 1;
            } else if ans == nums[i] {
                hp += 1;
            } else {
                hp -= 1;
            }
        }
        ans
    }
}

解题方法五(trick):随机

每次随机选取两个元素,直到选到两个相等的元素为止。

这种方法看似很盲目,其实效率很高,所选两元素相同的概率为 n 2 n × n − 1 2 n ≈ 1 4 \frac{n}{2n}\times \frac{n-1}{2n}\approx \frac{1}{4} 2nn×2nn−1≈41,平均4次随机选择就能找到答案,因此期望时间复杂度为 O ( 1 ) O(1) O(1)。

为什么是 n 2 n × n − 1 2 n \frac{n}{2n}\times \frac{n-1}{2n} 2nn×2nn−1呢?因为是全随机,第一次在 2 n 2n 2n个数字里面有 n n n个可选,第二次还是在 2 n 2n 2n个数字里面有 n − 1 n-1 n−1个可选。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)

AC代码

C++
cpp 复制代码
class Solution {
private:
    inline static mt19937 gen = mt19937(random_device{}());
    inline static uniform_int_distribution<> dis;
public:
    int repeatedNTimes(vector<int>& nums) {
        dis.param(uniform_int_distribution<>::param_type(0, nums.size() - 1));
        int loc1, loc2;
        do {
            loc1 = dis(gen);
            loc2 = dis(gen);
        } while (loc1 == loc2 || nums[loc1] != nums[loc2]);
        return nums[loc1];
    }
};
Python
python 复制代码
from typing import List
from random import randint

class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        n = len(nums) - 1
        while True:
            loc1 = randint(0, n)
            loc2 = randint(0, n)
            # print(loc1, loc2)
            if loc1 != loc2 and nums[loc1] == nums[loc2]:
                return nums[loc1]
Python.TLE

注意python random.randint包含左右区间端点,而random.randrange不包含右端点,所以下面代码在[1, 2, 3, 3]这种时候会因为永远无法随机到3而超时:

python 复制代码
from typing import List
from random import randrange

# THIS CANNOT ACCESS
class Solution:
    def repeatedNTimes(self, nums: List[int]) -> int:
        n = len(nums) - 1
        while True:
            loc1 = randrange(0, n)
            loc2 = randrange(0, n)
            if loc1 != loc2 and nums[loc1] == nums[loc2]:
                return nums[loc1]
Go
go 复制代码
package main

import "math/rand"

func repeatedNTimes(nums []int) int {
    n := len(nums)
    for true {
        l1 := rand.Intn(n)
        l2 := rand.Intn(n)
        if l1 != l2 && nums[l1] == nums[l2] {
            return nums[l1]
        }
    }
    return -1  // FAKE RETURN
}
Java
java 复制代码
import java.util.Random;

class Solution {
    public int repeatedNTimes(int[] nums) {
        Random random = new Random();
        int n = nums.length;
        while (true) {
            int l1 = random.nextInt(n);
            int l2 = random.nextInt(n);
            if (l1 != l2 && nums[l1] == nums[l2]) {
                return nums[l1];
            }
        }
    }
}
Rust
rust 复制代码
use rand::Rng;

impl Solution {
    pub fn repeated_n_times(mut nums: Vec<i32>) -> i32 {
        let mut rng = rand::thread_rng();  // 一定要为mut
        let n= nums.len();
        loop {
            let l1 = rng.gen_range(0..n);
            let l2 = rng.gen_range(0..n);
            if l1 != l2 && nums[l1] == nums[l2] {
                return nums[l1];
            }
        }
    }
}

End

The Real End, Thanks! 同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

相关推荐
清酒难咽18 小时前
算法案例之递归
c++·经验分享·算法
让我上个超影吧19 小时前
【力扣26&80】删除有序数组中的重复项
算法·leetcode
张张努力变强20 小时前
C++ Date日期类的设计与实现全解析
java·开发语言·c++·算法
沉默-_-20 小时前
力扣hot100滑动窗口(C++)
数据结构·c++·学习·算法·滑动窗口
钱彬 (Qian Bin)20 小时前
项目实践19—全球证件智能识别系统(优化检索算法:从MobileNet转EfficientNet)
算法·全球证件识别
feifeigo12320 小时前
基于EM算法的混合Copula MATLAB实现
开发语言·算法·matlab
漫随流水20 小时前
leetcode回溯算法(78.子集)
数据结构·算法·leetcode·回溯算法
全栈游侠21 小时前
数据结构 - 链表
数据结构·链表
一生只为赢21 小时前
通俗易懂:ARM指令的寻址方式(三)
运维·arm开发·数据结构·嵌入式实时数据库
IT猿手21 小时前
六种智能优化算法(NOA、MA、PSO、GA、ZOA、SWO)求解23个基准测试函数(含参考文献及MATLAB代码)
开发语言·算法·matlab·无人机·无人机路径规划·最新多目标优化算法