Code Random Notes - Array

Summary overview 

Array theory basics

An array is a collection of data of the same type stored in contiguous memory space. as the picture shows:

Notice:

  • Array subscripts all start from 0.
  • The addresses of the array memory space are consecutive

       The addresses of arrays in the memory space are continuous, so when deleting or adding elements, it is inevitable to move the addresses of other elements.

For example, to delete the element with subscript 3, you need to move all elements behind the element with subscript 3, as shown in the figure:

If you use C++, you should pay attention to the difference between vector and array. The underlying implementation of vector is array. Strictly speaking, vector is a container, not an array.

Array elements cannot be deleted, only overwritten.

The two-dimensional array is directly displayed in the picture.

Take C++ as an example. In C++, two-dimensional arrays are continuously distributed .

Let's do an experiment. The C++ test code is as follows:

void test_arr() {
    int array[2][3] = {
		{0, 1, 2},
		{3, 4, 5}
    };
    cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
    cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}

int main() {
    test_arr();
}

 The test address is:

000000CD9F13F968 000000CD9F13F96C 000000CD9F13F970
000000CD9F13F974 000000CD9F13F978 000000CD9F13F97C

The difference between each address is 4, which is 4 bytes. Since this is an int type array, the address difference between two adjacent array elements is 4 bytes. 

Note that the address is in hexadecimal. It can be seen that the two-dimensional array address is a continuous line.

So it can be seen that in C++, two-dimensional arrays are continuous in the address space .


binary search

704. Binary search

Given an n-element sorted (ascending) integer array nums and a target value target, write a function to search the target in nums and return the subscript if the target value exists, otherwise return -1.

Problem-solving ideas 

The idea of ​​dichotomy is simple, because the entire array is ordered and the array is increasing by default .

  • First select the number in the middle of the array and compare it with the target value you need to find.
  • If they are equal, it is best to return the answer directly.
  • if not equal
    • If the middle number is greater than the target value, then all numbers to the right of the middle number are greater than the target value , and all are excluded
    • If the middle number is less than the target value, then all numbers to the left of the middle number are less than the target value , and all are excluded

This is how the binary method performs a quick elimination search. [Binary search] Detailed illustration

The premise of this question is that the array is an ordered array . At the same time, the question also emphasizes that there are no repeated elements in the array , because once there are repeated elements, the element subscript returned by the binary search method may not be unique. These are the prerequisites for using the binary search method. Conditions, when you see that the title description meets the above conditions, you should think about whether you can use the dichotomy method.

The main method of dichotomy is to clearly understand the definition of the interval, and always insist on doing boundary processing according to the definition of the search interval in the loop. Therefore, loop conditions and assignment issues must be unified, that is, loop invariants.

The definition of an interval is an invariant. In a loop, insisting on doing boundary processing according to the definition of the search interval is the loop invariant rule.

When writing dichotomy, there are generally two definitions of intervals, left closed and right closed, that is, [left, right], or left closed, right open, that is, [left, right). 

The two most important points of the dichotomy are : the loop condition and the subsequent interval assignment problem

  • The relationship between left and right in the while loop, is it left <= right or left < right?
  • The relationship between middle and right during the iteration process, is it right = middle - 1 or right = middle

Problem solving code

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1); // 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};
  • Time complexity: O(log n)
  • Space complexity: O(1)

35. Search for the insertion position https://leetcode.cn/problems/search-insert-position/ 34. Find the first and last position of the element in the sorted array https://leetcode.cn/problems/find-first -and-last-position-of-element-in-sorted-array/

 27. Remove elements

Problem-solving ideas 

The elements of the array are continuous in the memory address. An element in the array cannot be deleted individually, but can only be overwritten.

Double pointer method: double pointer algorithm

The work of two for loops is completed under one for loop through a fast pointer and a slow pointer. 

  • Collision pointers: the left and right pointers move closer to the middle;
  • Speed ​​and slow pointers: two pointers on the left and right, one for slow and one for slow;
  • Sliding window: The left and right pointers form a "window", the right pointer continues to expand, and the left pointer shrinks according to conditions.

Three key points:

  • Selecting the starting position of the pointer
  • direction of movement of pointer
  • pointer movement speed

 Problem solving code

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex]; // 将数组从右到左地复制
            }
        }
        return slowIndex;
    }
};

977. Square of ordered array

Problem-solving ideas

Problem solving code

class Solution {
public:

    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> Res(nums.size(),0);
        int k = nums.size()-1;

        //两个指针++和--是有条件的,故不写在for循环里面
        for(int leftIndex = 0, rightIndex = nums.size()-1; leftIndex <= rightIndex;  ){ 
            if((nums[leftIndex]*nums[leftIndex]) > (nums[rightIndex]*nums[rightIndex])){
                Res[k--] = nums[leftIndex]*nums[leftIndex];
                leftIndex++;
            }
            else{
                Res[k--] = nums[rightIndex]*nums[rightIndex];
                rightIndex--;
            }
        }
        return Res;
    }
};

 209. Subarray with minimum length

 Problem-solving ideas

It can be found that the subtlety of the sliding window is to continuously adjust the starting position of the subsequence according to the current subsequence and size.

Problem solving code

/* 时间复杂度:O(n)   空间复杂度:O(1) */
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int head = 0; // 滑动窗口数值之和
        int sum = 0;  // 滑动窗口数值之和
        int subLen = 0;  // 滑动窗口的长度
        int res = nums.size()+1; // 定义最大长度

        for(int tail = 0; tail < nums.size(); tail++){
            sum += nums[tail];

            // 注意这里使用while,每次更新head(起始位置),并不断比较子序列是否符合条件
            while(sum >= target){
                subLen = (tail - head + 1);  // 取子序列的长度
                res = res < subLen ? res : subLen;
                sum -= nums[head++];  // 这里体现出滑动窗口的精髓之处,不断变更head(子序列的起始位置)
            }
        }
        // 如果res没有被赋值的话,就返回0,说明没有符合条件的子序列
        return res == (nums.size()+1) ? 0 : res;
    }
};

 76. Minimum covering substring    hash table maintenance sliding window

Sliding window summary 

Understanding of sliding windows

       The sliding window can also be understood as a kind of double-pointer method! However, this solution is more like the movement of a window, so it is more suitable to call it a sliding window.

      It can be used to solve problems related to sub-elements of arrays/strings , and can convert nested loop problems into single loop problems, thereby reducing time complexity. Therefore, the complexity of the sliding window algorithm is generally O(n).

The following three points are mainly determined:

  • What's inside the window?
  • How to move the starting position of the window?
  • How to move the end position of the window?

template 

1、声明左右两个指针left和right,初始时都指向起始位置 left = right = 0。
2、满足不了条件是(while 窗口内不符合维护的条件),
   right 指针不停地后移以扩大窗口 [left, right]接近目标,
   直到窗口中的序列符合要求。
3、找到一个符合要求的子序列时,停止移动 right的值,
   转而不断移动左端 left 指针以缩小窗口 [left, right],
   直到窗口中的序列不再符合要求。同时,每次增加 left前,都要更新一轮结果。
4、重复第 2 和第 3 步,直到 right 到达序列的尽头。

1寻找最长:
如果窗口满足条件,R向右滑动扩大窗口,更新最优值;
如果窗口不满足条件,L向右缩小窗口。
2寻找最短:
如果窗口满足条件,L向右滑动扩大窗口,更新最优值;
如果窗口不满足条件,R向右缩小窗口。

59. Spiral Matrix II

Problem-solving ideas

Run a straight line to cut off a layer (according to fixed rules, the boundaries are constantly updated)

Filling steps: In each round of the loop:

  • Fills the upper border of the current layer from left to right; updates the upper border, moving one line inward.
  • Fills the right border of the current layer from top to bottom; updates the right border, moving inward one column.
  • Fills the lower border of the current layer from right to left; updates the lower border, moving one line inward.
  • Fills the left border of the current layer from bottom to top; updates the left border, moving inward one column.

Loop until the entire matrix is ​​filled.

Problem solving code

        The code continues to shrink the boundaries of the spiral layer by filling in numbers in the order of top, right, bottom, and left in each cycle, and eventually fills the entire matrix. 

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)); // 创建一个 n x n 的矩阵,所有元素初始化为 0
        int k = 1; // 用于表示要填充的数字,从 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; // 返回填充完毕的矩阵
    }
};

54. The spiral matrix  code is as follows:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.empty() || matrix[0].empty()) {
            return {}; // 如果输入的矩阵是空的,直接返回一个空数组
        }

        int rows = matrix.size(), columns = matrix[0].size();
        int total = rows * columns;
        vector<int> ans(total); // 创建一个数组来存放螺旋顺序的元素

        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;
        int index = 0; // 用于在 ans 数组中定位当前要填充的位置

        while (index < total) {
            // 从左到右填充上边界元素,并将当前元素填充到 ans 数组中
            for (int i = left; i <= right && index < total; ++i) {
                ans[index++] = matrix[top][i]; 
            }
            ++top; // 上边界向内移动一行

            // 从上到下填充右边界元素,并将当前元素填充到 ans 数组中
            for (int i = top; i <= bottom && index < total; ++i) {
                ans[index++] = matrix[i][right]; 
            }
            --right; // 右边界向内移动一列

            // 从右到左填充下边界元素,并将当前元素填充到 ans 数组中
            for (int i = right; i >= left && index < total; --i) {
                ans[index++] = matrix[bottom][i]; 
            }
            --bottom; // 下边界向内移动一行

            // 从下到上填充左边界元素,并将当前元素填充到 ans 数组中
            for (int i = bottom; i >= top && index < total; --i) {
                ans[index++] = matrix[i][left]; 
            }
            ++left; // 左边界向内移动一列
        }
        return ans; // 返回填充好的一维数组
    }
};

Guess you like

Origin blog.csdn.net/weixin_43200943/article/details/132155023