剑指Offer 03. 数组中重复的数字
作者:互联网
LeetCode_剑指Offer
03. 数组中重复的数字
一、题目描述
- 找出数组中重复的数字。
- 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
二、示例描述
-
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3 限制:2 <= n <= 100000
三、解法分析
方案一:暴力求解
- 遍历数组,将数组中的每个元素与其他元素进行对比,有相同的就进行输出即可
代码实现:
public class Solution {
public int findRepeatNumber(int[] nums) {
for (int i = 0; i < nums.length; i++) {
for(int j = i+1; j< nums.length; j++){
if(nums[i] == nums[j]){
return nums[i];
}
}
}
return -1;
}
}
算法复杂度分析:
- 时间复杂度O(n^2)
- 空间复杂度O(1)
存在的问题:限制:2 <= n <= 100000,时间复杂度太高,提交代码结果会“超过时间限制”
- 剑指Offer书中提到一个方法,先将数组进行排序后,在从排序的数组中长出重复的数字。排序后遍历数组两两相邻元素比较即可找出重复元素。排序时间复杂度为O(nlogn),遍历时间复杂度O(n)
方案二:哈希表
- 题目如果改成是统计数组中每个元素出现的次数,最先想到的是Map。遍历数组以元素作为key,元素出现次数作为value。主要使用的就是Map中的key唯一的这一点特性。同样能保证数据唯一的还有Set。
- 使用哈希表来解决该问题:
- 遍历数组,每扫描到一个数字时,都可以使用O(1)的时间来判断哈希表里是否包含该数字
- 如果哈希表中没有该数字,就将该数字加入到哈希表
- 若果哈希表中已有该数字,则找到了重复数字,返回该数字即可
- 这里用到了典型的空间换时间的思想,此种方式的时间复杂度减少为O(n),但提高时间效率是以大小为O(n)的哈希表为代价的。
代码实现:
public class Solution {
public int findRepeatNumber(int[] nums) {
HashSet hashSet=new HashSet();
for (int num : nums) {
if(hashSet.contains(num)){
return num;
}
hashSet.add(num);
}
return -1;
}
}
方案三:原地置换
- 注意数组中的数字范围是0~n-1,如果数组没有重复数字,排序后索引 i 处的元素也应是 i 。由于数组中有重复数字,则有的位置存在多个重复数字。
- 一个萝卜一个坑:
- 【萝卜坑】萝卜 ——>n 【索引】元素
- 遍历萝卜坑,每次遍历都是为了给萝卜坑找到对应编号的萝卜
{【0】2,【1】3,【2】1,【3】0,【4】2,【5】5,【6】3} - 首先遍历到萝卜坑【0】,发现坑里是萝卜2,那你就看看萝卜坑【2】中的萝卜是不是也放错了,发现人家不匹配,就舍己为人,先让人家配对,现将萝卜坑【0】和萝卜坑【2】中萝卜交换。(因为你的目的是萝卜坑和萝卜匹配,你不匹配那就先让人家匹配吧,而且人家坑里的萝卜不能扔了吧,你得给带走,那就进行交换。交换完了你可能还是不匹配,但是你已经让其中的一个萝卜坑匹配了萝卜)
{【0】1,【1】3,【2】2,【3】0,【4】2,【5】5,【6】3} - 这时候萝卜坑【0】中是萝卜1,还是不匹配,发现萝卜坑【1】也不匹配,现在讲萝卜坑【0】和萝卜坑【1】中萝卜交换
{【0】3,【1】1,【2】2,【3】0,【4】2,【5】5,【6】3} - 这时候萝卜坑【0】中是萝卜3,还是不匹配,发现萝卜坑【3】也不匹配,现在讲萝卜坑【0】和萝卜坑【3】中萝卜交换
{【0】0,【1】1,【2】2,【3】3,【4】2,【5】5,【6】3} - 这时候萝卜坑【0】中是萝卜0,匹配,接着进行遍历
- 遍历时发现萝卜坑【1】【2】【3】都匹配,那就啥都不用做
- 遍历到了萝卜坑【4】,发现是萝卜2,不匹配,然后想跟萝卜坑【2】中萝卜进行交换,发现人家萝卜坑已经匹配到萝卜,那这个萝卜2就是多余了,重复了
- 输出萝卜2即可
遍历萝卜坑{
当前萝卜和萝卜坑不匹配 (循环){
如果 萝卜应当待的坑中 坑和萝卜匹配{
当前萝卜是多余重复的
}
将当前坑中萝卜 和 萝卜叮当待的坑中萝卜 交换
}
}
代码实现:
public class Solution {
public int findRepeatNumber(int[] nums) {
int length = nums.length;
if(nums == null || length<=0){//空数组
return -1;
}
for(int i = 0; i < length; i++){
if(nums[i]<0 || nums[i]>length-1){//数组不满足条件
return -1;
}
}
for (int i = 0; i < length; i++){
while (nums[i] != i) {
if (nums[nums[i]] == nums[i]) {
return nums[i];
}
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}
算法复杂度分析:
- 时间复杂度:代码中有两个循环for/while,所以大家都会认为算法复杂度是O(n^2),但是每个数字最多只要交换两次就能够找到属于自己的位置,因此时间复杂度是O(n)
- 空间复杂度:所有的操作都是在原本的数组中进行的,不需要额外分配内存,因此空间复杂度是O(1)
标签:03,数字,nums,int,萝卜,复杂度,Offer,数组 来源: https://blog.csdn.net/qq_24410589/article/details/118498216