二分查找|笔试题

 

 

二分查找又叫折半查找,首先它数组中的元素是有序的,他将事物的规模每次缩小一般,直至查找到目标,或者目标不存在。

一.数组中找目标值

定义一个结构体

typedef struct
{
	int index;//下标
	bool tag;//目标值是否存在数组中的标志
}Result;

 

  • 做法一:递归
Result Find(int *ar,int left,int right,int val)
{
	Result res = {-1,false};
	int mid = (right - left +1)/2 + left;
	if(left <= right)
	{
		
		if(val < ar[mid])
		{
			res = Find(ar,left,mid-1,val);
		}
		else if(val > ar[mid])
		{
			res = Find(ar,mid+1,right,val);
		}
		else
		{
			res.index = mid;
			res.tag = true;
		}
	}
	if(!res.tag && res.index == -1) 
	//如果条件改成if(!res.tag),在递归返回中最终将找不到的元素都插入到原始的mid位置,此题为4位置
	//
	{
       res.index = mid;
	}
	return res;
}
  • 拿循环写上面的那部分
Result Find(int *ar,int left,int right,int val)
{
	Result res = {-1,false};
	while(left <= right)
	{
		int mid = (right - left +1)/2+left;
		if(ar[mid] == val)
		{
			res.index = mid;
			res.tag = true;
			break;
		}
		else if(val < ar[mid] )
		{
			right = mid -1;
		}
		else
		{
			left = mid + 1;
		}
	}
	if(!res.tag)
	{
		res.index = left;///此处通过举例子可以看出,不能将left改成right
	}
	return res;
}

剩余部分的代码

Result FindValue(int *ar,int n,int val)
{
	Result res = {-1,false};
	if(ar != NULL && n> 0)
	{
		res = Find(ar,0,n-1,val);
	}
	return res;
}

int main()
{
	int arr[] = {1,3,5,7,9,12,45,67,100};
	int n = sizeof(arr)/sizeof(arr[0]);
	int val;
	while(cin>>val,val!=-1)
	{
		Result res = FindValue(arr,n,val);
		cout<

运行截图:

二分查找|笔试题_第1张图片

二.贪吃的小明

 

题目有错出差3天7块奶糖

思想:1----------------m为奶糖数,假设第一天吃了mid = m/2个奶糖,以后每天吃(mid+1)/2个奶糖,上一天的一半对上取整,计算这几天一共吃的奶糖数与m比较,若吃多了,则将规模缩小到mid的左边,否则将规模缩小到mid的右边。

int Sum(int n,int mid)
{
	int sum = 0;
	for(int i = 0;i < n;++i)
	{
		sum+=mid;
		mid = (mid+1)/2;
	}
	return sum;
}
int Get_Max(int n,int m)
{
	int left = 1,right = m;
	while(left <= right)
	{
		int mid = (right+left)/2;
		int sum = Sum(n,mid);//吃的奶糖数
		bool tag = m>nday>>m,nday != -1 && m > 0)
	{
		int maxm = Get_Max(nday,m);
		cout<

需要注意的是,奶糖够吃与否不需要与确切的值比较。当left = right时,对mid左加减1操作。

三.寻找旋转排序数组中的最小值

二分查找|笔试题_第2张图片

分析:用两个指针分别指向数组中的第一个元素和最后一个元素。接着寻找数组的中间元素,如果中间元素大于第一个指针指的元素,那么表示该元素位于前面的递增子数组,我们将第一个指针指向该元素;如果中间元素小于最后一个元素,则表示该元素位于后面的递增递增子数列,将第二个指针执行该元素。这样就可以缩小范围。当第一个指针指向前面数组的最大元素,第二个指针指向后面递增子数组的最小元素(即两个指针相邻时退出循环)。但是有一种特殊情况,就是数组一开始就是递增的,没有旋转,所以将mid初始值设为left.

int Reverse(int *ar,int n)
{
	if(NULL == ar || n < 0)
		exit(1);
	int left = 0;
	int right = n-1;
	int mid = left;//以便没有发生旋转
	while(ar[left] > ar[right])//发生了旋转
	{
		if(right - left == 1)
		{
			mid = right;
			break;
		}
		mid = (right-left+1)/2+left;
		if(ar[mid] > ar[left])//位于前面的递增子数组中
		{
			left = mid;
		}
		else
		{
			right = mid;
		}
		
	}
	return ar[mid];
}

当题目改为可以存在重复元素时,用上面的代码就有缺陷了。举个例子,数组{1,1,1,0,1};我们无法确认中间元素是属于第一还是第二个递增子数组。所以无法通过缩小范围来查找,因此需要顺序查找。

int Min(int *ar,int left,int right)//顺序查找最小数
{
	int min = ar[left];
	for(int i = left+1;i <= right;++i)
	{
		if(ar[i]= ar[right])
	{
		if(right - left == 1)
		{
			mid = right;
			break;
		}
		mid = (right-left+1)/2+left;
		if((ar[mid] == ar[left])&&(ar[mid] == ar[right]))
		{
			return Min(ar,left,right);
		}
		if(ar[mid] >= ar[left])//位于前面的递增子数组中
		{
			left = mid;
		}
		else
		{
			right = mid;
		}
		
	}
	return ar[mid];
}

四.查询二维数组中的值。

剑指offer上的题,从数组的右上角开始,如果查找的值比此值大,则消去第一行,否则,消去第一列。

bool Find(int target, vector > array) {
        bool found = false;
        int rows = array.size();//行数,从1起
        int columns = array[0].size();//列数
        
        if(!array.empty() && rows > 0 && columns > 0)
        {
           int row = 0;
           int column = columns - 1;
            while(row < rows && column >= 0)
            {
                if(array[row][column] == target)
                {
                    found = true;
                    break;
                }
                else if(array[row][column] < target)
                {
                    ++row;
                }
                else
                {
                    --column;
                }
            }
        }
        return found;
    }

五.寻找峰值

二分查找|笔试题_第3张图片

分析:峰值是指左右相邻的元素值都比它小,有三种峰值的情况

1.数组的第一个元素大于第二个元素。

2.数组的倒数第一个元素大于倒数第二个元素。

3.设置两个指针,left指向第一个元素,right指向最后一个元素,mid指向中间元素。如果mid-1指向的元素小于mid指向的元素,则说明,mid之后可能存在最大值,left = mid;如果mid指向的元素大于mid+1指向的元素,说明mid之前存在最大值,right=mid。直到mid指向的元素大于mid+1和mid-1指向的元素时停止.

int FindVaule(int *arr,int left,int right)
{
	int pos = -1;
	if(arr[left] > arr[left+1])
	{
		pos = left;
	}
	else if(arr[right] > arr[right-1])
		pos = right;
	else
	{
	while(left < right)
	{
		int mid = (right-left+1)/2+left;
		if(arr[mid]>arr[mid-1] && arr[mid]>arr[mid+1])
		{
			pos = mid;
			break;
		}
		else if(arr[mid] > arr[mid-1])
			left = mid;
		else 
		{
		  	if(arr[mid] > arr[mid+1])
				right = mid;
		}
	}
	}
	return pos;
}
int Find(int *arr,int n)
{
	if(NULL == arr || n <= 1)
		return -1;
	else
		return FindVaule(arr,0,n-1);
}

六.查询第n小的整形数值

二分查找|笔试题_第4张图片

#include 
 using namespace std;
#define min(a,b)(a len2)
		 return Find(arr2,len2,arr1,len1,k);
	if(len1 == 0)
		return arr2[k-1];
	 if(k == 1)
		 return min(arr1[0],arr2[0]);
	 //将k分成两部分,分别在arr1,arr2数组中查找
	 int k1 = min(k/2,len1);//如果最小的长度不够k/2
	 int k2 = k-k1;
	 //如果arr1的前k/2的部分大于arr2的钱k-k1部分,则说明arr2的钱k-k1部分一定位于第k个元素之前,所以可以删除。
	 //k的值也会变成k-k2
	 if(arr1[k1 - 1] > arr2[k2-1])
	 {
		 return Find(arr1,len1,arr2+k2,len2-k2,k-k2);
	 }
	 else if(arr1[k1-1] < arr2[k2-1])
	 {
		 return Find(arr1+k1,len1-k1,arr2,len2,k-k1);

	 }
	 else//两个值相等,则说明找到了
	 {
		 return arr1[k1-1];
	 }
 }

int main()
{
 	int ar[] = {1,3,3,7,9};
	int br[] = {2,4,6,8,10};
	int len1 = sizeof(ar)/sizeof(ar[0]);
	int len2 = sizeof(br)/sizeof(br[0]);
    int pos = 4;
	int m = Find(ar,len1,br,len2,pos);
    cout<

七.实现int my_sqrt(int x)函数

二分查找|笔试题_第5张图片

int mySqrt(int x) {
        if (x <= 1) return x;
          int left = 0, right = x;
          while (left < right) 
		  {
              int mid = left + (right - left) / 2;//数组也是从0开始,所以此处括号里不用+1
              if (x / mid >= mid) 
				left = mid + 1;
             else right = mid ;
        }        
		  return right -1 ;
}

 

你可能感兴趣的:(二分查找|笔试题)