算法通关村第九关白银挑战——二分查找的多种应用(题目)

大家好,我是怒码少年小码。

本篇的主要内容是二分查找的扩展练习。

题目

1. 找到山脉数组的峰顶索引

LeetCode 852题。力扣的题目描述太复杂了,简单的说就是有一个数组,它里面的元素的值是先递增再递减,请你找到最大值的下标并返回。

这还不简单,最大值之前的所有元素都是 array[i] < array[i+1] 的,我们直接遍历找到第一个array[i] > array[i+1]的下标i就是峰值索引。这种方法很容易想到,这里就不实现了,我们讲讲用二分查找怎么做:

使用二分查找进一步优化:对于mid=(left+right)/ 2;

  • 如果array[mid] > array[mid-1]array[mid] > array[mid+1],那mid是顶峰下标;
  • 如果array[mid] > array[mid-1]array[mid] < array[mid+1],那么顶峰下标在mid的右边;
  • 如果array[mid] < array[mid-1]array[mid] > array[mid+1],那么顶峰下标在mid的左边。

这就是一个二分的简单变形:

int peekIndex(int array[], int size) {
	int left = 0;
	int right = size - 1;
	int ans;
	while (left <= right) {
		int mid = (left + right) / 2;
		if (array[mid] > array[mid + 1]) {
			ans = mid;
			right = mid - 1;
		}
		else {
			left = mid + 1;
		}
	}
	return ans;
}

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

LeetCode 153 :已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

给你一个元素值互不相同的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
时间复杂度为 O(log n) 的算法解决此问题。

  • 输入:nums = [3,4,5,1,2]
  • 输出:1
  • 解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

我们记左右边界分别为low和high, mid = (low + high)/ 2

  • 当nums[mid] > nums[hjgh]时,说明最小值在mid的右侧
  • 当nums[mid] < nums[high]时,说明最小值在mid的左侧
int findMin(vector<int>& nums) {
	int low = 0;
	int high = nums.size() - 1;
	while (low < high) {
		int pivot = (low + high) / 2;
		if (nums[pivot] < nums[high]) {
			high = pivot;
		}
		else {
			low = pivot + 1;
		}
	}
	return nums[low];
}

3. 找到缺失的数字

数组array包含从0到n的所有整数,但其中缺了一个。

分析:可以先对数组排序。
当数组排序之后,在缺失的数据之前,所有的元素都满足array[i] = i,在缺失的数据之后,所有的元素都满足array[i] != i。所以可以用二分法实现。

int missingNum(int array[], int size) {
	int left = 0;
	int right = size - 1;
	while (left < right) {
		int mid = (left + right) / 2;
		if (array[mid] == mid) {
			left = mid + 1;
		}
		else {
			right = mid - 1;
		}
	}
	return left;
}

4. 优化求平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留整数部分,小数部分将被舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

int mySqrt(int x) {
	int low = 0;
	int high = x;
	int ans;
	while (low <= high) {
		int n = low + (high - low) / 2;
		if ((long)n * n <= x) {
			ans = n;
			low = n + 1;
		}
		else {
			high = n - 1;
		}
	}
	return ans;
}

n * n 的时候如果还是int类型在数字大的时候会溢出,所以要强转为long类型。

END

行百里者半九十,坚持!加油!!

你可能感兴趣的:(算法学习,算法,数据结构,leetcode,c++)