【数组】数据结构与算法——代码随想录

(一)理论基础
数组时存放在连续内存空间上相同类型数据的集合
注意点:
a.数组下标都是从0开始
b.数组内存空间的地址是连续的
(二)相关例题
1.二分查找
二分查找
题目中数组的特点是有序
步骤:
a.定义区间的两端
d.对区间中的具体数值进行条件判断

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;                                  //定义区间的两端
        int right = nums.size()-1;
        while(left<=right){                            //对目标值和中值进行判断,3种情况
            int middle = left + (right - left)/2;
            if(nums[middle]>target){
                right = middle;
            }else if(nums[middle]<target){
                left = middle;
            }else{
                return middle;
            }
        }
        return -1;
    }
};

2.移除元素
移除元素
要求:原地移除所有数值等于目标值的元素
步骤:
a.定义两个指针,一个用来遍历,一个用来记录剩下的元素
b.遍历时对具体数值的判断
(刚开始犯的错误是,判断当值相等时怎么删除,然而在数组的存储中用值覆盖值就自然而然的删除了)

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;                   //用来记录数组长度
        for(int i = 0; i<nums.size();i++){   //for 循环中也相当于一个下标指针
            if(nums[i]!=val){
                nums[slowIndex++] = nums[i];

            }else{
            }
        }
        return slowIndex;

    }
};

3.有序数组的平方
有序数组的平方
最简单的想法,每个数平方再排序;
数组平方后还是有一定的顺序
步骤:
a.定义一个新的数组
b.计算数组两端的平方值
c.比较两数的平方值
d.将较大的先放入数组后面,更新指针的具体值
数组的下标和数组的长度要做注意区分
错误示范
解决办法再额外再定义一个新的数组

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int k = nums.size()-1;k!=-1;k--){
            int nums1 = nums[0]*nums[0];
            int nums2 = nums[k]*nums[k];
            if(nums1<nums2){
                nums[k] = nums2;
            }else{
                nums[0] = nums[k];  //错误点在自己打乱了数组元素的顺序,交换了两个元素的值
                nums[k] = nums1;
            }
        }
        return nums;
    }
};

在这里插入图片描述

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int k = nums.size() - 1;
        vector<int>result(nums.size(),0);
        for(int i = 0,j = nums.size()-1;i<=j;){ //如果是小于的话会导致最后一个元素的排序上出错
            if(nums[i]*nums[i]<nums[j]*nums[j]){
                result[k--] = nums[j]*nums[j];
                j--;
            }
            else{
                result[k--] = nums[i]*nums[i];
                i++;
            }
        }
        return result;
    }
};

4.长度最小的子数组
长度最小的子数组
滑动窗口,不断调整子序列的起始位置
步骤:
窗内元素的移动,
窗口起始位置的移动,
窗口结束位置的移动。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;    //最大的int整数,是为了比较子串的长度
        int sum = 0;
        int i = 0;
        int subLength = 0;
        for(int j = 0;j<nums.size();j++){
            sum +=nums[j];          //先固定滑动窗口的初始端,累加
            while(sum>=target){     //再固定滑动窗口的末尾端,减少元素数目
                subLength = j-i+1;  
                result = result <subLength?result:subLength;
                sum-=nums[i++];
            }
        }
    //如果result没有被赋值,就返回0,说明没有符合条件的子序列
    return result == INT32_MAX?0:result;//
    }
};

5.螺旋矩阵
螺旋矩阵
每一个方向都要坚持相同的开闭方式
步骤:

vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组

a.对n 数组的分析
奇偶性判断;
b.对螺旋赋值的分析
螺旋圈数:n/2的整数部分;
绕圈需要的变量:起始位置、四个方向的遍历、边界条件的判断、每一圈的偏移量

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>>res(n,vector<int>(n,0));//定义一个二维数组
        int count = 1;//赋值元素的值
        int offset = 1;//每绕一圈边界就会有一个偏移量
        int startx = 0, starty = 0;//绕圈的起始位置
        int loop = n/2;//绕圈的圈数
        int mid = n/2;//对中间值单独处理
        while(loop--){
            int x = startx, y = starty;//每一圈的起点
            //从左到右遍历赋值(左闭右开),尝试过程中在y处加了等号,就会导致角落处元素被重复赋值
            for(;y<n-offset;y++){
                res[x][y] = count;
                count++;
            }
            //从上到下赋值
            for(;x<n-offset;x++){
                res[x][y] = count;
                count++;
            }
            //从右到左赋值
            for(;y>starty;y--){
                res[x][y] = count;
                count++;
            }
            //从下到上赋值
            for(;x>startx;x--){
                res[x][y] = count;
                count++;
            }
            //循环结束后更新相关的变量,(写代码时被忽略过)
            startx++;
            starty++;
            offset++;
        }
        if(n%2){
            res[mid][mid] = count;
        }
        return res;
    }
};
### 关于KMP算法的不同资源讲解对比 #### 王道教材中的KMP算法介绍 王道教材对于KMP算法的描述集中在该算法的核心概念及其应用场景上。KMP算法由Knuth、Morris和Pratt三位学者发明,名称来源于三人的姓氏首字母组合[^1]。此算法主要用于字符串模式匹配领域,在遇到字符不匹配的情形下,能够通过已知的部分匹配信息调整搜索起点而无需重新开始整个过程。 为了实现这一点,KMP依赖于`next`数组(有时也称为部分匹配表),用于记录模式串中各个位置之前的子串的最大相同前后缀长度。这种机制使得一旦发生失配情况,可以根据之前计算好的最大公共前后缀的信息快速定位新的起始点继续查找工作,从而提高了效率。 #### 代码随想录里的KMP算法解析 相比之下,《代码随想录》一书则更加注重实践操作以及具体编码技巧方面的指导。书中不仅解释了什么是`next`数组——即用来存储针对每一个字符的位置所能找到最长相等真前缀真后缀交集大小的数据结构;还特别强调理解和记忆之间的区别,指出仅仅依靠机械性的背诵难以长久保持知识点的记忆效果。 此外,《代码随想录》提供了详细的构建`next`数组的方法论,并且给出了完整的C++语言版本的具体实现案例,帮助读者更好地掌握这一经典算法的实际运用方式[^4]。 综上所述,两份资料各有侧重:前者偏向理论基础建设,后者更倾向于实战演练和技术细节分享。两者结合起来学习可以帮助使用者既获得扎实的知识体系又具备实际解决问题的能力。 ```cpp // 来自《代码随想录》,展示了如何创建 next 数组并应用于 KMP 字符串匹配算法 void getNext (int* next, const string& s){ next[0] = 0; int j = 0; for(int i = 1; i < s.size(); i++){ while(j > 0 && s[i] != s[j]) { j = next[j - 1]; } if(s[i] == s[j]) { j++; } next[i] = j; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值