代码随想录-数组部分

代码随想录-数组部分

前言:

某鸽了好多天的作者良心发现,差不多该更新点东西了,正好最近在刷代码随想录,就在这里写写刷题的题解。

704. 二分查找:

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入:
nums = [-1,0,3,5,9,12], target = 9
输出:
4
解释: 9 出现在 nums 中并且下标为 4

示例 2:
输入:
nums = [-1,0,3,5,9,12], target = 2
输出:
-1
解释: 2 不存在 nums 中因此返回 -1

提示:

你可以假设 nums 中的所有元素是不重复的。 n 将在 [1, 10000]之间。 nums 的每个元素都将在 [-9999,
9999]之间。

思路分析:

从题目我们得知输入的数组是升序的,所以我们可以通过判断中间值与寻找值的大小的关系,如果大于中间值,证明寻找的值可能在中间值左边,如果大于中间值,则在数组右边,如果恰好等于中间值,则返回中间值,重复此过程,直到区间长度为1为止,若此时未找到寻找值,返回-1。

代码实现:

以下是代码实现的过程:

class Solution {
public:
    int search(vector<int>& nums, int target) {
    int left=0,right=nums.size()-1;//定义初始区间左右值索引
  int mid=(left+(right-left)/2);//定义区间中间的索引
  if(nums[mid]==target) return mid;//如果此时中间值恰好等于寻找值,返回即可
  /*直到区间为0为止*/
 while(left<right){
     if(nums[mid]>target){
         right=mid-1;//如果中间值大于寻找值,更新区间右端点为中间端点-1;
         mid=(left+(right-left)/2);//更新中间端点。
     }
     if(nums[mid]<target){
         left=mid+1;//与上面相反,将区间左端点设为中间端点+1;
         mid=(left+(right-left)/2);//更新中间端点。
     }
     if(nums[mid]==target) return mid;//找到目标值,返回。
 }
 return -1;//未找到目标值,返回-1
    }
};

时间复杂度:O(logn)
空间复杂度:O(1)

总结:

二分查找是一种效率极高的查找方法,是有序且无重复元素的数组中搜索数据的首选。

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝 int len = removeElement(nums, val);

// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for
(int i = 0; i < len; i++) {
print(nums[i]); }

示例 1:

输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] 解释:函数应该返回新的长度 2, 并且
nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums =
[2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。 示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0,
4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

0 <= nums.length <= 100 0 <= nums[i] <= 50 0 <= val <= 100

思路分析:

题目指出,必须用原地算法解决,所以显然定义一个新的数组拷贝是不可行的。
首先我们要明确本题要我们返回的值是一个下标,这个下标是移除了元素之后数组的最后一位下一位的下标。这个下标恰好是新数组的元素的个数。
由于数组是一片连续的空间,所以我们无法直接删除元素,因此我们只能通过覆盖的方法进行。
由于题目指出,元素的顺序可以改变,所以我们可以利用相向指针来遍历数组,右边寻找不等于删除值的数,左边寻找等于删除值的数,最后用右边的值覆盖左边的值,然后重复这个过程,直到i>,此时i恰好指向新数组的下一位。这是第一种方法。
第二种方法是利用快慢指针,首先让两个指针相等
如果较快的那个指针指向的数不等于寻找值,则将快指针指向的数的值赋给慢的指针指向的数。然后让慢指针向前移动一步。(如果快指针等于寻找值的话,慢指针不会前进,所以形成快慢)
在循环的最后,快指针会向前移动一步,重复这个操作,直到快指针遍历完整个数组。 最后慢指针在的地方,恰好是新数组的末尾下一位。这是第二种思路。

代码实现:

class Solution {
public://相向指针解决
    int removeElement(vector<int>& nums, int val) {
int i=0,j=nums.size()-1;//定义相向指针
while(i<=j){
    while(i<=j&&nums[i]!=val)i++;
    while(i<=j&&nums[j]==val)j--;
    if(i<j) nums[i++]=nums[j--];//覆盖完后同时往中间走一步
}
return i;}
};

时间复杂度:O(n),其中n为序列的长度,我们只需遍历一遍序列
空间复杂度:O(1),我们只需要若干变量的空间

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
int i=0,j=0;//定义快慢指针
for(;j<nums.size();j++){
    if(nums[j]!=val)
    nums[i++]=nums[j];//赋值完向前走一步
}
return i;}
};

时间复杂度:O(n),n为序列的长度,我们至多遍历一次序列
空间复杂度:O(1),只需要储存若干变量

总结:

双指针算法对于降低遍历数组的时间复杂度有奇效,往往能将O(n^2)降为O(n)。

977.有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为
[16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]

示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]

思路分析:

本题的nums是一个非递减顺序排序的数组,最简单的办法就是每个值平方后,再排序以下,美滋滋解决。利用c++自带的快速排序,时间复杂度为O(logn).

代码实现:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
for(auto &i:nums)i*=i;
sort(nums.begin(),nums.end());//快排 时间复杂度为nlogn
return nums;
    }
};

时间复杂度:O(logn) 快速排序为logn 遍历数组平方为n
空间复杂度:O(logn) 快排本身需要logn的栈空间

209.长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例 1:
输入:
target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:
target = 4, nums = [1,4,4] 输出:1
示例 3:

输入:
target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

思路分析:

首先最开始判断nums的元素大小,如果为0则返回0.
此题很明显是需要用到滑动窗口的算法,定义两个变量i,j,都指向数组最开头,定义一个辅助变量等于0,同时定义一个set容器来储存子字符串的长度,进入一个循环,循环条件为j小于nums数组的长度,让辅助变量加上nums[j],然后进行判断,如果辅助变量的值大于target,则往set里插入j-i(即字符串的长度),然后让辅助变量减去nums[i]的值,最后再让i++,重复此过程,直到辅助变量的值小于target。
最后判断容器是否为空,如果是,返回0,如果不是,返回set容器的第一个值。(set的特性是里面的元素是有序的)。

代码实现:

class Solution {
public://滑动窗口~~~
    int minSubArrayLen(int target, vector<int>& nums) {
int i=0,j=0,count=0;
if(nums.size()==0) return 0;
set<int> a;
    for(;j<nums.size();){
        count+=nums[j++];
        while(count>=target&&i<j){a.emplace(j-i);
            count-=nums[i++];
        }
        
    }
    if(a.empty()) return 0;
    return *a.begin();

    }
};

时间复杂度:O(n) n为序列的长度,i和j至多各移动n次,至少j移动n次,i不移动
空间复杂度:O(n) n为容器的长度

总结:

滑动窗口的算法是对双指针算法的高阶运用,熟练掌握滑动窗口可以让我们更有效地解决问题。

59.螺旋矩阵II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 1:

代码随想录-数组部分_第1张图片

输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1 输出:[[1]]

思路分析:

本题只需要模拟矩阵生成的过程即好,考察的是对代码的使用,不涉及什么算法,具体过程见实现代码

代码实现:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int t = 0;      // 定义上界
        int b = n-1;    // 定义下界
        int l = 0;      // 定义左界
        int r = n-1;    // 定义右界
        vector<vector<int>> ans(n,vector<int>(n));
        int k=1;
        while(k<=n*n){
            for(int i=l;i<=r;++i,++k) ans[t][i] = k;
            t++;
            for(int i=t;i<=b;++i,++k) ans[i][r] = k;
            r--;
            for(int i=r;i>=l;--i,++k) ans[b][i] = k;
            b--;
            for(int i=b;i>=t;--i,++k) ans[i][l] = k;
            l++;
        }
        return ans;
    }
};

时间复杂度:O(n^2) 遍历整个矩阵的长度
空间复杂度: O(n^2) 矩阵的容量

总结:

我们除了算法,也得提升自己的代码实现能力和模拟现实的过程。

刷题总结:

最后以一句话结尾今天的CSDN:

我不如很多人,当然也有很多人不如,大风吹倒梧桐树,各有旁人论短长,你所见即我,好与坏我都不反驳。

创造不易,望佬们给个点赞,点点关注,点点收藏。

你可能感兴趣的:(代码随想录,算法,leetcode,数据结构)