目录
- 1. 朴素二分查找
- 2. 在排序数组中查找元素的第一个和最后一个位置
- 3. 搜索插入位置
- 4. x的平方根
- 5. 山脉数组的峰值索引
- 6. 寻找峰值
- 7. 寻找旋转排序数组中的最小值
- 8. 点名
1. 朴素二分查找
- 题目信息:
- 题目链接:
二分查找- 二分查找的使用前提为数据具有"二段性",在二分时,并不一定要进行2等分(1/3,1/4…)
- 二分查找的时间复杂度:O( l o g N log^N logN)
- 求mid的算法优化(原算法有溢出风险)
class Solution
{
public:
int search(vector<int>& nums, int target)
{
int right = nums.size() - 1;
int left = 0;
while(right >= left)
{
//此种算法存在溢出风险
//int mid = (right + left) / 2;
int mid = (right - left) / 2 + left;
if(nums[mid] > target)
{
right = mid - 1;
}
if(nums[mid] < target)
{
left = mid + 1;
}
if(nums[mid] == target)
{
return mid;
}
}
return -1;
}
};
2. 在排序数组中查找元素的第一个和最后一个位置
- 题目信息:
- 题目链接:
在排序数组查找元素的第一个与最后一个位置- 思路:向符合条件位置不断推进
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target)
{
int right = nums.size() - 1;
int left = 0;
vector<int> count(2,-1);
//数据为空,特殊处理
if(nums.size() == 0)
{
return count;
}
//左端点
while(left < right)
{
//落到左区间
int mid = left + (right - left) / 2;
if(nums[mid] >= target)
{
right = mid;
}
if(nums[mid] < target)
{
left = mid + 1;
}
}
if(nums[left] == target)
{
count[0] = left;
}
right = nums.size() - 1;
left = 0;
//右端点
while(left < right)
{
//落到右区间
int mid = left + (right - left + 1) / 2;
if(nums[mid] > target)
{
right = mid - 1;
}
if(nums[mid] <= target)
{
left = mid;
}
}
if(nums[left] == target)
{
count[1] = left;
}
return count;
}
};
3. 搜索插入位置
- 题目信息:
- 题目链接:
搜索插入位置- 思路:取大,右区间的左边界
class Solution
{
public:
int searchInsert(vector<int>& nums, int target)
{
int left = 0;
int right = nums.size();
while(left < right)
{
//小于,大于等于
int mid = left + (right - left) / 2;
if(nums[mid] < target)
{
left = mid + 1;
}
if(nums[mid] >= target)
{
right = mid;
}
}
return left;
}
};
4. x的平方根
- 题目信息:
- 题目链接:
x的平方根- 思路:暴力解法的优化,左区间的右边界,二段性
class Solution
{
public:
int mySqrt(int x)
{
if(x < 1)
{
return 0;
}
int left = 1;
int right = x;
while(left < right)
{
//当left从0开始时,left + 1可能会溢出
long long mid = left + (right - left + 1) / 2;//防止溢出
if(mid * mid <= x)
{
left = mid;
}
if(mid * mid > x)
{
right = mid - 1;
}
}
return left;
}
};
5. 山脉数组的峰值索引
- 题目信息:
- 题目链接:
山脉数组的峰值索引- 思路:二分法,左区间右边界
class Solution
{
public:
int peakIndexInMountainArray(vector<int>& nums)
{
//左区间,右边界
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(nums[mid] > nums[mid - 1])
{
left = mid;
}
if(nums[mid] < nums[mid - 1])
{
right = mid - 1;
}
}
return left;
}
};
- 右区间的左边界
class Solution
{
public:
int peakIndexInMountainArray(vector<int>& nums)
{
//右区间,左边界
int left = 0;
int right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < nums[mid + 1])
{
left = mid + 1;
}
if(nums[mid] > nums[mid + 1])
{
right = mid;
}
}
return left;
}
};
6. 寻找峰值
- 题目信息:
- 题目链接:
寻找峰值- 思路:二段性,右区间的左边界,不足三个数特殊化处理
class Solution
{
public:
int findPeakElement(vector<int>& nums)
{
int left = 0;
int right = nums.size() - 1;
//特殊情况
//数组值小于3
if (nums.size() < 2)
{
return 0;
}
if (nums.size() < 3)
{
return nums[0] > nums[1] ? 0 : 1;
}
//右区间的左边界
while (left < right)
{
int mid = left + (right - left) / 2;
if (nums[mid] < nums[mid + 1])
{
left = mid + 1;
}
if (nums[mid] > nums[mid + 1])
{
right = mid;
}
}
return left;
}
};
7. 寻找旋转排序数组中的最小值
- 题目信息:
- 题目链接:
寻找寻转排序数组中的最小值- 思路:右区间,左边界
class Solution
{
public:
int findMin(vector<int>& nums)
{
//二段性,右区间,左边界
int right = nums.size() - 1;
int left = 0;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[right])
{
left = mid + 1;
}
if(nums[mid] < nums[right])
{
right = mid;
}
}
return nums[left];
}
};
8. 点名
- 题目信息:
- 题目链接:
点名- 思路:(多解法,考察知识广度)
<1>二分法:右区间的左边界,学号等于下标,学号大于下标,边界问题!
<2> 哈希表法:时间复杂度O(n),空间复杂度O(n)
<3> 异或法
<4> 等差数列求和
<5> 暴力遍历法
class Solution {
public:
int takeAttendance(vector<int>& records)
{
int left = 0;
int right = records.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(records[mid] == mid)
{
left = mid + 1;
}
if(records[mid] > mid)
{
right = mid;
}
}
//边界问题,n-1个元素,n个学生,必定有一人缺席,缺席为学号最后一位的学生
if(left == records.size() - 1 && left == records[left])
{
left++;
}
return left;
}
};
- 等差数列
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
//等差数列求和,首项加末项乘以项数除以2
int sum = ((0 + records.size()) * (records.size() + 1)) / 2;
for(int i = 0; i < records.size(); i++)
{
sum -= records[i];
}
return sum;
}
};
- 哈希表
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
//哈希表法
int n = records.size() + 1;
int* hash = (int*)malloc(n * sizeof(int));
//单位字节
memset(hash, 0, n * sizeof(int));
int i = 0;
for(i = 0; i < records.size(); i++)
{
hash[records[i]]++;
}
for(i = 0; i < n; i++)
{
if(hash[i] == 0)
{
break;
}
}
return i;
}
};
- 暴力遍历
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
//暴力遍历
int cur = 0;
while(cur < records.size())
{
if(cur != records[cur])
{
break;
}
cur++;
}
if(cur == records.size() - 1 && records[cur] == cur)
{
cur++;
}
return cur;
}
};
- 异或法
class Solution
{
public:
int takeAttendance(vector<int>& records)
{
//异或法
int sum = 0;
for(int i = 0; i < records.size() + 1; i++)
{
sum ^= i;
}
for(int i = 0; i < records.size(); i++)
{
sum ^= records[i];
}
return sum;
}
};