数组基础知识
时间复杂度
算法的时间复杂度,也就是算法的时间量度,记作:。它表示随问题规模的增大,算法执行时间的增长率和的增长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。
算大O方法
有时候我们去计算时间复杂度的时候发现不是一个简单的或者, 而是一个复杂的表达式,例如:
1.去掉运行时间中的加法常数项 (因为常数项并不会因为n的增大而增加计算机的操作次数)。
2.去掉常数系数
3.只保留保留最高项,去掉数量级小一级的n (因为 的数据规模远大于),最终简化为:
4.如果这一步理解有困难,那也可以做提取n的操作,变成,省略加法常数项后也就别变成了:
所以最后我们说:这个算法的算法时间复杂度是 。
常见时间复杂度
数组理论基础
- 数组是存放在连续内存空间上的相同类型数据的集合。
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的。
- 数组的元素是不能删的,只能覆盖。
- C++中二维数组是连续分布的。
- Java的二维数组是无序的。
二分查找
前提条件
- 有序数组(一般从小到大);
- 数组中无重复元素。
题目
704.二分查找
自己看到题目的第一想法
我的想法是使用"指针"指向整个数组的开头,再利用一个count=-1变量记录指针滑动了多少次,指针依次取值然后与target的值进行对比,然后return count的值即为目标值所在的位置。
定义一个size变量读取数组长度,若count的值大于size,则表示数值内没有与target相同的数,则直接输出count初值。
class Solution {
public:
int search(vector<int>& nums, int target) {
int size = nums.size();
int count = -1;
int p = 0;
for(int i = 0;i < size;i++){
if(nums[p+i] == target ){
count = i;
}
}
return count;
}
};
代码随想录的方法
左闭右闭
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
if (nums[middle] > target) {
right = middle - 1; // target 在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; // target 在右区间,所以[middle + 1, right]
} else { // nums[middle] == target
return middle; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
左闭右开
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
while (left < right) { // 因为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; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
27.删除元素
自己看到题目的第一想法
1、想法为定义一个新的数组,这样直接把留下的数值赋值到新数组里面,再输出。但题目要求不能开辟新数组空间。此路不通。
2、快慢指针的想法:快指针遍历整个数组,做判断,遇到不是要删除的数值,则赋值给慢指针。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
int fast = 0;
while(fast < nums.size()){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow ++;
}
fast ++;
}
return slow;
}
};
代码随想录的方法
暴力方法
// 时间复杂度: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;
}
};
Python
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
fast = 0 # 快指针
slow = 0 # 慢指针
size = len(nums)
while fast < size: # 不加等于是因为,a = size 时,nums[a] 会越界
# slow 用来收集不等于 val 的值,如果 fast 对应值不等于 val,则把它与 slow 替换
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
利用库函数方法
remove函数
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
return remove(nums.begin(), nums.end(), val) - nums.begin();
}
};
erase函数
- erase(pos,n); 删除从pos开始的n个字符,比如erase(0,1)就是删除第一个字符
- erase(position);删除position处的一个字符(position是个string类型的迭代器)
- erase(first,last);删除从first到last之间的字符(first和last都是迭代器)
class Solution {
public:
//直接调用库函数,删除所有等于val值的元素
int removeElement(vector<int>& nums, int val) {
nums.erase(std::remove(nums.begin(), nums.end(), val), nums.end());
return nums.size();
}
};
学以致用
二分查找和快慢指针皆可用于自动驾驶中点云、图像的一些遍历操作,比如删除点云中为0的点。
今日收获,记录一下自己的学习时长
二分查找印象不是很深,之后还得复习 快慢指针终于自己写出来了!
2月1日:10点到12点半,学习两个半小时。