【剑指offer】旋转数组中的最小值

题目总结:

1.若没有进行旋转,或者说旋转后的效果跟没有旋转是一样的,那么index1指示的值小于index2指示的值,返回index1的值。

2.若是一般性的旋转,那么最小的值旋转后肯定在中间,那么我们就可以从两边向中间夹逼。

3.夹逼的过程中,若 [ index1, middle ] 是有序的,说明这部分子区间没被破坏,旋转所移动的元素都在middle 的后面,那么最小值可定也在后面的部分,令 index1 = middle,继续向后夹逼;同理,若 [ middle ,index2 ] 是有序的,说明这部分子区间也没被最小值破坏,即最小值也没在这里,而应该在前部分,令 index2 = middle,继续向前夹逼,这样两边向中间的收缩,就把最小值确定了。

4.书中说道的 左,中,右相等的时候,无从判断最小值在哪一部分,但可以肯定的是在[ index1, index2] 中,遍历。

int MinInOrder(const vector<int>& nums, int begin, int end){
	assert(nums.size() > 0);
	int value_min = nums[begin];
	while(++begin <= end)
	{
		if(nums[begin] < value_min)
			value_min = nums[begin];
	}
	return value_min;
}

int Min(const vector<int>& nums){
	assert(nums.size() > 0);
	int index1 = 0;
	int index2 = nums.size() - 1;
	int middle = index1;
	//if index is still in the front part
	while (nums[index1] >= nums[index2])
	{
		if(index2 - index1 == 1){
			middle = index2;
			break;
		}

		middle = (index1 + index2) >> 1;
		//can‘t make sure which part the middle belong to.
		if(nums[middle] == nums[index1] &&
			nums[middle] == nums[index2])
			return MinInOrder(nums, index1, index2);
		//the front part is in order
		else if(nums[middle] >= nums[index1])
			index1 = middle;
		//the back part is in order.
		else if(nums[index2] >= nums[middle])
			index2 = middle;
	}
	return nums[middle];
}

除了找最小值,还可以找最大值,思路和这一样,就是找两个区间的断裂处。

这里给定的是一个经过选择的数组,我们试着自己实现下这种旋转。

函数接口如下:

void RotaArray(vector<int>& nums, int roteNums)

nums:给定的要旋转的数组。

roteNums:表示数组的前 roteNums 个数移动到数组的后面。

分析:

我们开始将旋转的两部分视为两个元素,那么目标就是交换着两个元素的位置,看来是要用到交换。

比如 1, 2, 3, 4, 5,将前两个交换到后面,变成 3, 4, 5, 1, 2.

我们首先不管其他交换一下看看。

首尾交换我们得到:5,4,3,2,1.

虽然 1,2 确实到了数组的后面,但是 5,4,3,区间 和 2,1区间是反的。但再看之下,我们发现,在把两个区间各自内部交换下就行了。

5,4,3交换,得到:3, 4, 5

2,1交换,得到:1, 2.

这样就得到:3 ,4 ,5 ,1, 2.

当然先对子区间内部交换再整体交换也是一样的。

void Swap(int& a, int& b){
	a ^= b;
	b ^= a;
	a ^= b;
}

void Reverse(vector<int>& nums, int begin, int end){
	if(begin < 0 || end > nums.size())
		return;
	while (begin < end)
	{
		Swap(nums[begin++], nums[end--]);
	}

}
void RotaArray(vector<int>& nums, int roteNums){

	if(nums.size() <= 1 || roteNums == 0 || roteNums >= nums.size())
		return;
	Reverse(nums, 0, roteNums - 1);
	Reverse(nums, roteNums, nums.size() - 1);
	Reverse(nums, 0, nums.size() - 1);
}

【剑指offer】旋转数组中的最小值,布布扣,bubuko.com

时间: 2024-12-26 09:57:08

【剑指offer】旋转数组中的最小值的相关文章

剑指offer (36) 数组中的逆序对

题目:在数组中的两个数字如果前面一个数字大于后面一个数字,则这两个数字组成一个逆序对 题解分析: 首先应该想到很简单的一种解法,顺序遍历数组,对每个数,逐个比较该数字和其以后的数字,T(n) = O(n^2) (1)总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量 count += Merge(data, temp, first, mid);//找左半段的逆序对数目 count += Merge(data, temp, mid + 1, e

【剑指offer】数组中只出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 输入: 每个测试案例包括两行: 第一行包含一个整数n,表示数组大小.2<=n <= 10^6. 第二行包含n个整数,表示数组元素,元素均为int. 输出: 对应每个测试案例,输出数组中只出现一次的两个数.输出的数字从小到大的顺序. 样例输入: 8 2 4 3 6 3

【剑指offer】数组中的逆序对

# @left part: [start, mid] # @right part: (mid, end] def merge(data, start, mid, end): if mid < start or end < mid: return 0 reverse = 0 ''' @ for start, it play as the start index of left part, and mid @ play as the end index of left part; @ mid +

剑指OFFER之数组中出现次数超过一半的数字(九度OJ1370)

题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 输入: 每个测试案例包括2行: 第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数. 第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]. 输出: 对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1. 样例输入:

剑指offer (29) 数组中出现次数超过一半或1/3或1/N的数字

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字 方法一:如果把这个数字排序,那么排序之后位于数组中间的数字一定就是出现次数超过数组长度一半的数字 这个数字就是统计学中的中位数,即长度为n的数组中第n/2大的数字 在数组中得到任意第k大数字,这一问题有O(n)解,注:这里第kth个元素,kth从1开始计数,并且重复元素不去重 (1) 直接sort排序,然后定位到索引为kth-1的元素 int FindKth1(std::vector<int>& num, int kt

【剑指offer】数组中仅仅出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描写叙述: 一个整型数组里除了两个数字之外,其它的数字都出现了两次.请敲代码找出这两个仅仅出现一次的数字. 输入: 每一个測试案例包括两行: 第一行包括一个整数n,表示数组大小.2<=n <= 10^6. 第二行包括n个整数,表示数组元素,元素均为int. 输出: 相应每一个測试案例.输出数组中仅仅出现一次的两个数.输出的数字从小到大的顺序. 例子输入: 8 2 4

[剑指Offer]40.数组中只出现一次的数字

题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路 我们直到异或的性质: 任何一个数字异或他自己都等于0. 所以说我们如果从头到尾依次异或每一个数字,那么最终的结果刚好只出现一次的数字,因为成对出现的两次的数字全部在异或中抵消了. 这道题中有两个数字只出现一次.这样的话我们得到的结果就是这两个数字的异或结果.因此我们想办法把原数组分成两个子数组,使得每个子数组包含一个只出现一次的数字.这样我们就可以对这两个子数组分别异或,就能得到两个只出现一

【Java】 剑指offer(39) 数组中出现次数超过一半的数字

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 思路 思路一:数字次数超过一半,则说明:排序之后数组中间的数字一定就是所求的数字. 利用partition()函数获得某一随机数字,其余数字按大小排在该数字的左右.若该

【Java】 剑指offer(51)数组中的逆序对

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 思路 如果遍历数组,对每个数字都和后面的数字比较大小,时间复杂度为O(n^2),效率太低. 利用归并排序的思想,先将数组分解成为n个长度为1的子数组,然后进行两两合并同时排好顺序. 在对两个子区域合并排序时,记左边区域(下标为start~mid)的指针

剑指offer—旋转数组的最小数字

题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组(3,4,5,1,2)位{1,2,3,4,5}的一个旋转,该数组的最小值为1. 基本思想: 二分查找,p1指向a[0],p2指向a[len-1].如果a[mid]>a[p1],则最小值在后半段,p1=mid:如果a[mid]<a[p2],则最小值在前半段,p2=mid. 特殊情况: 注意:在这两个数组中,第一个数字.最后一个数字和中间数字都是1,我们无法