剑指_3.1数组中重复的数字(Python/C++)

题目介绍:

  • 在一个长度为n的数组里,所有的数字都在0到n~1的范围内!
  • 数组中某些元素是重复的,有些是不重复的。
  • 请找出数组中任意一个重复的数字。

题目分析:

一些简要的思路:

  • 先把数组排序,排序好的数组要找出重复的数字很是容易,只需要从头到尾扫面一遍就可以了。排序的时间复杂度是O(nlogn)。
  • 用哈希表表示:从头到尾扫描数组的每个数字,每扫描一个数字,就可以用O(1)的时间判断哈希表里是否包含了该数字。如果哈希表里还没有这个数字,就把它加入哈希表,如果哈希表里面已经存在该数字,就找到了一个重复数字。这个算法的时间复杂度是O(n),但是他提高时间效率的代价是以一个大小为O(n)的哈希表为代价的。

对题目的分析:

观察数组,发现数组的数字都在0-n-1的范围内。即如果这个数组中没有重复的数字,那么排序后元素 i 将出现在下标为 i 的位置。

例如:数组 [2,1,0],长度为3,即数组中的元素都要在0-2之间,符合要求,那么排序后,数组变为 [0,1,2],和数组元素的下标一致。

由于数组中有重复的数字,有些位置可能存在多个数字,同时有些位置可能没有数字。

详细思路:

  • 从头到尾扫描这个数组中每一个数字。当扫描到下标为 i 的数字时,首先比较这个数字(m)是不是等于 i 。
  • 如果是,说明这个数字是在排好序后的位置,则继续扫描下一个数字;
  • 如果不是,则再拿数字(m)和下标为 m 对应的数字比较。
  • 如果数字(m)和第m个数字相等,就找到一个重复的数字(因为该数字在下标i和m的位置都出现了。);
  • 如果数字(m)和第m个数字不相等,就把这两个数字交换,即把数字(m)放到下标为m的位置上。
  • 接下来继续重复比较、交换这样的步骤,直到我们发现一个重复的数字。

代码实现与验证(Python):

class Solution:
    def duplicate(self,array):
        if array == None or len(array) <= 0:             # 数组为空,直接False
            return False
        for i in range(len(array)):
            if array[i] < 0 or array[i] >= len(array):   # 数组中的元素都应在0-n-1之间,否则False
                return False
        for i in range(len(array)):                # 遍历
            while array[i] != i:                   # 若下标和元素值不等,则一直循环下去
                if array[array[i]] == array[i]:    # 若下标为i的元素 array[i] 和下标为 array[i] 的元素相等,则重复,返回array[i]
                    return array[i]
                else:                              # 否则,交换
                    array[array[i]], array[i] = array[i], array[array[i]]
        return False

if __name__=='__main__':
    
# 验证

# 数组中不含重复的数字
    test_1 = [3,1,2,0]

# 长度为 n 的数组里包含一个或多个重复的数字
    test_2 = [2, 3, 1, 0, 2, 5, 3]

# 数组为空
    test_3 = None

# 数组中元素值超出了0-n-1
    test_4 = [2, 6, 1, 0]

    solution = Solution()
    print("test_1:", solution.duplicate(test_1))
    print("test_2:", solution.duplicate(test_2))
    print("test_3:", solution.duplicate(test_3))
    print("test_4:", solution.duplicate(test_4))

验证结果:

test_1: False
test_2: 2
test_3: False
test_4: False

复杂度分析:

时间复杂度:
上述代码中,有两个循环,但是每一个数字最多交换两次就可以找到属于它自己的位置,因此总的时间复杂度的 O(n)。

空间复杂度:
上述代码的所有操作都是在原数组上进行的,不需要额外分配内存,因此空间复杂度是 O(1)。

C++实现

#include <iostream>
using namespace std;
/*****************************************/
// 参数:
//        numbers:     一个整数数组
//        length:      数组的长度
// 返回值:             
//        数组中的一个重复的数字
/*****************************************/
class Solution {
public:
	bool duplicate(int numbers[], int length)
	{
		if (numbers == nullptr || length <= 0)
		{
			cout << "数组为空!" << endl;
			return false;
		}
		for (int i = 0; i < length; i++)
		{
			if (numbers[i]<0 || numbers[i]>length)
			{
				cout << "数组中元素超出范围!" << endl;
				return false;
			}
		}
		for (int i = 0; i < length; i++)
		{
			while (numbers[i] != i)
			{
				if (numbers[i] == numbers[numbers[i]])
				{
					cout << numbers[i] <<endl;
					return true;
				}
				else
				{
					int temp = numbers[i];
					numbers[i] = numbers[temp];
					numbers[temp] = temp;
				}
			}
		}
		return false;
	}
};
Solution solution;
int main()
{
	int numbers[7] = { 2,3,1,0,2,5,3};
	//int *numbers = nullptr;
	int length = 7;
	solution.duplicate(numbers, length);
	return 0;
}
输出:2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值