剑指 Offer(第 2 版)刷题 | 03. 数组中重复的数字

简介: 本文是作者针对《剑指 Offer(第 2 版)》中 "数组中重复的数字" 问题的刷题记录,分享了使用排序算法和相邻比较大小两种方法来找出数组中的重复数字,并提供了C++的实现代码。

题目 03.找出数组中重复的数字

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

示例 1:

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

限制:

2 <= n <= 100000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的解法:

(1)先排序
(2)相邻比较大小

(1)排序算法

尝试了c++常用的排序算法,在未测试递归的情况下,测试发现插入排序算法速度最优,在一定编译时间内通过的还有希尔排序。下面理解一下,插入排序和希尔排序。

插入排序

对于第K个元素,将该元素的值存储在零时变量中,比较第前一个元素与该元素的大小,如果大于该元素就将前一个元素往后移动一步;
比较前面第二个元素与该元素的大小,如果大于该元素就将前第二个元素往后移动一步;
重复上述过程直到找到小于等于原来第K个元素(保存在零时变量中)的位置,并将第K个元素插入到这个元素的后面。或者找不到小于等于第K个元素的位置,就将原来第K个元素插入到数组的首地址。
————————————————
版权声明:本文为CSDN博主「liqinzhe223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liqinzhe11/article/details/78743743

代码如下:

        //插入排序  44ms
        int n = nums.size();
        for (int i = 1; i < n; i++)
        {
   
            int insert_num = nums[i], j;
            for (j = i - 1; j >= 0; j--)
            {
   
                if (nums[j] > insert_num)
                    nums[j + 1] = nums[j];
                else
                    break;
            }
            nums[j + 1] = insert_num; //重要,后移的关键一步,类似交换
        }

将第K个元素插入到这个元素的后面,需要理解如下的中间过程。

3个数[1,3,2], 经历如上代码,依次中间过程如下:

i = 1, j = 0, insert_num = nums[1] = 3;

nums[0] < nums[1] ;

无break; j = 0 ;

nums[1] = insert_num = 3; 3个数变为[1,3,2]

i = 2, j = 1, insert_num = nums[2] = 2;

nums[1] > nums[2] ;

nums[2] = nums[1];

3个数变为[1,3,3];

i = 2 , j = 0,insert_num = nums[2] = 2;

nums[0] < nums[1] ;

break; j = 0 ;

nums[1] = insert_num = 2;

3个数变为[1,2,3]

希尔排序

希尔排序是在插入排序的基础上进行发展的,通过一个希尔增量先排序一定间隔的数据。

简单的,一个希尔增量如下:

int increment = n / 2; increment > 0; increment /= 2

简单的,将插入排序中增1的地方改为增K就行。

如下三句代码:

j = i - increment; j >= 0; j -= increment;

nums[j + increment] = nums[j];

nums[j + increment] = insert_num;

        // 希尔排序 88ms
        int n = nums.size();
        for (int increment = n / 2; increment > 0; increment /= 2)
        {
   
            for (int i = increment; i < n; i++)
            {
   
                int insert_num = nums[i], j;
                for (j = i - increment; j >= 0; j -= increment)
                {
   
                    if (nums[j] > insert_num)
                        nums[j + increment] = nums[j];
                    else
                        break;
                }
                nums[j + increment] = insert_num;
            }
        }

所有非递归的排序算法代码如下:

class Solution {
   
public:
    int findRepeatNumber(vector<int>& nums) {
   
        //先排序
        //相邻比较大小
        //相等的数取出来,完成
        //输出结果
        int repeatNum = -1;

        // 版权声明:本文为CSDN博主「liqinzhe223」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
        // 原文链接:https://blog.csdn.net/liqinzhe11/article/details/78743743
        //冒泡排序1  编译成功,超出时间限制
        // int n = nums.size();
        // for (int i = 0; i < n; i++)
        // {
   
        //     for (int j = 0; j < n - 1 - i; j++)
        //     {
   
        //         if (nums[j] > nums[j+1])
        //             swap(nums[j], nums[j+1]);
        //     }
        // }

        //冒泡排序2  编译成功,超出时间限制
        // int n = nums.size();
        // bool flag;
        // for (int i = 0; i < n; i++)
        // {
   
        //     flag = false;
        //     for (int j = 0; j < n - 1 - i; j++)
        //     {
   
        //         if (nums[j] > nums[j+1])
        //         {
   
        //             swap(nums[j], nums[j+1]);
        //             flag = true;
        //         }
        //     }
        //     if (!flag)
        //         break;
        // }

        //冒泡排序3  编译成功,超出时间限制
        // int n = nums.size();
        // int flag = n;
        // int stop_pos;
        // for (int i = 0; i < n; i++)
        // {
   
        //     stop_pos = flag - 1;
        //     flag = 0;
        //     for (int j = 0; j < stop_pos; j++)
        //     {
   
        //         if (nums[j] > nums[j+1])
        //         {
   
        //             swap(nums[j], nums[j+1]);
        //             flag = j + 1;
        //         }
        //     }
        // }

        //插入排序  44ms
        int n = nums.size();
        for (int i = 1; i < n; i++)
        {
   
            int insert_num = nums[i], j;
            for (j = i - 1; j >= 0; j--)
            {
   
                if (nums[j] > insert_num)
                    nums[j + 1] = nums[j];
                else
                    break;
            }
            nums[j + 1] = insert_num;
        }

        //选择排序  编译成功,超出时间限制
        // int n = nums.size();
        // for (int i = 0; i < n - 1; i ++)
        // {
   
        //     int swap_pos = i;
        //     for (int j = i + 1; j < n; j++)
        //     {
   
        //         if (nums[swap_pos] > nums[j])
        //         {
   
        //             swap_pos = j;
        //         }
        //     }

        //     if (swap_pos != i)
        //     {
   
        //         swap(nums[swap_pos], nums[i]);
        //     }
        // }

        // // 希尔排序 88ms
        // int n = nums.size();
        // for (int increment = n / 2; increment > 0; increment /= 2)
        // {
   
        //     for (int i = increment; i < n; i++)
        //     {
   
        //         int insert_num = nums[i], j;
        //         for (j = i - increment; j >= 0; j -= increment)
        //         {
   
        //             if (nums[j] > insert_num)
        //                 nums[j + increment] = nums[j];
        //             else
        //                 break;
        //         }
        //         nums[j + increment] = insert_num;
        //     }
        // }

        // 堆排序,leetcode编译器不支持函数定义  
        //改造后  编译出错 Line 1033: Char 34: runtime error: addition of unsigned offset to 0x603000000040 overflowed to 0x603000000038 (stl_vector.h)
        // int n = nums.size();
        // int size = n;
        // while (n)
        // {
   
        //     //make_heap(a, n); //每次把新的最大元素移到堆顶,也就是nums[0]
        //     for (int i = size - 1; i > 0; i--)
        //     {
   
        //         if (i % 2 && nums[i] > nums[(i - 1) / 2])//奇数
        //             swap(nums[i], nums[(i - 1) / 2]);
        //         else if (i % 2 == 0 && nums[i] > nums[(i - 2) / 2])//偶数
        //             swap(nums[i], nums[(i - 2) / 2]);
        //     }
        //     n--;
        //     size = n;
        //     swap(nums[0], nums[n]); //然后把当前最大移动到后面来作为排好序的元素
        // }

        // 归并排序  用到递归,leetcode编译器不支持函数定义,未尝试
        // 快速排序  用到递归,leetcode编译器不支持函数定义,未尝试

        //相邻比较
        //nums[i+1],预处理超出索引问题
        if(nums[n-2]==nums[n-1])
        {
   
            repeatNum = nums[n-1];
            return nums[n-1];
        }
        //i < n-1,,预处理超出索引问题
        for(int i = 0; i < n-1 ; i++)
        {
   
            if( (nums[i] == nums[i+1]))
            {
   
                repeatNum = nums[i];//相等的数取出来
                return nums[i];
            }
        }
        return repeatNum;
    }
};

(2)相邻比较大小

主要是处理超出索引问题。

        //相邻比较
        //nums[i+1],预处理超出索引问题
        if(nums[n-2]==nums[n-1])
        {
   
            repeatNum = nums[n-1];
            return nums[n-1];
        }
        //i < n-1,,预处理超出索引问题
        for(int i = 0; i < n-1 ; i++)
        {
   
            if( (nums[i] == nums[i+1]))
            {
   
                repeatNum = nums[i];//相等的数取出来
                return nums[i];
            }
        }

参考链接:C++排序算法代码汇总

相关文章
|
2月前
|
C语言 C++ 索引
剑指 Offer(第 2 版)刷题 | 04. 二维数组中的查找
本文是针对《剑指 Offer(第 2 版)》中 "二维数组中的查找" 问题的刷题记录,尝试了二分法查找并记录了遇到的索引越界错误,同时推荐了类二叉树或者线性查找的解法。
剑指 Offer(第 2 版)刷题 | 04. 二维数组中的查找
|
机器学习/深度学习 Cloud Native
【刷题日记】剑指 Offer II 083. 没有重复元素集合的全排列
本次刷题日记的第 35 篇,力扣题为:剑指 Offer II 083. 没有重复元素集合的全排列 ,中等
|
6月前
剑指 Offer 03:数组中重复的数字
剑指 Offer 03:数组中重复的数字
27 0
|
6月前
剑指 Offer 56 - I:数组中数字出现的次数
剑指 Offer 56 - I:数组中数字出现的次数
48 0
|
6月前
剑指 Offer 56 - II:数组中数字出现的次数 II
剑指 Offer 56 - II:数组中数字出现的次数 II
48 0
|
6月前
剑指 Offer 38:字符串的排列
剑指 Offer 38:字符串的排列
46 0
图解LeetCode——剑指 Offer 53 - I. 在排序数组中查找数字 I
图解LeetCode——剑指 Offer 53 - I. 在排序数组中查找数字 I
82 0