数组基础理论
数组是存放在连续内存空间上的相同类型数据的集合。
删除或增添元素时,要移动其他元素的地址。
数组的元素是不能删的,只能覆盖。
vector是容器,不是数组。
二维数组在内存的空间地址是连续的么?c++是,java不是。
704. 二分查找
思路:最基本的二分查找,略。
时间复杂度:O(logn)
空间复杂度:O(1)
注意:
1.我习惯左闭右闭的写法,判断条件是while(left<=right)。
2.按照target的位置来找。
3.二分查找使用的条件:数组有序,元素唯一
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while(left <= right ){
int mid = (left + right) / 2;
if(target < nums[mid]){
right = mid - 1;
}
else if(target > nums[mid]){
left = mid + 1;
}
else {
return mid;
}
}
return -1;//未找到
}
};
27. 移除元素
思路:暴力解法,两层for循环,外层用于遍历整个数组,内层用于移动剩余元素。
时间复杂度:O(
)
空间复杂度:O(1)
注意:
1.预先定义一个size,可以动态变化数组的实际大小。
2.覆盖操作的时候,注意左右边界,不要访问数组越界。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i = 0;
//预先定义一个size,可以动态变化数组的实际大小
int size = nums.size();
while(i < size){
if(nums[i] == val){
for(int j = i; j < size - 1; j++){
nums[j] = nums[j + 1];
}
size--;
}
else{
i++;
}
}
return size;
}
};
思路:前后指针,前指针维护已经确定的结果数组的右边界,后指针向前遍历寻找符合条件的元素,如果找到了,就将后指针指向的元素覆盖前指针指向的元素,前指针+1。
时间复杂:O(n)
空间复杂度:O(1)
注意:
1.左闭右闭
2.向前找
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int front = 0;
int rear = nums.size() - 1;
//左闭右闭
while(front <= rear){
if(nums[front] == val){
nums[front] = nums[rear];
rear--;
}
else{
front++;
}
}
return front;
}
};
思路:双指针:快慢指针,慢指针维护已经确定的结果数组的右边界,快指针向前遍历寻找符合条件的元素,如果找到了,就将快指针指向的元素覆盖慢指针指向的元素,慢指针+1。(和前后指针一样,只不过一个是向后找,一个是向前找)
时间复杂度:O(n)
空间复杂度:O(1)
注意:
1.左闭右闭
2.向后找
3.这个方法代码最简洁
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//fast在nums中去找与val不同的元素
//slow维护找到的元素的右边界(开区间),因此实际的找到的元素数量=slow
int fast = 0;
int slow = 0;
for(;fast<nums.size();fast++){
if(nums[fast] != val){
nums[slow++] = nums[fast];
}
}
return slow ;
}
};
34. 在排序数组中查找元素的第一个和最后一个位置
思路:二分查找的应用,一个二分查找可以找到一个元素的位置,因此要找到第一个和最后一个位置,需要使用两次二分查找。
时间复杂度:O(logn)
空间复杂度:O(1)
注意:
1.二分查找要求:数组有序,元素唯一;而本题条件:数组有序,元素不唯一。
2.代码可以分块写,思路更清晰。也可以优化一起写,代码更简洁。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> arr = {-1, -1};
arr[0] = getLeft(nums, target);
arr[1] = getRight(nums, target);
return arr;
}
int getLeft(vector<int>& nums, int target){
int result = -1;
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int mid = (left + right) / 2;
if(target < nums[mid]){
right = mid - 1;
}
else if(target > nums[mid]){
left = mid + 1;
}
else{
result = mid;
right = mid - 1;
}
}
return result;
}
int getRight(vector<int>& nums, int target){
int result = -1;
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int mid = (left + right) / 2;
if(target < nums[mid]){
right = mid - 1;
}
else if(target > nums[mid]){
left = mid + 1;
}
else{
result = mid;
left = mid + 1;
}
}
return result;
}
};
35. 搜索插入位置
思路:二分查找的应用,在二分查找的基础上,考虑找不到的处理情况
时间复杂度:O(logn)
空间复杂度:O(1)
注意:
1.二分查找要求:数组有序,元素唯一,本题完美符合。
2.未找到是的插入位置可以有两种:return right + 1;或者 return left;(注意不是left + 1)
3.未找到情况下,循环结束时,left和right指针的位置情况:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while(left <= right){
int mid = (left + right) / 2;
if(target < nums[mid]){
right = mid - 1;
}
else if(target > nums[mid]){
left = mid + 1;
}
else{
return mid;
}
}
return left;
//或者return right + 1;
}
};