29. Divide Two Integers
整数相除,不能用除法或者乘法或者取模。如果溢出,返回INT_MAX。
重点:左移乘2:<<
思路:这里模拟整数相除,本质就是计算一个被除数包括多少个除数,这包含多少个,如果一个个累加肯定是太慢了,可以考虑二分的思路,两倍两倍来计算,利用位运算,每一步移位来计算。这里还需要注意的两个问题,一个是溢出,一个是符号。溢出考虑两种情况:divisor = 0;
dividend = INT_MIN and divisor = -1
(because abs(INT_MIN) = INT_MAX + 1).
符号计算:
int flag = ((dividend < 0) ^ (divisor < 0)) ? -1 : 1;
代码:
class Solution {
public:
int divide(int dividend, int divisor) {
if (divisor == 0 || (dividend == INT_MIN && divisor == -1)) return INT_MAX;
long long m = abs((long long)dividend), n = abs((long long)divisor), res = 0;
int sign = ((dividend < 0) ^ (divisor < 0)) ? -1 : 1;
if (n == 1) return sign == 1 ? m : -m;
while (m >= n) {
long long t = n, p = 1;
while (m >= (t << 1)) {
t <<= 1;
p <<= 1;
}
res += p;
m -= t;
}
return sign == 1 ? res : -res;
}
};
34. Search for a Range
升序序列中,找到目标值的起始位置和终止位置。时间复杂度O(logn)
二分查找,找到后左右移动
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res(2, -1);
int len = nums.size();
if (len == 0) return res;
int left = 0, right = len - 1, mid = left;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] == target)
break;
else if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
if (nums[mid] != target) return res;
left = right = mid;
while(right < len -1 && nums[right] == nums[right+1]) ++right;
while(left > 0 && nums[left] == nums[left-1]) --left;
res[0] = left;
res[1] = right;
return res;
}
};
优雅版代码:
vector<int> searchRange(int A[], int n, int target) {
int i = 0, j = n - 1;
vector<int> ret(2, -1);
// Search for the left one
while (i < j)
{
int mid = (i + j) /2;
if (A[mid] < target) i = mid + 1;
else j = mid;
}
if (A[i]!=target) return ret;
else ret[0] = i;
// Search for the right one
j = n-1; // We don't have to set i to 0 the second time.
while (i < j)
{
int mid = (i + j) /2 + 1; // Make mid biased to the right
if (A[mid] > target) j = mid - 1;
else i = mid;
}
ret[1] = j;
return ret;
}
50. Pow(x, n)
实现:pow(x, n).
class Solution {
public:
double myPow(double x, int n) {
if(n==0) return 1;
//对负数的处理,防止溢出
if(n<0) return (1/x)*myPow(1/x,-(n+1));
return n%2==0 ? myPow(x*x, n/2) : x*myPow(x*x, n/2);
}
};
69. Sqrt(x)
实现:int sqrt(int x).
class Solution {
public:
int mySqrt(int x) {
int start = 0, end = x/4 + 1;
//二分查找的套路
while (start + 1 < end) {
int mid = (end - start) / 2 + start;
//如果直接比较,可能存在越界的问题
if (mid < (x / mid)) start = mid;
else if (mid > (x / mid)) end = mid;
else return mid;
}
if (end <= (x / end)) return end;
else return start;
}
};
74. Search a 2D Matrix
写出一个高效的算法来搜索 m × n矩阵中的值。
这个矩阵具有以下特性:
* 每行中的整数从左到右是排序的。
* 每行的第一个数大于上一行的最后一个整数。
因为条件中有每行的第一个数大于上一行的最后一个整数。所以先对每行的第一个数来进行一个二分查找,找到最后一个不大于target的数然后再对这一行进行二分查找即可,这样首先对每行的第一个数查找复杂度为O(logn),再对某一行进行查找,复杂度为O(logn),所以为O(logn)。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int start = 0, end = matrix.size() - 1;
while (start + 1 < end) {
int mid = (end - start) / 2 + start;
if (matrix[mid][0] < target) start = mid;
else end = mid;
}
int new_start = 0, new_end = matrix[0].size() - 1;
int index = matrix[end][0] <= target ? end : start;
if (matrix[start][0] > target) return false;
while (new_start + 1 < new_end) {
int mid = (new_end - new_start) / 2 + new_start;
if (matrix[index][mid] < target) new_start = mid;
else if (matrix[index][mid] > target) new_end = mid;
else return true;
}
if (matrix[index][new_start] == target) return true;
if (matrix[index][new_end] == target) return true;
return false;
}
};
162. Find Peak Element
题目:给出一个整数数组(size为n),其具有以下特点:
相邻位置的数字是不同的
假设num[-1] = num[n] = -∞
如果某个元素大于临近元素,就称其为峰值。数组中可能存在多个峰值,返回其中任意一个峰值的位置。
样例:给出数组[1, 2, 1, 3, 4, 5, 7, 6]返回1, 即数值 2 所在位置, 或者6, 即数值 7 所在位置。
思路:二分法,这里根据中间值和临近值,可以分为四种情况:
第一种情况:当前点就是峰值,直接返回当前值。
第二种情况:当前点是谷点,不论往那边走都可以找到峰值。
第三种情况:当前点处于下降的中间,往左边走可以到达峰值。
第四种情况:当前点处于上升的中间,往右边走可以达到峰值。
每种情况,把有答案的一半保留下来即可。
if (nums.empty()) return -1;
if (nums.size() == 1) return 0;
int start = 0, end = nums.size() - 1;
while (start + 1 < end) {
int mid = (end - start) / 2 + start;
if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1])
return mid;
// else if (nums[mid] > nums[mid - 1] && nums[mid] < nums[mid + 1])
// start = mid;
else if (nums[mid] < nums[mid - 1] && nums[mid] > nums[mid + 1])
end = mid;
else
start = mid;
}
if ((start == 0 && nums[start] > nums[start + 1]) || (nums[start] > nums[start - 1] && nums[start] > nums[start + 1]))
return start;
if ((end == nums.size()-1 && nums[end] > nums[end - 1]) || (nums[end] > nums[end - 1] && nums[end] > nums[end + 1]))
return end;
精简版,参考别人的代码:We just check the mid and mid+1 element and recheck the bigger side as the bigger side will have the peak element for sure.
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int start=0, end=nums.size()-1;
while(end>start){
int mid=(start+end)/2;
if(nums[mid] < nums[mid+1]) start=mid+1;
else end=mid;
}
return start;
}
};