代码随想录刷题笔记1——数组

二分查找

例题:704(简单)二分查找

需要注意的要点有:

  1. 数组的大小:通过nums.size()来获得(在C版本中会直接有参数numsSize)。
  2. 尽量使用左闭右闭区间进行二分查找**(left<=right),这样的话无论是在哪个区间范围内都是要增减1个下标的操作**,不用多操心是哪一边的区间范围;但是使用该方法时,需要注意初始化right需要numsSize-1

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {return mid;}
            else if (nums[mid] < target) {left = mid + 1;}
            else {right = mid - 1;}
        }
        return -1;
    }
};

C版本:

int search(int* nums, int numsSize, int target){
    int left = 0, right = numsSize - 1;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {return mid;}
        else if (nums[mid] < target) {left = mid + 1;}
        else {right = mid - 1;}
    }
    return -1;
}
}

拓展题:35(简单)搜索插入位置

注意要点:

  1. 这一题展示一下左闭右开区间的运算(left<right),此时,如果target在右边的区间范围内,由于left也包含在区间内,所以left=mid+1;如果在左边,由于right不包括在区间内,则right=mid;
  2. 如果没有找到,那么需要考虑插入的位置,需要判断的有四种情况:在最左边;在最右边以及插入在中间;可以通过输出left和right来判断,最终结果就是都返回left就可以了。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
    int left = 0, right = nums.size();
    while (left < right)
    {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] > target) {right = mid;}
        else if (nums[mid] < target) {left = mid + 1;}
        else {return mid;}
    }
    return left;
    }
};

C版本:

int searchInsert(int* nums, int numsSize, int target){
    int left = 0, right = numsSize;
    while (left < right)
    {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] > target) {right = mid;}
        else if (nums[mid] < target) {left = mid + 1;}
        else {return mid;}
    }
    return left;
}

拓展题:69(简单)x的平方根

注意要点:

  1. 由于是向下取整,所以答案的更新应该在左区间范围内,且应该在更新前就赋值到记录的变量中

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int mySqrt(int x) {
        int left = 0, right = x;
        int ans = 0;
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if ((long long)mid * mid <= x)
            {
                ans = mid;
                left = mid + 1;
            }
            else {right = mid - 1;}
        }
        return ans;
    }
};

C版本:

int mySqrt(int x){
    int left = 0, right = x;
    int ans = 0;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if ((long long)mid * mid <= x )
        {
            ans = mid;
            left = mid + 1;
        }
        else {right = mid - 1;}
    }
    return ans;
}

拓展题:367(简单)有效的完全平方数

注意要点:

  1. 看到算法要求为O(logn),应该快速想起二分法;

下面直接贴出代码:

CPP版本:

class Solution {
public:
    bool isPerfectSquare(int num) {
        int left = 0, right = num;
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            long long square = (long long )mid * mid;
            if (square < num) {left = mid + 1;}
            else if (square > num) {right = mid - 1;}
            else {return 1;}
        }
        return 0;
    }
};

C版本:

bool isPerfectSquare(int num){
    int left = 0, right = num;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        long long square = (long long )mid * mid;
        if (square < num) {left = mid + 1;}
        else if (square > num) {right = mid - 1;}
        else {return 1;}
    }
    return 0;
}

拓展题34(中等)在排序数组中查找元素的第一个和最后一个位置

注意要点:

  1. 在cpp版本中,因为public的函数类型缘故,所以可以很简单的返回例如{-1,-1},可以直接作为返回值输出;而在C版本中,就需要自己创建一个数组,最后赋值给数组并返回数组的首地址
  2. 获得左边界的方法:二分查找,在更新到right的情况下,把right值赋值到创建的左边界记录变量中;反过来,寻求右边界,就是更新left的情况下把left更新到记录右边界的变量中;需要注意,都是在找到了target的情况下进行更新,即nums[mid]==target
  3. 如果最终的记录边界变量有一个未更新,说明没有找到;当且仅当两个边界量的差>1才证明是找到了这个区间,否则则说明没有找到。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftboarder = getLeft(nums, target);
        int rightboarder = getRight(nums, target);
        if (leftboarder == -2 || rightboarder == -2) {return {-1, -1};}
        else if (rightboarder - leftboarder > 1)
        {
            return {leftboarder + 1, rightboarder - 1};
        }
        return {-1, -1};
    }

private:
    int getLeft(vector<int>& nums, int target)
    {
        int left = 0, right = nums.size() - 1;
        int leftboarder = -2;   //记录一下是否找到,没找到就会=-2
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {left = mid + 1;}
            else {right = mid - 1;leftboarder = right;}
        }
        return leftboarder;
    }

    int getRight(vector<int>& nums, int target)
    {
        int left = 0, right = nums.size() - 1;
        int rightboarder = -2;   //记录一下是否找到,没找到就会=-2
        while (left <= right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] > target) {right = mid - 1;}
            else {left = mid + 1;rightboarder = left;}
        }
        return rightboarder;
    }
};

C版本:

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int getLeft(int* nums, int numsSize, int target)
{
    int left = 0, right = numsSize - 1;
    int boarder = -2;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (nums[mid] >= target) {right = mid - 1;boarder = right;}
        else if (nums[mid] < target) {left = mid + 1;}
    }
    return boarder;
}

int getRight(int* nums, int numsSize, int target)
{
    int left = 0, right = numsSize - 1;
    int boarder = -2;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (nums[mid] > target) {right = mid - 1;}
        else if (nums[mid] <= target) {left = mid + 1;boarder = left;}
    }
    return boarder;
}

int* searchRange(int* nums, int numsSize, int target, int* returnSize){
    int left = getLeft(nums, numsSize, target);
    int right = getRight(nums, numsSize, target);
    int* ans = (int* )malloc(sizeof(int) * 2);
    *returnSize = 2;
    if (left == -2 || right == -2) {ans[0] = ans[1] = -1;}
    else if (right - left > 1)
    {
        ans[0] = left + 1;
        ans[1] = right - 1;
    }
    else {ans[0] = ans[1] = -1;}
    return ans;
}

移除元素

例题:27(简单)移除元素

注意要点:

  1. 可以通过双指针(快慢指针的方法),进行数组的删除,慢指针代表当前的元素,快指针则是遍历所有元素;遍历过程中根据题目要求判断是否要在slow处进行数据拷贝

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;
        for (int fast = 0; fast < nums.size(); fast++)
        {
            if (nums[fast] == val) {continue;}
            else {nums[slow++] = nums[fast];}
        }
        return slow;
    }
};

C版本:

int removeElement(int* nums, int numsSize, int val){
    int slow = 0;
    for (int fast = 0; fast < numsSize; fast++)
    {
        if (nums[fast] == val) {continue;}
        else {nums[slow++] = nums[fast];}
    }
    return slow;
}

拓展题26(简单)删除有序数组中的重复项

注意要点:

  1. 同样使用快慢指针,重复项需要向前比较,所以循环变量从1开始向后遍历;
  2. 依据题意,只有不相等才把快指针的值赋值给慢指针处,同时慢指针可以向后移一位。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (!nums.size()) {return 0;}
        int slow = 1, fast = 1;
        while (fast < nums.size())
        {
            if (nums[fast] != nums[fast - 1])
            {
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
};

C版本:

int removeDuplicates(int* nums, int numsSize){
    if (numsSize == 0) {return 0;}
    int fast = 1, slow = 1;
    while (fast < numsSize) 
    {
        if (nums[fast] != nums[fast-1])
        {
            nums[slow] = nums[fast];
            slow++;
        }
        fast++;
    }
    return slow;
}

拓展题283(简单)移动零

注意要点:

  1. C++中有已经实现的swap函数,直接把要交换的参数填入即可;
  2. 快指针非零,就移动与慢指针交换,且都右移;如果快指针是零,就右移快指针;这样最终慢指针左侧都是处理好的,右侧全是零。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow = 0, fast = 0;
        while (fast < nums.size())
        {
            if (nums[fast])
            {
                swap(nums[slow], nums[fast]);
                slow++;
            }
            fast++;
        }
    }
};

C版本:

void moveZeroes(int* nums, int numsSize){
    int left = 0, right = 0;
    for (right; right < numsSize; right++)
    {
        if (nums[right])
        {
            int tmp = nums[right];
            nums[right] = nums[left];
            nums[left] = tmp;
            left++;
        }
    }
    return nums;
}

拓展题844(简单)比较含退格的字符串

注意要点:

  1. 双指针法,该字符是否被退格只取决于其身后有否相邻退格符,所以遍历方向应为从后向前
  2. 需要记录所需退格数来判断哪一个字符是有效的;s和t都退完才进入比较;
  3. 对于字符串,求取字符串长度,C++用s.length(),而C则是strlen(s)

下面直接贴出代码:

CPP版本:

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int i = s.length() - 1, j = t.length() - 1;
        int skips = 0, skipt = 0;  //记录当前的所需退格数
        while (i >= 0 || j >= 0)
        {
            while (i >= 0)  //处理s中的退格符
            {
                if (s[i] == '#') {skips++;i--;}
                else if (skips) {skips--;i--;}
                else {break;}
            }
            while (j >= 0)  //处理t中的退格符
            {
                if (t[j] == '#') {skipt++;j--;}
                else if (skipt) {skipt--;j--;}
                else {break;}
            }
            if (i >= 0 && j >= 0)  //都还在字符串中,需要比较是否为同一字符
            {
                if (s[i] != t[j]) {return 0;}
            }
            else {if (i >= 0 || j >= 0) {return 0;}}  //判断是否已经全部比较完了,如果还有剩余字符,则说明不相同
            i--;j--;
        }
        return 1;
    }
};

C版本:

bool backspaceCompare(char * s, char * t){
    int left = strlen(s) - 1, right = strlen(t) - 1;
    int count_l = 0, count_r = 0;
    while (left >= 0 || right >= 0)
    {
        while (left >= 0)
        {
            if (s[left] == '#') {left--;count_l++;}
            else if (count_l) {left--;count_l--;}
            else break;
        }
        while (right >= 0)
        {
            if (t[right] == '#') {right--;count_r++;}
            else if (count_r) {right--;count_r--;}
            else break;
        }
        if (left < 0 || right < 0) break;
        else if (s[left] != t[right]) return false;
        left--;
        right--;
    }
    if (left == -1 && right == -1) return true;
    return false;
}

有序数组的平方

例题977(简单)有序数组的平方

注意要点:

  1. 由于已经是升序数组,所以平方后的最大数一定是两侧的数字取其一(绝对值越大,越远离数组中心);
  2. 通过双指针,不断比较当前的两侧平方,大的那个保存到答案数组的最后面。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> ans(nums.size());
        for (int i = 0, j = nums.size() - 1, pos = nums.size() - 1; i <= j;)
        {
            if (nums[i] * nums[i] > nums[j] * nums[j])
            {
                ans[pos--] = nums[i] * nums[i];
                i++;
            }
            else
            {
                ans[pos--] = nums[j] * nums[j];
                j--;
            }
        }
        return ans;
    }
};

C版本:

/**
 1. Note: The returned array must be malloced, assume caller calls free().
 */
int* sortedSquares(int* nums, int numsSize, int* returnSize) {
    int* ans = malloc(sizeof(int) * numsSize);
    *returnSize = numsSize;
    for (int i = 0, j = numsSize - 1, pos = numsSize - 1; i <= j;)
    {
        if (nums[i] * nums[i] > nums[j] * nums[j])
        {
            ans[pos--] = nums[i] * nums[i];
            i++;
        }
        else
        {
            ans[pos--] = nums[j] * nums[j];
            j--;
        }
    }
    return ans;
}

长度最小的子数组

例题209(中等)长度最小的子数组

注意要点:

  1. 由于需要记录最短的子数组长度,所以记录值的初始化一定要初始化为最大值,C++中为INT32_MAX,而C中为INT_MAX
  2. 通过双指针,完成滑动窗口的计算,只有当前的和>=target,进入滑动窗口的计算,计算子数组长度,并且判断是否可以赋值,并减去最左侧的数,完成窗口的滑动

下面直接贴出代码:

CPP版本:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ret = INT32_MAX;
        int count = 0;
        int sum = 0;
        int left = 0, right = 0;
        while (right < nums.size())
        {
            sum += nums[right++];
            while (sum >= target)
            {
                count = right - left;
                ret = min(count, ret);
                sum -= nums[left++];
            }
        }
        return ret == INT32_MAX ? 0 : ret;
    }
};

C版本:

int minSubArrayLen(int target, int* nums, int numsSize){
    int head = 0;
    int sum = 0;
    int result = numsSize+1;
    int sub = 0;
    for (int tail = 0; tail < numsSize; tail++)
    {
        sum += nums[tail];
        while (sum >= target)
        {
            sub = tail - head + 1;
            result = sub < result ? sub : result;
            sum -= nums[head];
            head++;
        }
    }
    return result == numsSize+1 ? 0 : result;
}

螺旋矩阵

例题59(中等):螺旋矩阵II

注意要点:

  1. 本题并不涉及什么算法,主要是容器和模拟;首先C++中,可以使用vector<vector<int>> ans(n, vector<int> (n, 0)),初始化一个n*n的所有元素均为零的二维数组;
  2. 可以通过定义一个loop记录所需旋转次数,并设置当前循环的开始位置来进行旋转,同时在旋转过程中的模拟可以统一采用左开右闭,方便代码编辑。

下面直接贴出代码:

CPP版本:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> ret(n, vector<int> (n, 0));
        int startx = 0, starty = 0;  //记录循环的起始位置
        int loop = n / 2;  //所需的旋转次数
        int offset = 1;  //在循环内的偏置大小,用于控制右开区间的位置
        int count = 1;  //当前的写入大小
        while (loop--)
        {
            int i = startx, j = starty;
            for (j = starty; j < n - offset; j++) {ret[i][j] = count++;}
            for (i = startx; i < n - offset; i++) {ret[i][j] = count++;}
            for (; j > starty; j--) {ret[i][j] = count++;}
            for (; i > startx; i--) {ret[i][j] = count++;}
            startx++;starty++;
            offset++;
        }
        if (n % 2) {ret[startx][starty] = count;}  //如果是奇数,则最后会剩余一个元素
        return ret;
    }
};

C版本:

/**
 1. Return an array of arrays of size *returnSize.
 2. The sizes of the arrays are returned as *returnColumnSizes array.
 3. Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
    int** result = (int** )malloc(sizeof(int*) * n);
    *returnSize = n;
    *returnColumnSizes = (int* )malloc(sizeof(int) * n);
    for (int i = 0; i < n; i++) 
    {
        result[i] = (int* )malloc(sizeof(int) * n);
        memset(result[i], 0, sizeof(int) * n);
        (*returnColumnSizes)[i] = n;
    }
    int startx = 0, starty = 0;
    int loop = n / 2;
    int offset = 1;
    int count = 1;
    int i, j;
    while (loop--)
    {
        i = startx;j = starty;
        for (; j < n - offset; j++) {result[i][j] = count++;}
        for (; i < n - offset; i++) {result[i][j] = count++;}
        for (; j > starty; j--) {result[i][j] = count++;}
        for (; i > startx; i--) {result[i][j] = count++;}
        startx++;starty++;
        offset++;
    }
    if (n % 2) {result[startx][starty] = count;}
    return result;
}

拓展题54(中等)螺旋矩阵

注意要点:

  1. 由于是m*n矩阵,通过loop控制会非常麻烦,因为碰到奇数可能还是需要for循环来填充,需要重新判断;
  2. 可以定义一个方向数组,当按照当前方向递增溢出,就转入下一个方向继续遍历
  3. 当前遍历结束与否,可以通过设置一个遍历的bool数组记录当前位置是否已被使用

下面直接贴出代码:

CPP版本:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (!matrix.size() || !matrix[0].size()) {return {};}
        int rows = matrix.size(), cols = matrix[0].size();
        vector<vector<bool>> visited(rows, vector<bool> (cols, 0));
        int total = rows * cols;
        vector<int> order(total);
        int row = 0, col = 0;
        int directionIndex = 0;
        for (int i = 0; i < total; i++)
        {
            order[i] = matrix[row][col];
            visited[row][col] = 1;
            int nextrow = row + directions[directionIndex][0];
            int nextcol = col + directions[directionIndex][1];
            if (nextrow < 0 || nextrow >= rows || nextcol < 0 || nextcol >= cols || visited[nextrow][nextcol])
            {
                directionIndex = (directionIndex + 1) % 4;
            }
            row += directions[directionIndex][0];
            col += directions[directionIndex][1];
        }
        return order;
    }
private:
    const int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
};

C版本:

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize){
    if (!matrixSize || !matrixColSize[0]) {*returnSize = 0;return NULL;}
    int rows = matrixSize, cols = matrixColSize[0];
    int* ans = (int* )malloc(sizeof(int) * rows * cols);
    bool visited[rows][cols];
    memset(visited, 0, sizeof(visited));
    int row = 0, col = 0;
    int directionIndex = 0;
    *returnSize = rows * cols;
    for (int i = 0; i < rows * cols; i++)
    {
        ans[i] = matrix[row][col];
        visited[row][col] = 1;
        int nextrow = row + direction[directionIndex][0];
        int nextcol = col + direction[directionIndex][1];
        if (nextrow < 0 || nextrow >= rows || nextcol < 0 || nextcol >= cols || visited[nextrow][nextcol])
        {
            directionIndex = (directionIndex + 1) % 4;
        }
        row += direction[directionIndex][0];
        col += direction[directionIndex][1];
    }
    return ans;
}

总结

这里我直接白嫖了代码随想录的一个总结:
数组的一些知识点总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值