题目要求: 给定一个有序整数数组,返回其中绝对值最小的元素。
例如数组【-5,2,0,1,3】则返回 0
此处很明显应该借用二分查找的思想,但是有几个细节还是值得注意。
- 先修知识点:
- 二分查找的非递归写法
- 为叙述清晰,下文一律使用“线程栈“指代每个线程拥有的栈空间,用”栈“来指代数据结构意义上的栈。使用“堆”指代一个进程的堆空间。
- 非递归实现的好处: 递归算法在递归深度过深时,会发生占“线程栈”溢出, 因为每一次函数调用都会占用更多的“线程栈”空间, 而每个线程拥有的栈空间是独立的,而且大小是预分配好的。
- 非递归写法常常通过使用“栈”来保存递归调用中需要记录的信息,这部分空间可以来源于“堆”空间。 一个进程的多个线程会共享堆空间,通常堆空间相比线程栈的空间要大,且可以动态增加,因此更不容易发生溢出。
- 二分查找的非递归写法
二分查找的非递归写法
非递归
# here the parameter n is the length of array
binary_search(int[] a, int n, int value)
{
int left = 0
int right = n-1
while( left< = right) //易错
{
middle = left + (right - left)/ 2
if(a[middle] > value)
{
right = middle -1 //易错
}
else if ( a[middle] < value)
{
left = middle +1 //易错
}
else
{
return middle
}
}
return -1
}
- 二分查找法的思想很简单,唯一需要主义的就是几个易错的点
- 第8行whil循环条件是 left <= right 不是left < right
- 第13行right = middle -1
- 第21行left = middle -1
二分查找之最小绝对值查找
int absMin(int* array, int size)
{
if(size == 1)
return a[0];
if(a[0] * a[size-1] >= 0) //如果同符号,这里用乘法判断正负有可能发生溢出,为书写简便,在此不作修正
return (a[0] >= 0) ? a[0] : a[size-1];
else
{
int low = 0, high = size-1, mid;
while(low < high) //易错
{
if(a[middle]==0)
return middle;
//此处不加也可以让后续的部分正确运行,但是加上可以减少无谓的对比。如果不加的话,遇到数组中间元素为0的情况,会一直循环到left和right相邻,且其中有一个位置的元素为0
if(low + 1 == high)
return abs(a[low]) < abs(a[high]) ? a[low] : a[high];
mid = low + (high - low) / 2;
if(a[low] * a[mid] >= 0)
low = mid; //易错,
if(a[high] * a[mid] >= 0)
high = mid; //易错
}
}
}
- 首先给定的数组有序, 因此先检查首位元素是否同符号,如果同符号, 则绝对值最小元素可以立刻给出(同正则为第一个元素, 同负则为最后一个元素)
- 当数组不同符号时,运用二分查找的思想, 首先计算middle = (left + right)/2
- 此处要注意与二分查找相似而不同的地方:
- 得到middle后,需要判断middle是否为0
- 如果为0, 则可以直接返回该元素位置作为绝对值最小的元素
- 如果该位置不为0, 则判断其正负, 如果为正,则需检查middle-left是否大于1,因为middle-left = 1的情况是找到了该数组正负变化的边界, 需要比较边界处两个元素哪个绝对值比较小。 例如【-5.-3.-2.-1,2,4,6】,最终比较的应该是-1与2的绝对值大小
- 尤其和二分查找不同的是, 循环中,由于每一次的middle都有可能是正负边界的元素,所以每一次循环中,left = middle 或 left = right 而不是 left = middle+1 或 right = middle - 1
- 得到middle后,需要判断middle是否为0
- 此处要注意与二分查找相似而不同的地方: