数组理论基础
一维数组
- 数组中的元素在内存空间中是连续的
- 数组名与数组中第一个元素的地址相同(一维数组)
- 数组的下标从0开始
- 删除数组的元素其实是用后面的元素覆盖掉要删除的元素
- 数组的长度不能改变
二维数组
- 二维数组是按照行存储的,也是连续的
- 将二维数组看作是一维数组的一维数组
- 二维数组就是指针组成的数组,可以用二级指针表示
int arr[2][3] = {{1,2,3},{4,5,6}}
// 首先将二维数组arr看作元素是arr[0],arr[1]的一维数组
arr // 二维数组arr的起始地址
arr[i] // 第i行一维数组的数组名,代表该一维数组的首元素地址,即第一个元素arr[i][0]的地址 =*(arr+i)
arr[i]+j // 代表arr[i][j]的地址,即&(arr[i][j])
*(arr[i]+j) // 代表arr[i][j]
arr[i][j] // 代表arr[i][j]
arr+i // 代表二维数组中第i行数组的地址
*(arr+i) // 即arr[i],第i行第0列的地址
*(arr+i)+j // 即&(arr[i][j])
*(*(arr+i)+j) // 即arr[i][j]
如果难以理解,可以看看在一维数组中的情况:
int arr[3]={1,2,3}
arr[1] // 代表了a[1]的值,即2
arr+1 // 代表了a[1]的地址,即&a[1]
*(arr+1) // 代表了a[1]的值,即2
704 二分查找
题目链接:二分查找
思路
暴力解法
如果这道题目的名字不是二分查找,那么拿到题目一个最直接的思路就是for
循环暴力求解。
class Solution {
public:int search(vector<int>& nums, int target) {for(int i=0; i<nums.size(); i++){if(nums[i] == target){return i;}}return -1;}
};
二分解法
再一看,输入的数组是有序的,同时数组中还没有重复元素,再结合题目二分查找,便也可轻易地想到实用二分法来查找元素。自己在纸上画画,有一个二分法的伪代码。
二分法的具体实现要关注两个点:
- 区间问题
到底是左闭右开区间[left,right),还是左闭右闭区间[left, right],我选择实用左闭右闭区间,因为这样看起来比较好理解,同时while条件中可以left可以等于right。 - 溢出问题
这是一个小问题,计算mid时:mid = (left+right)/2,这样可能出现的一个问题是:left+right太大导致溢出。所以可以采取另一个计算方法:mid = left + (right - left) / 2,可以有效避免溢出问题。
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 - left) / 2;if (nums[mid] == target){return mid;}else if (nums[mid] < target){left = mid + 1;}else if (nums[mid] > target){right = mid - 1;}}return -1;}
};
35 搜索插入位置
题目链接:搜索插入位置
思路
暴力解法
还是老样子,for
循环,如果target等于当前元素,则返回当前下标;如果target小于当前元素,则代表该target需要插入到当前位置,返回当前下标;最后一种情况是在数组末尾添加元素,需要返回数组的长度。但是时间复杂度为O(n)
。
class Solution {
public:int searchInsert(vector<int>& nums, int target) {for(int i=0; i<nums.size(); i++){if(target == nums[i]){return i;}else if(target < nums[i]){return i;}}return nums.size();}
};
二分法
可以使用二分法对该元素进行查找,找到则返回下标mid
,找不到则返回left
(因为while
条件退出时left = right + 1
)。时间复杂度为O(logn)
class Solution {
public:int searchInsert(vector<int>& nums, int target) {int left = 0, right = nums.size()-1;while(left <= right){int mid = left + (right - left)/2;if(nums[mid] == target){return mid;}else if(nums[mid] > target){right = mid - 1;}else{left = mid + 1;}}return left;}
};
27 移除元素
题目链接:移除元素
思路
暴力解法
在看完题目说明后。首先是得删除掉数组中的目标元素(在数组中删除元素本质是后继元素的覆盖),然后返回的是剩余元素的长度。使用for
循环,如果当前元素等于目标元素,则: 将后续的所有元素向前移动一位(这里又要使用一个for循环),同时数组长度减一,for
循环的i
也减一(因为数组已经移动,当前位置的元素是之前的下一个位置的元素,还没有经过if
判断)。
class Solution {
public:int removeElement(vector<int>& nums, int val) {int n = nums.size();for(int i=0; i<n; i++){if(nums[i] == val){for(int j=i+1; j<n; j++){nums[j-1] = nums[j];}n--;i--;}}return n;}
};
双指针法
这道题不看解析想不到要用双指针法,对于什么是双指针法,现在浅显的认识也就是要有两个东西来进行处理,之前的for
循环都是使用一个东西来遍历。
双指针法就是得要有两个东西来对数组进行处理,直观解释一下过程。
变量fast
,也就是快指针,用来遍历要处理的数组;变量slow
,也就是慢指针,用来对新数组的下标进行计数。
class Solution {
public:int removeElement(vector<int>& nums, int val) {int slow = 0;for(int fast = 0; fast<nums.size(); fast++){if(nums[fast] != val){nums[slow] = nums[fast];slow++;}}return slow;
}
};
参考链接
- https://book.itheima.net/course/223/1263669610003230722/1263675595644137474
- https://zhuanlan.zhihu.com/p/148737542
- https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html