循环有序数组的正确解法

 一个循环有序数组([7,7,8,9,1,2,3,6,7,7,7]),不知道其最大值的位置,要查找任一数值的位置。要求算法时间复杂度为log2(n)。

解这道题的有两种:

1. 找到分界点,将数组分为两段, 然后用有序数组的二分查找方法。

2. 直接二分查查找法思想就是,比较a[mid], a[end], k三者的大小,来确定下次查找是在mid的右边还是左边。 看了网上很多的解法,很多都没有处理好a[mid] == a[end]时情况,导致当数组为:{7,7,7,7,7,7,7,7,9,12,1,7}  或者{7,7,9,1,2,3,7,7,7,7,7,7,7,7} 的时候,测试不通过。

以下代码经过多次测试,验证无误。欢迎各位同仁多多指证。

#include <stdio.h>
//查找分界点,数组中最大元素的位置
int find_max(int *a, int len)
{
	if(len == 1)
	{
		return 0;
	}
	int left = 0;
	int right = len - 1;
	int mid;
	while(left<=right)
	{
		mid = (left + right) / 2;
		printf("left=%d, right=%d, mid=%d\n", left, right,mid);
		if(a[mid] > a[mid+1])
		{
			return mid;
		}
		else if(a[mid] > a[len-1])
		{
			left = mid + 1;
		}
		else if(a[mid] < a[len-1])
		{
			right = mid - 1;
		}
		else //a[mid]=a[len-1]时,无法直接判断 比如:[7,7,7,7,7,7,8,1,7]          [8,9,1,7,7,7,7,7,7]
		{
			int i = mid + 1;
			int flag = 0;
			while(i<=right)
			{
				if(a[i] > a[len-1])
				{
					flag = 1;
					break;
				}
				++i;
			}
			if(flag == 1) // [mid+1,right] 区间内有大于末尾元素的值
			{
				left = i;
			}
			else
			{
				right = mid - 1;
			}
		}
	}
	return len-1;
}

int find(int *a, int len, int k)
{
	int left = 0;
	int right = len - 1;
	int mid  = -1;
	while(left<=right)
	{
		mid = (left + right) / 2;
		//printf("left=%d, right=%d, mid=%d, val_mid=%d, val_right=%d,  ",left, right, mid, a[mid], a[right]);
		int val_mid = a[mid];
		int val_end = a[len - 1]; // 也可以 int val_end = a[right], 不过性能应该要差点
		if(val_mid == k)
		{
			return mid;
		}
		else if(val_mid > k)
		{
			if(val_mid > val_end)
			{
				if(val_end >= k)
				{
					left = mid + 1;
				}
				else
				{
					right  = mid - 1;
				}
			}
			else if(val_mid < val_end)
			{
				right = mid - 1;
			}
			else
			{
				int i = mid + 1;
				int flag = 0;
				while(i<=right)
				{
					if(a[i] < val_mid)
					{
						flag = 1;
						break;
					}	
					++i;
				}
				if(flag == 1)
				{
					left = i;
				}
				else
				{
					right = mid - 1;
				}
			}
		}
		else
		{
			if(val_mid < val_end)
			{
				if(val_end >= k)
				{
					left = mid + 1;
				}
				else
				{
					right = mid - 1;
				}
			}
			else if(val_mid > val_end)
			{
				left = mid + 1;
			}
			else
			{
				int i = mid + 1;
				int flag = 0;
				while(i<=right)
				{
					if(a[i] > val_mid)
					{
						flag = 1;
						break;
					}	
					++i;
				}
				if(flag == 1)
				{
					left = i;
				}
				else
				{
					right = mid - 1;
				}
			}
		}
	}
	return -1;
}
int main ()
{
	//int a[] = {7,7,7,7,7,7,7,7,9,12,1,7};
	int a[] = {7,7,9,1,2,3,7,7,7,7,7,7,7,7};
	//int a[] = {1,2,3,4,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7};
	int len = sizeof(a) / sizeof(int);
	int k =1;
	int index = find(a, len ,k);
	if(index == -1)
	{
		printf("not find\n");
	}
	else
	{
		printf("len=%d, index=%d, a[index]=%d\n", len, index, a[index]);
	}
	return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值