题目:实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
解析:
mid = left + (right - left)/2
采用牛顿法
int mySqrt(int x){
if (x < 2)
return x;
double x0 = x;
double x1 = 0.5 * (x0 + x/x0);
while (fabs(x0 - x1) >= 1)
{
x0 = x1;
x1 = 0.5 * (x0 + x/x0);
}
return (int)x1;
}
题目:你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
解析:有序排列
mid = left + (right - left)/2;
这样写不用担心,加法溢出// Forward declaration of isBadVersion API.
bool isBadVersion(int version);
int firstBadVersion(int n) {
int left = 1;
int right = n;
int mid;
while (left < right)
{
mid = left + (right - left)/2;
if (isBadVersion(mid))
right = mid;
else
left = mid + 1;
}
return left;
}
注:类似的有求最小的,right = mid
题目:给定一个正整数 num,编写一个函数,如果 num 是一个完全平方数,则返回 True,否则返回 False。
说明:不要使用任何内置的库函数,如 sqrt。
解析:平方根不大于 num/2
x = (x + num/x) / 2;
关键的一步迭代,逐渐逼近平方根bool isPerfectSquare(int num){
if (num == 1)
return true;
long long x = num / 2;
while (x * x > num)
{
x = (x + num/x) / 2;
}
return (x * x == num);
}
bool isPerfectSquare(int num){
if (num == 1)
return true;
int left = 2;
int right = num/2;
long long mid;
while (left <= right)
{
mid = left + (right - left)/2;
if (mid * mid == num)
return true;
else if (mid * mid < num)
left = mid + 1;
else
right = mid - 1;
}
return false;
}
题目:我们正在玩一个猜数字游戏。 游戏规则如下:
我从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。
每次你猜错了,我会告诉你这个数字是大了还是小了。
你调用一个预先定义好的接口 guess(int num),它会返回 3 个可能的结果(-1,1 或 0):
解析:
int guess(int num);
int guessNumber(int n)
{
int left = 1;
int right = n;
int mid;
while (left < right) //必然可以查找到
{
mid = left + (right - left) / 2;
if (guess(mid) == 0)
return mid;
else if (guess(mid) == 1)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
题目:你总共有 n 枚硬币,你需要将它们摆成一个阶梯形状,第 k 行就必须正好有 k 枚硬币。
给定一个数字 n,找出可形成完整阶梯行的总行数。
n 是一个非负整数,并且在32位有符号整型的范围内。
解析:运用求和公式 s u m = ( 1 + k ) ⋅ k / 2 sum = (1+ k) \cdot k / 2 sum=(1+k)⋅k/2,查找符合条件的n
int arrangeCoins(int n){
if (n < 1)
return 0;
int left = 1;
int right = n;
long long mid;
long long temp;
while (left <= right) //相等时可能已经超过
{
mid = left + (right - left)/2;
temp = (1 + mid) * mid / 2;
if (temp == n)
return mid;
else if (temp < n)
left = mid + 1;
else
right = mid - 1;
}
return right;
}
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
说明:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
解析:排好序的查找
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize - 1;
int mid, temp;
while (left <= right) //不一定能查找到,遍历完
{
mid = left + (right - left)/2;
temp = nums[mid];
if (temp == target)
return mid;
else if (temp < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
题目:给定一个只包含小写字母的有序数组letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。
数组里字母的顺序是循环的。举个例子,如果目标字母target = ‘z’ 并且有序数组为 letters = [‘a’, ‘b’],则答案返回 ‘a’。
解析:有序查找
right = mid
char nextGreatestLetter(char* letters, int lettersSize, char target){
int left = 0;
int right = lettersSize; //这个地方可写成lettersSize - 1
int mid;
char temp;
while (left < right)
{
mid = left + (right - left)/2;
temp = letters[mid];
if (temp <= target)
left = mid + 1;
else
right = mid;
}
//与上面的呼应,这里可改成:再判断一次letters[right]是否大于target,否则输出letters[0]
return letters[left % lettersSize];
}
题目:我们把符合下列属性的数组 A 称作山脉:
A.length >= 3
存在 0 < i < A.length - 1 使得A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1]
给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1] 的 i 的值。
解析:找最大值
right = mid
int peakIndexInMountainArray(int* A, int ASize){
int left = 0;
int right = ASize - 1;
int mid;
while (left < right)
{
mid = left + (right - left)/2;
if (A[mid] < A[mid+1])
left = mid + 1;
else
right = mid;
}
return left;
}
题目:魔术索引。 在数组A[0…n-1]中,有所谓的魔术索引,满足条件A[i] = i。给定一个有序整数数组,编写一种方法找出魔术索引,若有的话,在数组A中找出一个魔术索引,如果没有,则返回-1。若有多个魔术索引,返回索引值最小的一个。
解析:找到第一个满足条件的
mid != nums[mid]
时,左右两边都有可能是最终结果,所以递归二分int DivideFind(int* arr, int left, int right)
{
if (left == right) //边界返回
{
if (left == arr[left])
return left; //符合
else
return -1;
}
int mid = left + (right - left)/2;
if (mid == arr[mid])
{
int ans = DivideFind(arr, left, mid);
return (ans == -1) ? mid : ans; //递归查询的是否符合
}
else
{
int ans = DivideFind(arr, left, mid); //
if (ans == -1) //左半部分不符合,再查询右半部分
return DivideFind(arr, mid + 1, right);
else
return ans;
}
}
int findMagicIndex(int* nums, int numsSize){
return DivideFind(nums, 0, numsSize-1);
}
题目:稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
解析:
strcmp()
int findString(char** words, int wordsSize, char* s){
int left = 0;
int right = wordsSize - 1;
int mid, temp;
char* test;
while (left <= right)
{
mid = left + (right - left) / 2;
test = words[mid];
if (strcmp(test, "") == 0) //与空字符比较,不要直接用==
{
while (strcmp(test, "") == 0 && mid < right-1)
{
mid++;
test = words[mid];
}
if (strcmp(test, "") == 0)
{
right--;
continue;
}
}
temp = strcmp(test, s);
if (temp == 0)
return mid;
else if (temp > 0)
right = mid - 1;
else
left = mid + 1;
}
return -1;
}
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
解析:准确说是非递减的数组,左半部分有序、右半部分有序
int minArray(int* numbers, int numbersSize){
int left = 0;
int right = numbersSize - 1;
int mid, temp;
while (left < right)
{
mid = left + (right - left)/2;
temp = numbers[mid];
if (temp < numbers[right])
right = mid;
else if (temp > numbers[right])
left = mid + 1;
else
right--;
}
return numbers[left];
}
题目:统计一个数字在排序数组中出现的次数。
解析:
int search(int* nums, int numsSize, int target){
int left = 0;
int right = numsSize - 1;
int mid, temp;
int cnt = 0;
while (left < right)
{
mid = left + (right - left)/2;
temp = nums[mid];
if (temp == target)
right = mid;
else if (temp < target)
left = mid + 1;
else
right = mid - 1;
}
while (left < numsSize && nums[left] == target)
{
cnt++;
left++;
}
return cnt;
}
查找第一次出现和最后一次出现的位置
int FindFirst(int* arr, int len, int target)
{
int left = 0;
int right = len - 1;
int mid, temp;
while (left < right)
{
mid = left + (right - left)/2;
temp = arr[mid];
if (temp == target)
right = mid;
else if (temp < target)
left = mid + 1;
else
right = mid - 1;
}
if (arr[left] == target)
return left;
else
return -1;
}
int FindLast(int* arr, int len, int target)
{
int left = 0;
int right = len - 1;
int mid, temp;
while (left < right)
{
mid = left + (right - left + 1)/2; //不让mid等于left
temp = arr[mid];
if (temp == target)
left = mid;
else if (temp < target)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
int search(int* nums, int numsSize, int target){
if (numsSize == 0)
return 0;
int firstIndex, lastIndex;
firstIndex = FindFirst(nums, numsSize, target);
if (firstIndex == -1)
return 0;
lastIndex = FindLast(nums, numsSize, target);
return lastIndex - firstIndex + 1;
}
题目:一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
解析:
int missingNumber(int* nums, int numsSize){
int left = 0;
int right = numsSize;
int mid;
while (left < right)
{
mid = left + (right - left)/2;
if (nums[mid] == mid) //判断条件
left = mid + 1;
else
right = mid;
}
return left;
}