第一题 搜索螺旋排序数组
二分查找时,右指针应该为数组大小减一
二分查找只适用于有序数列
注意考虑边界0 、 1 等特殊值
class Solution {
public:
int search(vector<int>& nums, int target) {
//暴力枚举 复杂度O(n)
//if(!nums.size())
// return -1;
// for(int i = 0; i < nums.size(); i++)
// {
// if(target == nums[i])
// return i;
// }
// return -1;
//不能使用二分查找,二分查找只适合有序的
// int left = 0;
// int right = nums.size() - 1;
// int mid;
// while(left <= right)
// mid = (left + right) / 2;
// if(target > nums[mid])
// {
// left = mid + 1;
// }
// else if(target < nums[mid])
// {
// right = mid - 1;
// }
// else
// {
// return mid;
// }
// }
// return -1;
//改进版本的二分查找,考虑到有一部分是有序的
int n = nums.size();
if (!n)//判断为空的时候
{
return -1;
}
if (n == 1)//一个元素不用二分
{
return (target == nums[0])? 0 : -1;
}
int l = 0;
int r = n - 1;
// while (l <= r)
// {
// int mid = (l + r) / 2;
// if(target == nums[mid])//如果为中间值返回
// return mid;
// if(nums[0] < nums[mid])//判断左侧是否为序区间
// {
// if(target < nums[mid])//判断中间值是否在左侧有序区间
// {
// r = mid - 1;//如在左侧有序区间,在左侧使用二分查找
// }
// else//如不在左侧
// {
// l = mid + 1;
// }
// }
// else//如左侧不为有序区间,则右侧为有序区间
// {
// if(target > nums[mid])//判断中间值是否在右侧有序区间
// {
// l = mid + 1;//如在右侧有序区间,在右侧使用二分查找
// }
// else//如不在右侧有序区间
// {
// r = mid - 1;
// }
// }
// }
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid])//如果左边是有序的,
{
if (nums[0] <= target && target < nums[mid]) //左边有序并且目标值在左边
{
r = mid - 1;//使用二分查找
}
else //如果右边无序,且目标值在右边
{
l = mid + 1;//遍历右侧
}
}
else //左边无序则右边一定有序
{
if (nums[mid] < target && target <= nums[n - 1])//右边有序,且目标值在右边
{
l = mid + 1;//右边二分查找
}
else //左边无序,且目标值在左边
{
r = mid - 1;//遍历左侧 }
}
}
return -1;
}
};
第二题 搜索旋转排序数组Ⅱ
class Solution {
public:
bool search(vector<int>& nums, int target) {
int n = nums.size();
if(!n)
{
return false;
}
// //暴力
// for(int i = 0; i < nums.size(); i++)
// {
// if(target == nums[i])
// {
// return true;
// }
// }
// return false;
//二分
int l = 0;
int r = n - 1;
int mid;
while(l <= r)
{
mid = (l + r) / 2;
if(target == nums[mid])
{
return true;
}
if(nums[mid] == nums[l] && nums[mid] == nums[r])//当序列允许重复的情况下,无法判断哪一边是有序的
{
l++;
r--;
}
else if(nums[l] <= nums[mid])//此处可随l的值更新,因为nums[l] == nums[mid] 已经判断过,也可以使用nums[0],但速度会变慢
{
if(nums[l] <= target && target < nums[mid])
{
r = mid - 1;
}
else
{
l = mid + 1;
}
}
else
{
if(nums[mid] < target && target <= nums[r])//此处可随r的值更新,因为nums[r] == nums[mid] 已经判断过,也可以使用nums[n - 1],但速度会变慢
{
l = mid + 1;
}
else
{
r = mid - 1;
}
}
}
return false;
}
};
第三题 寻找排序数组中的最小值
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
if(!n)
{
cerr << "error: nums.size() == 0.\n";
return 0;
}
暴力
// int min = 50001;
// for(int i = 0; i < nums.size(); i++)
// {
// if(nums[i] < min)
// {
// min = nums[i];
// }
// }
// return min;
先排序,在输出,O(nlogn)
// sort(nums.begin(), nums.end());
// return nums[0];
//两个for,速度快一半吧 O(n/2)
// int l = 0;
// int r = n - 1;
// int mid = 0;
// int min = 50001;
// mid = (l + r) / 2;
// if(nums[0] <= nums[mid])
// {
// min = nums[0];
// for(; mid <= r; mid++)
// {
// if(nums[mid] < min)
// {
// min = nums[mid];
// }
// }
// }
// else
// {
// min = nums[r];
// for(; l <= mid; l++)
// {
// if(nums[mid] < min)
// {
// min = nums[l];
// }
// }
// }
// return min;
//二分法
//补充:nums[mid] > nums[right]最极端的情况是位于最大值处,加一即为最小值,因而,left == mid + 1;
//补充:nums[mid] < nums[right]最极端的情况是mid处即为最小值,因而,right == mid;
//补充:如果left 和 right 都 === mid 当序列为基数个元素时循环将无法结束
//下面的解释引用自leetcode评论
int left = 0;
int right = nums.size() - 1;
int mid = 0;
//疑问:为什么while的条件是low<high,而不是low<=high呢
// 解答:low<high,假如最后循环到{*,10,1,*}的这种情况时,nums[low]=10,nums[high]=1,nums[mid]=10,low=mid+1,
//直接可以跳出循环了,所以low<high,此时low指向的就是最小值的下标;
// 如果low<=high的话,low=high,还会再不必要的循环一次,此时最后一次循环的时候会发生low==high==mid,
//则nums[mid]==nums[high],则会走一次else语句,则low=mid+1,此时low指向的是最小值的下一个下标,
// 则需要return[low-1]
while(left < right)
{
mid = (left + right) / 2;
if(nums[mid] < nums[right])
{
//如果中间值小于最大值,则最大值减小
//疑问:为什么 high = mid;而不是 high = mid-1;
//解答:{4,5,1,2,3},如果high=mid-1,则丢失了最小值1
right = mid;
}
else
{
//如果中间值大于最大值,则最小值变大
//疑问:为什么 low = mid+1;而不是 low = mid;
//解答:{4,5,6,1,2,3},nums[mid]=6,low=mid+1,刚好nums[low]=1
//继续疑问:上边的解释太牵强了,难道没有可能low=mid+1,正好错过了最小值
//继续解答:不会错过!!! 如果nums[mid]是最小值的话,则其一定小于nums[high],走if,就不会走else了
left = mid + 1;
}
}
return nums[left];
}
};
第四题 斐波那契数
滚动数组思想:“简要来说就是通过观察dp方程来判断需要使用哪些数据,可以抛弃哪些数据,一旦找到关系,就可以用新的数据不断覆盖旧的数据量来减少空间的使用。”感谢——Andy01_
// //空间复杂度为O(n)
// class Solution {
// public:
// int fib(int n) {
// int f[1000] = {0};
// if(n < 2)
// return n;
// f[0] = 0;
// f[1] = 1;
// for(int i = 2; i <= n; i++)
// {
// f[i] = f[i - 1] + f[i - 2];
// }
// return f[n];
// }
// };
//用滚动数组思想降低空间复杂度
//分析上述的数组中只需要三个位置来存储数据
class Solution {
public:
int fib(int n) {
if(n < 2)
{
return n;
}
int a[3];
a[0] = 0;
a[1] = 1;
for(int i = 2; i <= n; i++)
{
a[2] = a[1] + a[0];//a[3] = a[2] + a[1];需要把 a[2] 和 a[1] 保留, a[0] 是空闲
a[0] = a[1];
a[1] = a[2];
}
return a[2];
}
};
第五题 第N个泰波那契数
class Solution {
public:
int tribonacci(int n) {
if(n < 2)
{
return n;
}
if(n == 2)
{
return 1;
}
int a[4] = {0};
a[0] = 0;
a[1] = 1;
a[2] = 1;
for(int i = 3; i <= n; i++)
{
a[3] = a[0] + a[1] + a[2];//a[4] = a[3] + a[2] + a[1]; 数组滚动
a[0] = a[1];
a[1] = a[2];
a[2] = a[3];
}
return a[3];
}
};
第六题 拿硬币 贪心?不太懂贪心是什么
class Solution {
public:
int minCount(vector<int>& coins) {
//暴力
// int ans = 0;
// for(int i = 0; i < coins.size(); i++)
// {
// if(coins[i] % 2 == 0)
// {
// ans += coins[i] / 2;
// }
// else
// {
// ans += coins[i] / 2 + 1;
// }
// }
// return ans;
//贪心?
int ans = 0;
for(int i = 0; i < coins.size(); i++)
{
ans += (coins[i] + 1 ) / 2;//coins[i] + 1 来处理 % 取余为1的情况
}
return ans;
}
};
第七题 山脉数组的峰顶索引
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
// 枚举
// int mi = -1;
// for(int i = 1; i < arr.size() - 1; i++)
// {
// if(arr[i] > arr[i + 1])
// {
// mi = i;
// break;
// }
// }
// return mi;
if(arr.size() < 3)
return -1;
//二分法
int left = 1;//边界注意别溢出
int right = arr.size() - 2;
int mid = 0;
int ans = 0;
while(left <= right)
{
mid = (left + right) / 2;
if(arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1])
{
return mid;
}
if( arr[mid] > arr[mid + 1])
{
ans = mid;
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return left;
}
};
第八题 差的绝对值为K的数对数
class Solution {
public:
int countKDifference(vector<int>& nums, int k) {
// int ans = 0;
// //暴力
// for(int i = 0; i < nums.size(); i++)
// {
// for(int j = i + 1; j < nums.size(); j++)
// {
// if(abs(nums[j] - nums[i]) == k)
// {
// ans++;
// }
// }
// }
// return ans;
//哈希表,没太看懂
int res = 0, n = nums.size();
unordered_map<int int> cnt;
for(int j = 0; j < n; ++j)
{
res += cnt.count(nums[j] - k);
res +=cnt.count[nums[j] + k];
++cnt[nums[j]];
}
}
};