剑指offer(java)—— 数组

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

题目:在一个长度为n的数组里的所有数字都在 0到 n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为 7的数组 {2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字 2。
分析
1、采用简单的方法,将输入的数组排序,从头扫描排序的数组,但是对一个长度为n的数组进行排序,时间复杂度为O(nlogn)。
2、利用哈希表存储数字,如果哈希表里还没有这个数字,就将其加入哈希表;有的话就找到一个重复的数字。这个算法的时间复杂度为O(n),但是空间复杂度为O(n)。
3、所有的数字都是在 0到 n-1的范围内,如果这个数组中没有重复的数字,那么当是数组排序之后数字 i 将出现在下标为 i的位置。由于数组中有重复的数
字,有些位置可能存在多个数字,同时有些位置可能没有数字。
从头到尾依次扫描这个数组中的每个数字。当扫描到下标为 i 的数字时,首先比较这个数字(用 m表示)是不是等于 i。如果是,接着扫描下一个数字。如果不是,再拿它和第 m个数字进行比较 。如果它和第 m个数字相等,就找到了一个重复的数字。如果它和第 m个数字不相等,就把第 i 个数字和第 m 个数字交换,把 m 放到属于它的位置。接下来再重复比较、交换的过程,直到发现重复的数字。
空间复杂度为O(1)。

代码实现

    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
public class Solution{
	public boolean duplicate(int numbers[], int length,int[] duplication) {
	   	if(numbers == null || length == 0 ) {
			return false;
		}
		for(int i = 0; i < length - 1; i++) {
			if(numbers[i] < 0 || numbers[i] > length - 1) {
				return false;
			}
			while(numbers[i] != i) {
				//如果第 i个数字 m 和第 m 个数字相等,则找到重复的数字
				if(numbers[i] == numbers[numbers[i]]) {
					duplication[0] = numbers[i];
					return true;
				}else {
					int temp = numbers[i];
					numbers[i] = numbers[numbers[i]];
					numbers[numbers[i]] = temp;
	 			}
			}
		}
		return false;
	}
}

扩展:在长度为n+1的数组里的所有数字都都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为 8的数组 {2,3,5,4,3,2,6,7},那么对应的输出是重复的数字 2 或 3。
分析如果不能修改数组中的数字,可以借助一个长度为n+1的数组,然后逐一把原数组的每个数字复制到辅助数组中下标为m的位置,但这需要O(n)的辅助空间。
将1 ~ n 的数字从中间的数字m分为两部分,前一半为1 ~ m,后面一半为m+1 ~ n。如果1 ~ m 的数字在整个数组中的个数超过m,那么这一半的区间里一定包含重复的数字;否则,另一半m+1 ~ n的区间里一定包含重复的数字,我们可以继续把包含重复数字的区间一分为二,知道找到一个重复的数字,类似于二分查找。

代码实现

public class Solution{
   public static int getDuplication(int[] numbers, int length) {  
      if(numbers == null || length == 0) {  
         return -1;
      }
      int start = 1;      //1
      int end = length - 1;     //n                                                                                                                   
      while(start <= end) {
           int mid = (end - start)/2 + start;     //1~n的中间数
           //计算1~mid的数字在这个数组中出现的次数 
           int count = countRange(numbers, length, start, mid);  
           if(end == start) {
               if(count > 1)  return start;
               else break;
           }
           if(count > (mid - start + 1)) end = mid;  //重复数字在前一半
           else start = mid + 1;   //重复数字在后一半
      }
      return -1;
   }
   public static int countRange(int[] numbers, int length, int start, int end) {
       if(numbers == null) return 0;
       int count = 0;
       for(int i = 0; i < length - 1; i++) {
           if(numbers[i] >= start && numbers[i] <= end) {
               count++;
           }
       }
       return count;
   }

}

但是该方法不能保证找出所有的重复的数字,比如,不能找出 {2,3,5,4,3,2,6,7}中的重复数字2。

面试题4:二维数组中的查找

题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
分析:首先选取数值中右上角的数字。如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的;如果该数字小于要查找的数字,则剔除这个数字所在的

代码实现

public class Solution{
	public boolean Find(int target, int[][] array) {
		boolean flag = false;
		
		int rows = array.length;
		int columns = arrat[0].length;

		int row = 0;
		int column = columns - 1;
		while(row < rows && column >= 0) {
			if(array[row][column] == target) {
				flag = true;
				break;
			}else if(array[row][colunm] > target) {
				column--;   //去除列
			}else {
				row++;     //去除行
			}
		} 
	return flag;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值