二分查找又叫折半查找,首先它数组中的元素是有序的,他将事物的规模每次缩小一般,直至查找到目标,或者目标不存在。
定义一个结构体
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<
运行截图:
题目有错出差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操作。
分析:用两个指针分别指向数组中的第一个元素和最后一个元素。接着寻找数组的中间元素,如果中间元素大于第一个指针指的元素,那么表示该元素位于前面的递增子数组,我们将第一个指针指向该元素;如果中间元素小于最后一个元素,则表示该元素位于后面的递增递增子数列,将第二个指针执行该元素。这样就可以缩小范围。当第一个指针指向前面数组的最大元素,第二个指针指向后面递增子数组的最小元素(即两个指针相邻时退出循环)。但是有一种特殊情况,就是数组一开始就是递增的,没有旋转,所以将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;
}
分析:峰值是指左右相邻的元素值都比它小,有三种峰值的情况
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);
}
#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 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 ;
}