有序数组旋转的问题

1、有序数组旋转后寻找数组中的最小值

一般情况下我们的第一反应肯定是遍历此数组,但这肯定不是我们所期望的,因为此数组是有序数组旋转而成,所以我们此时应该想到用二分法来寻找数组中的最小值。假设我们给定递增数组{0,1,2,3,4,5,6,7,8,9}

旋转后的数组有下面几种形式

{6,7,8,9,0,1,2,3,4,5}

{9,0,1,2,3,4,5,6,7,8}
{0,1,2,3,4,5,6,7,8,9}
{5,6,7,8,9,0,1,2,3,4}
通过观察可以发现,在经过旋转后的数组在寻找最小值的过程中0的前面的数总是大于0后面的数,所以我们可以以此为条件,在将范围缩小为2个数时,后面的数就是最小值,根据此思路代码如下:

<span style="font-family:Microsoft YaHei;font-size:14px;">int MinArray(int* a, int n)
{
	assert(a != NULL || n <= 0);
	int start = 0;
	int end = n - 1;
	int mid = start;
	while (a[start] >= a[end])  //确定数组经过旋转已经不是有序数组
	{
		if (end - start == 1) //当只剩两个数时直接返回较小值的下标
		{
			mid = end;
			break;
		}
		mid = (start + end) / 2;
		//如果a[start],a[mid],a[end]相等时,只能遍历寻找最小值
		if (a[mid] == a[start] && a[mid] == a[end])
		{
			int min = a[start];
			while (start <= end)
			{
				if (a[start] < min)
				{
					mid = a[start];
				}
				++start;
			}
			return min;
		}
		if (a[mid] >= a[start])
		{
			start = mid;
		}
		else if (a[mid] <= a[end])
		{
			end = mid;
		}
	}
	return a[mid];
}</span>


上面的程序已经将一种特殊情况给处理了,例如下面这种例子
{1,0,1,1,1}
{1,1,1,0,1}
此时若直接进行比较,将会出现将最小值跳过的情况,所以我们在此种情况下只能采取遍历寻找最小值。
我们也可以使用下标的方式来实现寻找最小值,思路和上面的程序一样,代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;">int MinArray2(int* a, int n)
{
	assert(a != NULL || n <= 0);
	int start = 0;
	int end = n - 1;
	while (start < (start+end)/2)  
	{
		int mid = (start + end) / 2;
		//如果a[start],a[mid],a[end]相等时,只能遍历寻找最小值
		if (a[mid] == a[start] && a[mid] == a[end])
		{
			int min = a[start];
			while (start <= end)
			{
				if (a[start] < min)
				{
					mid = a[start];
				}
				++start;
			}
			return min;
		}
		if (a[mid] >= a[start] && a[mid] <= a[end])  //此时数组有序,直接返回数组下标0
		{
			return 0;
		}
		if (a[mid] >= a[start])
		{
			start = mid;
		}
		else if (a[mid] <= a[end])
		{
			end = mid;
		}
	}
	if (a[start] > a[end])
	{
		return a[end];
	}
	return a[start];
}</span>

上面是求去旋转有序数组的最小值,求最大值的思路和最小值的一样,只需要在循环结束返回时返回那个较大值即可。

2、有序数组旋转后给定数值返回此数值在数组中的下标

实现在一个旋转有序数组中寻找一个数并返回下标,很多人的第一反应肯定是直接遍历,但这样的话有序数组旋转的特性没有得到应用,所以我们需要分析旋转有序数组的特点,例如下面旋转有序数组
{5,6,7,8,9,0,1,2,3,4}
{6,7,8,9,0,1,2,3,4,5}
{0,1,2,3,4,5,,6,7,8,9}
通过观察上面的数组我们可以发现无论数组如何旋转,数组分成两个子数组时,一个肯定是有序的,所以我们可以利用此特性来实现寻找一个特定的数,每次将给定数值通过和有序数组的首尾两个值对比,来判断此数是否存在在这个数组中,如果在的话,将此数组递归,若不在,将另一个数组递归,重复此过程,直至首尾元素下标相同,或寻找到此给定数值。
依据上面的思路实现的代码如下:
<span style="font-family:Microsoft YaHei;font-size:14px;">int Search(int* a, int n, int x)
{
	assert(a != NULL || x <= 0);
	int start = 0;
	int end = n - 1;
	while (start < end)
	{
		int mid = (start + end) / 2;
		if (a[mid] == x)
		{
			return mid;
		}
		else if (a[mid] > a[start])
		{
			if (x <= a[mid] && x >= a[start])
			{
				end = mid;
			}
			else
			{
				start = mid;
			}
		}
		else if (a[end] > a[mid])
		{
			if (x >= a[mid] && x <= a[end])
			{
				start = mid;
			}
			else
			{
				end = mid;
			}
		}
		else
		{
			++start; //处理数组中有重复的数字
		}
	}
	return -1;
}</span>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值