剑指offer-面试题51-数组中重复的数字

package case51_FindDuplicateNumbers;

import java.util.HashMap;
import java.util.HashSet;

/**
 * 题目:在一个长度为n的数组中的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
 * 请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
 * 
 * @author WangSai
 *
 */
public class FindDuplicateNumbers {

	/**
	 * 方法1:根据map集合实现。遍历arr[],把遍历到的数字作为map的键,若map键不包含,则不重复;若有该键,说明重复
	 * 时间复杂度O(n),空间复杂度O(n)
	 * 
	 * @param arr,要被遍历的数组
	 */
	private void findTheNumbers(int[] arr) {
		// 异常值检测
		if (arr == null || arr.length <= 0)
			throw new IllegalArgumentException("非法的输入参数,请重新检查");
		// 遍历arr[],把遍历到的数字作为map的键,若map键不包含,则不重复;若有该键,说明重复。
		HashMap<Integer, Integer> hp = new HashMap<>();
		for (Integer key : arr) {
			if (!hp.containsKey(key))
				hp.put(key, 1);
			else
				hp.put(key, hp.get(key) + 1);
		}
		// 遍历hp的键set集合,把>1的value的key保存到set中
		HashSet<Integer> hs = new HashSet<>();
		for (Integer key : hp.keySet()) {
			int value = hp.get(key);
			if (value > 1)
				hs.add(key);

		}
		System.out.println(hs.toString());

	}

	/**
	 * 方法2:在原数组的基础上,通过判断当前数字是否符合当前的位置判断。
	 * 
	 * 我们注意到数组中的数字都在0到n-1中。 如果这个数组中没有重复的数字,那么当数组排序之后数字i将出现在下标为i的位置。由于数组中有重复的
	 * 数字,有些位置可能存在多个数字,同时有些位置可 能没有数字。 现在让我们 重排这个数组,依然从头到尾一次扫描这个数组中的每个数字。当扫描到
	 * 下标为i的数字时,首先比较这个数字(用m表示)是不是等于i。如果是,接着扫描下一个数字。如果不是,再拿它和第m个数字进行比较。 如果它和第
	 * m个数字相等,就找到了一个 重复的 数字(该数字在下标为i和m的位置都出现了)。如果它和第m个数字不相等,就把第i个数字和第m个数字交换,把m
	 * 放到属于它 的位 置。接下来再重读这个比较、交换的过程,直到我们发现一 个重复的数字。
	 * 
	 * 时间复杂度O(n),空间复杂度O(1)
	 * 
	 * @param arr,要寻找的数组
	 * @return true,arr数组有重复的数字
	 */
	private boolean findTheNumbers2(int[] arr) {
		// 异常值检测
		if (arr == null || arr.length <= 0)
			throw new IllegalArgumentException("非法参数,请重新检查...");
		// 数组是否有重复的数字
		boolean moreThanOne = false;
		// 保存重复的数字
		HashSet<Integer> hs = new HashSet<>();
		// 遍历arr
		for (int i = 0; i < arr.length; i++) {
			// 判断当前的数arr[i]是否应该在当前的数组角标上i
			while (arr[i] != i) {
				// 判断以arr[i]为角标的数arr[arr[i]]是否应该在当前的数组角标上arr[i]上。
				// arr[arr[i]]不属于角标arr[i],把arr[i]放到角标arr[i]上
				if (arr[arr[i]] != arr[i]) {
					int temp = arr[arr[i]];
					arr[arr[i]] = arr[i];
					arr[i] = temp;
				}
				// arr[i]和角标arr[i]上的数字arr[arr[i]],即存在相同的数字。
				// 标志位设置为true
				// 跳出当前循环,判断下一个位置,即i+1的值
				else {
					moreThanOne = true;
					// 把重复的值保存到set中
					hs.add(arr[i]);
					break;
				}
			}
		}
		System.out.println(hs.toString());
		return moreThanOne;
	}

	public static void main(String[] args) {
		System.out.println("test1() Found:");
		test1();
		System.out.println("test2() Found:");
		test2();
		System.out.println("test3() Found:");
		test3();
	}

	// 数组中没有重复元素 arr[]={0,1,2,3,4,5}
	private static void test1() {
		int[] arr = { 0, 1, 2, 3, 4, 5 };
		FindDuplicateNumbers fdn = new FindDuplicateNumbers();
		System.out.print("findTheNumbers:   ");
		fdn.findTheNumbers(arr);
		System.out.print("findTheNumbers2:   ");
		fdn.findTheNumbers2(arr);

	}

	// 数组中有重复元素 arr[]={1,2,2,4,4}
	private static void test2() {
		int[] arr = { 1, 2, 2, 4, 4 };
		FindDuplicateNumbers fdn = new FindDuplicateNumbers();
		System.out.print("findTheNumbers:   ");
		fdn.findTheNumbers(arr);
		System.out.print("findTheNumbers2:   ");
		fdn.findTheNumbers2(arr);
	}

	// 数组中有重复元素 arr[]={1,1,1}
	private static void test3() {
		int[] arr = { 1, 1, 1 };
		FindDuplicateNumbers fdn = new FindDuplicateNumbers();
		System.out.print("findTheNumbers:   ");
		fdn.findTheNumbers(arr);
		System.out.print("findTheNumbers2:   ");
		fdn.findTheNumbers2(arr);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值