数组理论基础
文章链接:代码随想录
704. 二分查找
使用二分查找前提是数组为有序数组,同时题目还强调数组中无重复元素
right代表的是搜索范围的右边界,而不是数组的实际最后一个元素的索引
左闭右闭 [left, right]
class Solution {
public int search(int[] nums, int target) {
int left = 0; //定义左区间标识
int right = nums.length - 1; //定义右区间标识
// 当left==right,区间[left, right]依然有效,所以用 <=
while (left <= right) {
int mid= left + ((right - left) >> 1);// 防止溢出 等同于(left + right)/2
if(nums[mid] > target){ //中间值大于目标值
right = mid - 1; //右边界变为mid - 1 左闭右闭
}else if(nums[mid] < target){ //中间值小于目标值
left = mid + 1;
}else{
return mid;
}
}
return -1;
}
}
左闭右开 [left, right)
主要还是区间的把握
区间是你自己定义的,如果说有四个数字
定义左闭右开区间:left(0) < right(4) 可以取到下标为0,1,2,3的元素
定义左闭右闭区间:left(0) <= right(3),可以取到下标为0,1,2,3的元素
所以
class Solution {
public int search(int[] nums, int target) {
int left = 0; //定义左区间标识
int right = nums.length; //定义右区间标识
// 当left==right,区间[left, right)依然有效,所以用 <=
while (left < right) {
int mid= left + ((right - left) >> 1);// 防止溢出 等同于(left + right)/2
if(nums[mid] > target){ //中间值大于目标值
right = mid; //右边界变为mid 左闭右闭
}else if(nums[mid] < target){ //中间值小于目标值
left = mid + 1;
}else{
return mid;
}
}
return -1;
}
}
35. 搜索插入位置
暴力遍历
class Solution {
public int searchInsert(int[] nums, int target) {
/* 分四种情况 */
for(int i = 0; i < nums.length; i++){
//目标值在所有数组元素之前
//目标值等于数组元素中某一个元素
//目标值插入数组的位置
if(nums[i] >= target){
return i;
}
}
//目标值在所有元素之后
return nums.length;
}
}
二分法
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
// 定义target在左闭右闭的区间,[low, high]
int low = 0;
int high = n - 1;
while (low <= high) { // 当low==high,区间[low, high]依然有效
int mid = low + (high - low) / 2; // 防止溢出
if (nums[mid] > target) {
high = mid - 1; // target 在左区间,所以[low, mid - 1]
} else if (nums[mid] < target) {
low = mid + 1; // target 在右区间,所以[mid + 1, high]
} else {
// 1. 目标值等于数组中某一个元素 return mid;
return mid;
}
}
// 2.目标值在数组所有元素之前 3.目标值插入数组中 4.目标值在数组所有元素之后 return right + 1;
return high + 1;
}
}
//第二种二分法:左闭右开
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length;
while (left < right) { //左闭右开 [left, right)
int middle = left + ((right - left) >> 1);
if (nums[middle] > target) {
right = middle; // target 在左区间,在[left, middle)中
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,在 [middle+1, right)中
} else { // nums[middle] == target
return middle; // 数组中找到目标值的情况,直接返回下标
}
}
// 目标值在数组所有元素之前 [0,0)
// 目标值插入数组中的位置 [left, right) ,return right 即可
// 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
return right;
}
27. 移除元素
暴力法
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for (int i = 0; i < size; i++) {
if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
双指针-快慢指针 并没有改变元素的相对位置
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
双指针-相向指针
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
//找左边等于val的元素
while(left <= right && nums[left] != val){
//使用++leftIndex 可以确保我们先移动指针,然后再进行下一次判断。如果使用leftIndex++,则会导致先判断当前位置的元素,然后再移动指针
++left;
}
//找右边不等于val的元素
while(left <= right && nums[right] == val){
--right;
}
//将右边不等于val的元素覆盖左边等于val的元素
if(left < right){
//是先将右边不等于目标值的元素赋值给左边等于目标值的元素,然后同时移动左右指针,以继续寻找下一个需要移动的元素。
nums[left++] = nums[right--];
}
}
return left;
}
}