【劍指offer】2.3.1 數組

面試題3:數組中重複的數字

題目一:找出數組中重複的數字。

    在一個長度爲n的數組裏的所有數字都在0~n-1的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。例如:如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者3。

解答:方法1:先對輸入的數組排序,然後從頭開始掃描數組,找到重複數字。時間複雜度:O(nlogn)。代碼如下:

int Partition(int *numbers,int left,int right)
{
	int tmp = numbers[left];
	while(left < right)
	{
		while(left < right && numbers[right] > tmp)
		{
			right--;
		}
		if(left < right)
		{
			numbers[left++] = numbers[right];
		}
		else
		{
			break;
		}
		while(left < right && numbers[left] < tmp)
		{
			left++;
		}
		if(left < right)
		{
			numbers[right--] = numbers[left];
		}
		else
		{
			break;
		}
	}
	numbers[left] = tmp;

	return left;
}

//快速排序
void quickSort(int *numbers,int left,int right)
{
	if(NULL == numbers || left == right)
	{
		return;
	}

	int sit = Partition(numbers,left,right);
	if(left + 1 < sit)
	{
		quickSort(numbers,left,sit-1);
	}
	if(sit + 1 < right)
	{
		quickSort(numbers,sit+1,right);
	}
}

//是否存在重複數字
bool duplicate(int numbers[], int length, int* duplication) 
{
    if(NULL == numbers || NULL == duplication || length <= 0)
	{
		return false;
	}

	int i = 0;
	for(;i < length;i++)
	{
		if(numbers[i] < 0 || numbers[i] > (length - 1))
		{
			return false;
		}
	}

	quickSort(numbers,0,length-1);

	for(i = 0;i < (length - 1);i++)
	{
		if(numbers[i] == numbers[i+1])
		{
			*duplication = numbers[i];
			return true;
		}
	}

	return false;
}

    方法2:建立哈希表。從頭到尾掃描數組,分別判斷每一個數字是否在哈希表中,如果不在就加入哈希表,如果在就找到了一個重複數字。時間複雜度:O(n),空間複雜度:O(n)。代碼如下:

bool duplicate(int numbers[], int length, int* duplication)
{
	if(NULL == numbers || length <= 0)
	{
		return false;
	}

	bool *tmpArr = (bool *)malloc(length);
	if(NULL == tmpArr)
	{
		return false;
	}

	int i = 0;
	for(;i < length;i++)
	{
		if(numbers[i] < 0 || numbers[i] > (length - 1))
		{
			return false;
		}
	}

	for(i = 0;i < length;i++)
	{
		tmpArr[i] = 0;
	}

	for(i = 0;i < length;i++)
	{
		if(tmpArr[numbers[i]])
		{
			*duplication = numbers[i];
			return true;
		}

		tmpArr[numbers[i]] = 1;
	}

	return false;
}

    方法3:重排數組。從頭到尾掃描數組,當掃描到下標爲i的數字時,比較這個數字m是否等於i。如果是則繼續掃描下一個,如果不是就拿它和第m個數字比較,如果相等就找到了一個重複的數字,如果不等則交換。直到發現一個重複的數字。

bool duplicate(int numbers[], int length, int* duplication)
{
	if(NULL == numbers || length <= 0)
	{
		return false;
	}

	int i = 0;
	for(;i < length;i++)
	{
		if(numbers[i] < 0 || numbers[i] > (length - 1))
		{
			return false;
		}
	}

	for(i = 0;i < length;)
	{
		if(i == numbers[i])
		{
			i++;
		}
		else if(numbers[i] == numbers[numbers[i]])
		{
			*duplication = numbers[i];
			return true;
		}
		else
		{
			int tmp = numbers[i];
			numbers[i] = numbers[tmp];
			numbers[tmp] = tmp;
		}
	}
	return false;
}

題目二:不修改數組找出重複的數字。

在一個長度爲n+1的數組裏的所有數字都在1~n的範圍內,所以數組中至少有一個數字是重複的。請找出數組中任意一個重複的數字,但不能修改輸入的數組。例如,如果輸入長度爲8的數組{2,3,5,4,3,2,6,7},那麼對應的輸出是重複的數字2或者3。

解答:可以把從1~n的數字從中間的數字m分成兩部分,前面一半爲1~m,後面一半爲m+1~n。如果1~m的數字的數目超過m,那麼這一半的區間裏一定包含重複的數字;否則,另一半區間一定包含重複的數字。繼續把包含重複數字的區間一分爲二,直到找到一個重複的數字。

    以長度爲8的數組{2,3,5,4,3,2,6,7}爲例分析查找的過程。根據題目要求,這個長度爲8的所有數組都在1~7的範圍內。中間的數字4把1~7分爲兩段,一段是1~4,另一段是5~7。接下來統計1~4這四個數字在數組中出現的次數,他們一共出現了5次,因此這4個數字中一定有重複的數字。接下來再把1~4的範圍一分爲二,一段是1、2兩個數字,另一段是3、4兩個數字。數字1和2一共出現了2次,數字3和4一共出現了3次,這意味着3、4中一定有一個數字重複了。再分別統計這兩個數字出現的次數,發現數字3出現了兩次。

int countRange(const int *numbers,int length,int left,int right)
{
	if(NULL  == numbers || length <= 0 || left > right || left < 1 || right > length-1)
	{
		return -1;
	}

	int count = 0;
	for(int i = 0;i < length;i++)
	{
		if(numbers[i] >= left || numbers[i] <= right)
		{
			count++;
		}
	}

	return count;
}

int getDuplication(const int* numbers, int length)
{
	if(NULL  == numbers || length <= 0)
	{
		return -1;
	}

	int i = 0;
	for(;i < length;i++)
	{
		if(numbers[i] < 1 || numbers[i] > length-1)
		{
			return -1;
		}
	}

	int left = 1;
	int right = length - 1;
	while(left <= right)
	{
		int mid = ((right - left) >> 1) + left;
		int count = countRange(numbers,length,left,mid);
		if(left == right)
		{
			if(count > 1)
			{
				return left;
			}
			else
			{
				break;
			}
		}
		if(count > (mid-left+1))
		{
			right = mid;
		}
		else
		{
			left = mid + 1;
		}
	}

	return -1;
}

上述代碼按照二分查找的思路,如果輸入長度爲n的數組,那麼函數countRange將被調用O(logn)次,每次需要O(n)的時間,因此總的時間複雜度爲O(nlogn),空間複雜度爲O(1)。需要指出的是,這種算法不能找出所有重複的數字。

面試題4:二位數組中的查找

題目:在一個二維數組中,每一行都按照從左到右遞增的順序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入

這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

解答:首先選取數組中右上角的數字,如果該數字等於要查找的數字,則查找過程結束;如果該數字大於要查找的數字,則剔除

這個數字所在的列;如果該數字小於要查找的數字,則剔除這個數字所在的行。也就是說,如果要查找的數字不在數組的右上

角,則每一次都在數組的查找範圍中剔除一行或者一列,這樣每一步都可以縮小查找範圍,直到找到要查找的數字。代碼如下:

bool Find(int target, vector<vector<int> > array)
{
    bool res = false;

	if(array.size() == 0)
	{
		return res;
	}

	int row = array.size() - 1;
	int col = array[0].size() - 1;

	int i = 0;
	int j = col;

	while(i <= row && j >= 0)
	{
		if(array[i][j] == target)
		{
			res = true;
			break;
		}

		else if(array[i][j] > target)
		{
			j--;
		}

		else
		{
			i++;
		}
	}

	return res;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章