代码随想录算法训练营第一天|数组理论基础、704二分查找、27移除元素

数组理论基础

一维数组

  1. 数组中的元素在内存空间中是连续的
  2. 数组名与数组中第一个元素的地址相同(一维数组)
  3. 数组的下标从0开始
  4. 删除数组的元素其实是用后面的元素覆盖掉要删除的元素
  5. 数组的长度不能改变

二维数组

  1. 二维数组是按照行存储的,也是连续的
  2. 将二维数组看作是一维数组的一维数组
  3. 二维数组就是指针组成的数组,可以用二级指针表示
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;
   }
};

二分解法

再一看,输入的数组是有序的,同时数组中还没有重复元素,再结合题目二分查找,便也可轻易地想到实用二分法来查找元素。自己在纸上画画,有一个二分法的伪代码。
二分法的具体实现要关注两个点:

  1. 区间问题
    到底是左闭右开区间[left,right),还是左闭右闭区间[left, right],我选择实用左闭右闭区间,因为这样看起来比较好理解,同时while条件中可以left可以等于right。
  2. 溢出问题
    这是一个小问题,计算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;
}  
};

参考链接

  1. https://book.itheima.net/course/223/1263669610003230722/1263675595644137474
  2. https://zhuanlan.zhihu.com/p/148737542
  3. https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值