数据结构-数组篇

数组理论基础总结:

1.数组是存放在连续内存空间上的相同数据类型数据的集合
  • 数组下标从0开始
  • 数组内存空间的地址是连续的
2.数组的元素不能删除,只能覆盖

3.二维数组的内存空间地址是连续的吗?

不同编程语言的内存管理是不一样的,在C++中二维数组内存空间是连续分布的

数组的经典题目:

/* 
    采用左闭右闭([left,right])写法
    while(left <= right), 因为 left == right 是有意义的
    if(nums[mid] > target) right = mid - 1,因为当前这个nums[mid]值一定不是target
*/
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int leftIndex = 0;
        int rightIndex = nums.size() - 1;
        
        while (leftIndex <= rightIndex) {
            int midIndex = leftIndex + (rightIndex - leftIndex) / 2;
            
            if (nums[midIndex] == target) {
                return midIndex;
            } else if (nums[midIndex] < target) {
                leftIndex = midIndex + 1;
            } else {
                rightIndex = midIndex - 1;
            }
        }
        
        return -1;  // Target not found
    }
};

/*
    双指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
    · 快指针:遍历原数组,寻找新数组的元素
    · 慢指针:指向新数组下标的位置
*/
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex) {
            if(nums[fastIndex] != val) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

/*
    数组其实是有序的,只不过负数平方之后可能成为最大数了
    那么数组平方的最大数就在数组的两端,不是最左边就是最右边,不可能是中间
    此时可以考虑双指针法,i指向起始位置,k指向终止位置
*/
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;
    }
};

/*
    滑动窗口:不断调节子序列的起始位置和终止位置,从而得出我们想要的结果
    · 窗口:满足其和 ≥ target 的长度最小的连续子数组
    · 窗口的起始位置:如果当前窗口的值 ≥ target,窗口就要向前移动了(收缩窗口)
    · 窗口的结束位置:窗口的结束位置就是遍历数组的指针
*/
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0, right = 0, current_sum = 0;
        int min_length = INT_MAX;
        for(; right < nums.size(); ++right) {
            current_sum += nums[right];
            while(current_sum >= target) {
                min_length = min(min_length,right - left + 1);
                current_sum -= nums[left++];
            }
        }
        return (min_length == INT_MAX) ? 0 : min_length;
    }
};

/*
    循环不变量原则,模拟顺时针画矩阵的过程
    · 填充上行->从左到右
    · 填充右列->从上到下
    · 填充下行->从右到左
    · 填充左列->从下到上
*/
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n,vector<int>(n,0));
        int startX = 0,startY = 0;    // 定义每循环一圈的起始位置
        int mid = n / 2;    // 矩阵中心位置,如 n = 3,那么中心位置为(1,1)
        int loop = n / 2;   // 循环的圈数
        int offset = 1;     // 控制每一条边遍历的长度
        int count = 1;      // 用来给矩阵中每一个空格赋值
        int i,j;
        while(loop--) {
            i = startX;
            j = startY;

            // 模拟转圈(左闭右开)
            // Top:从左到右
            for(j;j < n - offset;j++) {
                res[i][j] = count++;
            }
            // Right:从上到下
            for(i;i < n - offset;i++) {
                res[i][j] = count++;
            }
            // Bottom:从右到左
            for(j;j > startY;j--) {
                res[i][j] = count++;
            }
            // Left:从下到上 
            for(i;i > startX;i--) {
                res[i][j] = count++;
            }

            // 往后循环转圈,起始位置+1
            startX++;
            startY++;

            // 边距长度+1
            offset++;
        }

        // 如果 n 为奇数,需要单独给矩阵中心空格赋值
        if(n % 2) {
            res[mid][mid] = count;
        }

        return res;
    }
};

/*
    看到本题,我当时的想法也是那么简单,直接获取区间累加不就行了
    提交后发现超时了,该算法的时间复杂度是O(n * m) m 是查询的次数
*/
#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, a, b;
    cin >> n;
    vector<int> vec(n);
    for (int i = 0; i < n; i++) cin >> vec[i];
    while (cin >> a >> b) {
        int sum = 0;
        // 累加区间 a 到 b 的和
        for (int i = a; i <= b; i++) sum += vec[i];
        cout << sum << endl;
    }
} 

/*
    前缀和:重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数
    例如,我们要统计 vec[i] 这个数组上的区间和,我们先做累加,即p[i]表示下标0到vec[i]累加之和
    vec[i]: 1 2 3 3 2 1 2
     p[i] : 1 3 6 9 11 12 14
*/
#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n, a, b;
    cin >> n;
    vector<int> vec(n);
    vector<int> p(n);
    int presum = 0;
    for (int i = 0; i < n; i++) {
        cin >> vec[i];
        presum += vec[i];
        p[i] = presum;
    }

    while (cin >> a >> b) {
        int sum;
        if (a == 0) sum = p[b];
        else sum = p[b] - p[a - 1];
        cout << sum << endl;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值