数组中重复的数字

剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n n n 的数组 nums 里的所有数字都在 0 ~ n − 1 0~n-1 0n1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

解法一:排序

思路

排序后重复的元素会扎堆在一起,然后遍历一遍即可。

复杂度

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)。排序。

空间复杂度: O ( 1 ) O(1) O(1)。原地堆排。

代码

int findRepeatNumber(int[] nums) {
    int n = nums.length;
    Arrays.sort(nums);
    for (int i = 1; i < n; i++) {
        if (nums[i] == nums[i - 1]) {
            return nums[i];  
        } 
    }
    return -1;
}

解法二:HashSet

思路

利用 HashSet 去重的特性,将数组元素加入 Set 前先判是否在 Set 中,如果 Set 中存在就说明重复了。

复杂度

时间复杂度: O ( n ) O(n) O(n)。遍历数组所有元素。

空间复杂度: O ( n ) O(n) O(n)。Set 最大容纳 n 个元素。

代码

int findRepeatNumber(int[] nums) {
    int n = nums.length;
    HashSet<Integer> s = new HashSet<>();
    for (int i = 0; i < n; i++) {
        if(s.contains(nums[i])) {
            return nums[i];
        }
        s.add(nums[i]);
    }
    return -1;
}

解法三:原地置换(最优解)

思路

将元素放在对应元素下标的元素中,当发现要放的位置与当前元素相同时,则说明坑位被占领,即元素重复。

复杂度

时间复杂度: O ( n ) O(n) O(n)。遍历数组所有元素。

空间复杂度: O ( 1 ) O(1) O(1)。在原数组上置换。

代码

int findRepeatNumber(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        while (nums[i] != i) {
            if (nums[nums[i]] == nums[i]) {
                return nums[i];
            }
            swap(nums, i, nums[i]);
        }
    }
    return -1;
}

void swap(int[] nums, int i, int j) {
    int t = nums[i];
    nums[i] = nums[j];
    nums[j] = t;
}

287. 寻找重复数

给定一个包含 n + 1 n + 1 n+1 个整数的数组 nums ,其数字都在 1 ~ n 1 ~ n 1n 之间(包括 1 和 n),nums 有且只有一个重复的整数 ,找出 这个重复的数 。

你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间

示例 1:

输入:nums = [1,3,4,2,2]
输出:2

示例 2:

输入:nums = [3,1,3,4,2]
输出:3

解法一:Floyd 判环法(最优解)

思路

这题与上一题的最大不同是,限制了 nums 中只有一个重复的整数。

把数组下标看成节点 A,那么对应下标上的元素则是节点 A 指向的下一个节点。按照此法定义,不难得出数组构成一个环。

如:[1,3,4,2,2]

在这里插入图片描述

如:[3,1,3,4,2]

在这里插入图片描述

从图中不难发现,重复节点即入口节点。所以原问题转换为寻找环的入口节点,也就是 141. 环形链表 II

复杂度

时间复杂度: O ( n ) O(n) O(n)。遍历数组所有元素。

空间复杂度: O ( 1 ) O(1) O(1)。只需两个常量。

代码

int findDuplicate(int[] nums) {
    int l = 0, r = 0;
    while (true) {
        l = nums[l];
        r = nums[nums[r]];
        if (l == r) break;
    }
    r = 0;
    while (true) {
        l = nums[l];
        r = nums[r];
        if (l == r) return l;
    }
}

141. 环形链表

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

不允许修改链表。

在这里插入图片描述

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

思路

  1. 慢指针走一步,快指针走两步,直到碰撞。
  2. 当第一次碰撞时,说明该链表存在环。

复杂度

时间复杂度: O ( n ) O(n) O(n)。遍历链表所有元素。

空间复杂度: O ( 1 ) O(1) O(1)。只需两个常量。

代码

boolean hasCycle(ListNode head) {
    ListNode l = head, r = head;
    while (true) {
        if (r == null || r.next == null) return false;
        l = l.next;
        r = r.next.next;
        if (l == r) break;
    }
    return true;
}

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

不允许修改链表。

在这里插入图片描述

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

解法一:Floyd 判环法(最优解)

思路

  1. 慢指针走一步,快指针走两步,直到碰撞。
  2. 当第一次碰撞时,说明该链表存在环。
  3. 快指针返回到原点,快慢指针同时走一步,直到再次碰撞。
  4. 当第二次碰撞时,所指向的节点就是入口节点。

复杂度

时间复杂度: O ( n ) O(n) O(n)。遍历链表所有元素。

空间复杂度: O ( 1 ) O(1) O(1)。只需两个常量。

代码

ListNode detectCycle(ListNode head) {
    ListNode l = head, r = head;
    while (true) {
        if (r == null || r.next == null) return null;
        l = l.next;
        r = r.next.next;
        if (l == r) break;
    }
    r = head;
    while (l != r) {
        l = l.next;
        r = r.next;
    }
    return l;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值