01.02 数组基础(第 03 ~ 04 天)总结

01.02.01 数组基础知识

学习资料:LeetCode 算法笔记

1. 数组

定义:数组(Array),一种线性表数据结构。它使用一组连续的内存空间,来存储一组具有相同类型的数据。
数组是最基础、最简单的数据结构。

特点:可以进行随机访问,使用下标。

多维数组:二维数组是一个由行列数据元素构成的特殊结构,其本质上是以数组作为数据元素的数组,即「数组的数组」。可以将二维数组看做是一个矩阵,并处理矩阵的相关问题,比如转置矩阵、矩阵相加、矩阵相乘等等。

c++中的数组,使用的是一块存储相同类型数据的、连续的内存空间。

int arr[3][4] = {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}};

2. 数组的基本操作

数据结构的操作一般涉及到增、删、改、查共4种情况

2.1 访问元素

使用下标访问第 i i i个元素:

void value(vector<int>& nums, int i){
    // 数组的长度判断需额外引入参数或用std::vector<int>
    if(i>=0 && i < nums.size())
    std::cout << value[i] << std::endl;
}
vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
value(arr, 3);

2.2 查找元素(查)

查找数组中元素值为 v a l val val的位置:

int find(vector<int>& nums, int val){
    for(int i = 0; i < nums.size(); i++){
        if(nums[i] == val)
            return i;
    }
    return -1;
}

vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
std::cout<<(find(arr, 5))<<std::endl;
2.3 插入元素(增)

在数组尾部插入值为val的元素:

vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
val = 4;
arr.push_back(val);
for (int v : arr) {
    std::cout << a << " ";
}

在数组第i个位置插入值为val的元素:

vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
i = 2; val = 4;
arr.insert(arr.begin()+i, val);
for (int v : arr) {
    std::cout << a << " ";
}
2.4 改变元素(改)

将数组中第 i i i个元素值改为:

void change(vector<int>& nums, int i, int val){
    if(i>=0 && i < nums.size())
        nums[i] = val;
}

vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
i = 2; val = 4;
change(arr, i, val);
for (int v : arr) {
    std::cout << a << " ";
}
2.5 删除元素(删)

删除元素分为三种情况:「删除数组尾部元素」、「删除数组第 i i i个位置上的元素」、「基于条件删除元素」。

删除数组尾部元素

  1. 只需将元素计数值减一即可。
vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
arr.pop_back();
for (int v : arr) {
    std::cout << a << " ";
}

删除数组第 i i i个位置上的元素

  1. 先检查下标 i i i 是否合法,即 0 ≤ i ≤ l e n ( n u m s ) − 1 0 \le i \le len(nums) - 1 0ilen(nums)1
  2. 如果下标合法,则将第 i + 1 i + 1 i+1 个位置到第 l e n ( n u m s ) − 1 len(nums) - 1 len(nums)1 位置上的元素依次向左移动。
  3. 删除后修改数组的元素计数值。

c++中vector删除中间元素可用erase方法,输入参数为指向删除位置的迭代器

vector<int> arr = {0, 5, 2, 3, 7, 1, 6};
int i = 3;
arr.erase(arr.begin()+i);//返回抹除位置pos的下一个位置的迭代器
for (int v : arr) {
    std::cout << a << " ";
}

基于条件删除元素:这种操作一般不给定被删元素的位置,而是给出一个条件要求删除满足这个条件的(一个、多个或所有)元素。这类操作也是通过循环检查元素,查找到元素后将其删除。

这里以删除数组中第 1 1 1 个值为 v a l val val 的操作为例。
c++ 的vector没有相应的方法吗,但STL有相应的函数

vector<int> arr = {1, 2, 3, 4, 5, 2, 6};
int val = 2;
auto it = std::find(arr.begin(), arr.end(), val);

if(it != arr.end())
    arr.erase(it);

for (int v : arr) {
    std::cout << a << " ";
}

练习题

0066. 加一
描述:给定一个非负整数数组,数组每一位对应整数的一位数字。

要求:计算整数加 1 1 1 后的结果。

说明

  • 1 ≤ d i g i t s . l e n g t h ≤ 100 1 \le digits.length \le 100 1digits.length100
  • 0 ≤ d i g i t s [ i ] ≤ 9 0 \le digits[i] \le 9 0digits[i]9

分析: 因为digits的长度可能为100,恢复表示的数字在加一不可取。加一要考虑进位问题,尤其是9,或99这种加一就会改变数组长度的情况不可忽略。

//数组长度改变时,使用插入
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
    int a = 1;
    int n = digits.size();
    int i = n-1;
    while(a && i >= 0){
        a += digits[i];
        digits[i] = a%10;
        a /= 10;
        i--;
    }
    if(a) digits.insert(digits.begin(),a);
    return digits;
    }
};

0724. 寻找数组的中心下标
描述:给定一个数组 n u m s nums nums

要求:找到「左侧元素和」与「右侧元素和相等」的位置,若找不到,则返回 − 1 -1 1

说明

  • 1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 \le nums.length \le 10^4 1nums.length104
  • − 1000 ≤ n u m s [ i ] ≤ 1000 -1000 \le nums[i] \le 1000 1000nums[i]1000
class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int total = std::accumulate(nums.begin(), nums.end(), 0);//sum
        int prefix = 0; // prefix sum
        for(int i = 0; i < nums.size(); i++){
            if(prefix == total - nums[i] - prefix)
                return i;// find!
            prefix += nums[i];
        }
        return -1; // don't find.
    }
};

0189. 轮转数组

描述:给定一个数组 n u m s nums nums,再给定一个数字 k k k

要求:将数组中的元素向右移动 k k k 个位置。

说明

  • 1 ≤ n u m s . l e n g t h ≤ 1 0 5 1 \le nums.length \le 10^5 1nums.length105
  • − 2 31 ≤ n u m s [ i ] ≤ 2 31 − 1 -2^{31} \le nums[i] \le 2^{31} - 1 231nums[i]2311
  • 0 ≤ k ≤ 1 0 5 0 \le k \le 10^5 0k105
  • 使用空间复杂度为 O ( 1 ) O(1) O(1) 的原地算法解决这个问题。

思路1:环状替换

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n = nums.size();
        k = k % n;
        if(k == 0) return ;
        // int count = gcd(k, n);
        int count = 0;
        for (int start = 0; count < n; ++start) {
            int current = start;
            int prev = nums[start];
            do {
                int next = (current + k) % n;
                swap(nums[next], prev);
                current = next;
                count++;
            } while (start != current);
        }
    }
};
//时间复杂度O(n),空间复杂度O(1)

思路2:翻转数组

class Solution {
public:
    void reverse(vector<int>& nums, int start, int end) {
        while (start < end) {
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }

    void rotate(vector<int>& nums, int k) {
        k %= nums.size();
        reverse(nums, 0, nums.size() - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.size() - 1);
    }
};
// 时间复杂度O(2n),空间复杂度O(1)

0048. 旋转图像

描述:给定一个 n × n n \times n n×n 大小的二维矩阵(代表图像) m a t r i x matrix matrix

要求:将二维矩阵 m a t r i x matrix matrix 顺时针旋转 90°。

分析:对于矩阵中的第一行,在旋转后,它出现在倒数第一列的位置;第二行,出现在倒数第二列的位置。即matrix [ i ] [ j ] [i][j] [i][j]在旋转后的位置为matrix [ j ] [ n − i − 1 ] [j][n-i-1] [j][ni1]
**思路1:**最简单的方法就是使用一个和matrix相同规模的矩阵存储旋转后的结果

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        auto matrix_new = matrix;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        matrix = matrix_new;
    }
};
//时间复杂度O(n^2),空间复杂度O(n^2)

思路2:一层层,每层调换三次,从外到内

class Solution {
public:
    void rotate(vector<vector<int>>& a) {
        int n = a.size();
        if (n == 1) return ;
        for (int i = 0; i < n / 2; i++) {
            for (int j = i; j < i + n - 2 * i - 1; j++) {
                swap(a[i][j], a[j][n - i - 1]);
                swap(a[i][j], a[n - j - 1][i]);
                swap(a[n - j - 1][i], a[n - i - 1][n - j - 1]);
            }
        }
    }
};

思路3:两次翻转。1:先上下翻转 ( i , j ) − − > ( n − i − 1 , j ) (i,j)--> (n-i-1, j) (i,j)>(ni1,j),再转置 ( n − i − 1 , j ) − − > ( j , n − i − 1 ) (n-i-1, j)-->(j,n-i-1) (ni1,j)>(j,ni1).
2: 先转置 ( i , j ) − − > ( j , i ) (i, j)-->(j,i) (i,j)>(j,i),再水平翻转 ( j , i ) − − > ( j , n − i − 1 ) (j, i)-->(j,n-i-1) (j,i)>(j,ni1).

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size() ;
        //首先进行上下翻转
        for( int i = 0 ; i < n/2 ; i ++ ){
            for (int j = 0; j < n; ++j) {
                swap(matrix[i, j], matrix[n - i - 1, j]); 
            }
        }
        
        //然后进行转置
        for( int i = 0 ; i < n ; i ++ ){
            for( int j = i ; j < n ;j ++ ){
                swap(matrix[i][j],matrix[j][i]);
            }
        }
    }
};

0054. 螺旋矩阵

描述:给定一个 m × n m \times n m×n 大小的二维矩阵 m a t r i x matrix matrix

要求:按照顺时针旋转的顺序,返回矩阵中的所有元素。

思路按层模拟。每层四个方向

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return {};
        }

        int rows = matrix.size(), columns = matrix[0].size();
        vector<int> order;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
        while (left <= right && top <= bottom) {
            for (int column = left; column <= right; column++) {
                order.push_back(matrix[top][column]);
            }
            for (int row = top + 1; row <= bottom; row++) {
                order.push_back(matrix[row][right]);
            }
            if (left < right && top < bottom) {
                for (int column = right - 1; column > left; column--) {
                    order.push_back(matrix[bottom][column]);
                }
                for (int row = bottom; row > top; row--) {
                    order.push_back(matrix[row][left]);
                }
            }
            left++;
            right--;
            top++;
            bottom--;
        }
        return order;
    }
};

0498. 对角线遍历

描述:给定一个大小为 m × n m \times n m×n 的矩阵 m a t mat mat

要求:以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。

思路:直接模拟,其中在同一对角线的元素索引相加相同。

class Solution {
public:
    vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
        int m = mat.size();
        int n = mat[0].size();
        vector<int> res;
        for (int i = 0; i <= m + n - 2; i++) {
            if (i % 2) {
                int x = i < n ? 0 : i - n + 1;
                int y = i < n ? i : n - 1;
                while (x < m && y >= 0) {
                    res.emplace_back(mat[x][y]);
                    x++;
                    y--;
                }
            } else {
                int x = i < m ? i : m - 1;
                int y = i < m ? 0 : i - m + 1;
                while (x >= 0 && y < n) {
                    res.emplace_back(mat[x][y]);
                    x--;
                    y++;
                }
            }
        }
        return res;
    }
};

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值