剑指offer第二版——面试题3(java)

面试题3:数组中重复的数字

考点:1. 一维数组   2. 二分查找

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

1. 先对数组进行排序,再从头到尾扫描数组。排序时间复杂度为O(nlogn)

2. 用一个数组来构造简单的哈希表,空间复杂度为O(n)

3. 从头到尾依次扫描数组中的每个数字。当扫描到下标为i的数字m时,如果m=i,则继续扫描下一个数字;如果m!=i,则将m与第m个数字进行比较,如果不同,则将m放到第m个位置上;若m等于第m个数字,则m为重复数字。时间复杂度O(n),空间复杂度O(1)

// 方法一:快排+遍历
// 注意当没有重复数字时的输出 
class Q3 {
	public static void main(String[] args) {
		// 不含重复数字
		int[] intList = new int[] {4,3,3,1,5,2,6,7,9,8};
		//数组排序
		Qsort(intList, 0, intList.length-1);
		showList(intList);
		//遍历输出重复数字
		int flag = 0;
		for(int i =0;i<intList.length-1;i++) {
			if (intList[i]==intList[i+1]){
				flag = 1;
				System.out.println(intList[i]);
			}
		}
		if(flag==0) {
			System.out.println(-1);
		}
	} 
	
	
	public static void Qsort(int[] L,int left,int right) {
		int loc;
		if(left<right) {
			loc = moveElement(L, left, right);
			// loc位置为正确位置,不用再挪动
			Qsort(L, left, loc-1);
			Qsort(L, loc+1, right);
		}
	}
	
	public static int moveElement(int[] L,int left,int right) {
		int low = left;
		while(left<right) {
			while(L[right]>=L[low] && left<right) {
				right--;
			}
			while(L[left]<=L[low] && left<right) {
				left++;
			}
			if(L[left]>L[low] && L[right]<L[low]) {
				swap(L, left, right);	
			}
		}
		swap(L, low, left);
		return left;
	}
	public static void showList(int[] L) {
		for (int i = 0; i < L.length; i++) {
			System.out.print(String.valueOf(L[i]) + ' ');
		}
		System.out.println();
	}
	
	public static void swap(int[] L,int i,int j) {
			int temp = L[i];
			L[i] = L[j];
			L[j] = temp;
		}
}  
//方法二
//注意:
//1.检查是否输入的数组包含0~n-1之外的数字(不检查会数组超界)
//2.没有重复数字时的输出

class Q3 {
	public static void main(String[] args) {
		// 不含重复数字
		int[] intList = new int[] {1,2,3,4,5,6,7,8};
		int solution = Solution(intList);
		System.out.println(solution);
		
		// 无效的测试用例:长度为n的数组中包含0~n-1之外的数字
		intList = new int[] {1,10,11};
		solution = Solution(intList);
		System.out.println(solution);
		
		// 正常测试用例
		intList = new int[] {1,2,4,4,4,5,6};
		solution = Solution(intList);
		System.out.println(solution);
	}  
	
	public static int Solution(int[] intList) {
		// 识别空
		if (intList.length == 0) {
			return -1;
		}
		//用数组来实现简单的哈希
		int[] hashList = new int[intList.length];
		
		for(int i=0;i<intList.length;i++) {
			// 检查是否输入的数组包含0~n-1之外的数字,包含则跳出
			if(intList[i]>hashList.length-1) {
				return -1;
			}
			if(hashList[intList[i]]==0) {
				hashList[intList[i]]++;
			}else {
				return intList[i];
			}
		}
		return -1;
	}
} 
// 方法三
// 注意:是否有超界数字
class Q3 {
	public static void main(String[] args) {
		// 不含重复数字
		int[] intList = new int[] {4,11,3,1,5,2,6,7,9,11};
		int num = findN(intList);
		System.out.println(num);
	}  
	

	
	public static int findN(int[] L) {
		for(int i=0;i<L.length;i++) {	
			if(L[i]!=i) {
				while(L[i]!=i) {
					if(L[i]!=L[L[i]]) {
						swap(L, i, L[i]);
					}else {
						return L[i];
					}
					if(L[i]>L.length-1) {
						return -2;   //包含0~n-1以外的数字
					}
				}
			}
		}
		return -1; //无重复数字
	}
	
	
	public static void swap(int[] L,int i,int j) {
			int temp = L[i];
			L[i] = L[j];
			L[j] = temp;
		}
	
}  

题目二:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

1. 构造长度为n+1的辅助数组,对原数组进行逐一检查

2. 按二分查找的方法,在原数组上进行查找重复数字:该算法不能找出所有的重复数字——以时间换空间

// 方法一
class Q3 {
	public static void main(String[] args) {
		// 不含重复数字
		int[] intList = new int[] {4,8,3,0,5,2,6,7,9,1};
		int fount = findN(intList);
		System.out.println(fount);
	}  
	

	
	public static int findN(int[] L) {
		int len = L.length;
		if(len==0) {
			return 0;
		}
		int[] temp = new int[len];
		for(int i=0;i<len;i++) {
			// 防止数组中有超过n+1的数
			if(L[i]>len) {
				return -2;
			}
			if(temp[L[i]]==0) {
				temp[L[i]]++;
			}else {
				return L[i];
			}
		}
        // 数组中没有重复数字
		return -1;
	}
}  
// 方法二
class Q3 {
	public static void main(String[] args) {
		// 不含重复数字
		int[] intList = new int[] {1,4,4,6,5,3,0};
		int Result = findN(intList);
		System.out.println(Result);
	}  
	
	public static int findN(int[] L) {
		// 检查为空数组
		if(L.length==0) {return -1;}
		// 检查小于1或超过n的数
		for (int i = 0; i < L.length; i++) {
	            if (L[i] < 1 || L[i] >= L.length) {
	                return -1;
	            }
		int start = 0;
		int end = L.length-1;
		int count = 0;
		while(start<=end) {
			if(start==end) {
				count = countN(L, start, end);
				if(count>1) {
					return start;
				}else {
					break;
				}
			}
			
			int mid = (start+end)/2;
			count = countN(L, start, mid);
			// 如果大于本该有的数字个数 则继续在该范围内搜索
			if(count > mid-start+1) {
				end = mid;
			}else {
				start = mid+1;
			}
		}
	}
		return -1;
}

	
	public static int countN(int[] L,int start,int end) {
		int count=0;	
		for(int i =0;i<L.length;i++) {
				if(L[i]>=start && L[i]<=end){
					count++;
				}
			}
		return count;
		}
}  

猜你喜欢

转载自blog.csdn.net/qq_22527013/article/details/88235419