【算法——数组】

目录

1.简单

1.1删除排序数组中的重复项(计数法)

1.2移除元素(计数法)

 2.中等

2.1盛最多水的容器(双指针)

2.2最接近的三数之和(双指针)

2.3 下一个排序

2.4搜索旋转排序数组(二分法)

2.5在排序数组中查找元素的第一个和最后一个位置(二分法)

2.6旋转图像

2.7螺旋矩阵

2.8合并区间

2.9螺旋矩阵Ⅱ

3.困难

3.1缺失的第一个正数

3.2插入区间

1.简单

1.1删除排序数组中的重复项(计数法)

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
解法:设置count指针,统计当前重复的数量,且元素向前移count个单位

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

执行用时 :16 ms, 在所有 C++ 提交中击败了87.07%的用户  

内存消耗 :10 MB, 在所有 C++ 提交中击败了27.10%的用户

1.2移除元素(计数法)

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element
计数法

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

 2.中等

2.1盛最多水的容器(双指针)

给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器,且 n 的值至少为 2。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water

int maxArea(vector<int>& height) {
    int left = 0, right = height.size() - 1;
    int maxArea = 0;
    int temp = 0;
    while (left < right){
        if(height[left] > height[right]){  //right小了,需要找更大的
            temp = height[right] * (right - left);
            right--;
        } else if(height[left] < height[right]){//left小了,需要找更大的
            temp = height[left] * (right - left);
            left++;
        } else{
            temp = height[left] * (right - left);
            left++;
            right--;
        }
        maxArea = max(maxArea, temp);
    }
    return maxArea;
}

2.2最接近的三数之和(双指针)

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum-closest

int threeSumClosest(vector<int>& nums, int target) {
    sort(nums.begin(), nums.end());
    int closest = nums[0] + nums[1] + nums[2];
    for(int i = 0; i < nums.size(); i++){
        int left = i + 1, right = nums.size() - 1;
        while(left < right){
            int sum = nums[i] + nums[left] + nums[right];
            if(sum == target)
                return target;
            if(sum < target){
                left++;
            } else{
                right--;
            }
            //存储更接近target的sum
            if(abs(target - sum) < abs(target - closest)){
                closest = sum;
            }
        }
    }
    return closest;
}

2.3 下一个排序

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-permutation
抄了一张图,仔细想想确实是那么回事。

 

void nextPermutation(vector<int>& nums) {
    int i = nums.size() - 2;
    while(i >= 0){
        if(nums[i] >= nums[i + 1]){
            i--;
        } else{
            int j = i + 1;
            while(j < nums.size()){
                if(nums[j] > nums[i])
                    j++;
                else
                    break;
            }
            swap(nums[i], nums[j - 1]);
            sort(nums.begin() + i + 1, nums.end());
            return;
        }
    }
    sort(nums.begin(), nums.end());
}

执行用时 :4 ms, 在所有 C++ 提交中击败了94.73%的用户 

2.4搜索旋转排序数组(二分法)

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
根据题意O(log n),肯定是采用二分法,但是不是完全排序的,所以要找规律,主要分为以下三种情况:

  1. (4),5,6,(7),0,1,(2)        mid 比 left 和 right 都大,且左半边有序
  2. (6),7,0,(1),2,4,(5)        mid 比 left 和 right 都小,且右半边有序
  3. 0,1,2,4,5,6,7(已经排序) 
int search(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while(left <= right){
        int mid = (left + right) / 2;

        if(nums[mid] == target)
            return mid;
        if(nums[left] == target)
            return left;
        if(nums[right] == target)
            return right;

        if(nums[mid] > nums[left] && nums[mid] > nums[right]){//第一种情况
            if(target > nums[left] && target < nums[mid]){
                right = mid - 1;
            } else{
                left = mid + 1;
            }
        } else if(nums[mid] < nums[left] && nums[mid] < nums[right]){//第二种情况
            if(target > nums[mid] && target < nums[right]){
                left = mid + 1;
            } else{
                right = mid - 1;
            }
        } else{            //第三种情况
            if(nums[mid] > target){
                right = mid - 1;
            } else{
                left = mid + 1;
            }
        }
    }

    return -1;
}

2.5在排序数组中查找元素的第一个和最后一个位置(二分法)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
最左和最右位置分两次查找,均采用二分法,比如查最左元素时,nums[mid] == target && nums[mid - 1] < target才是正确位置(特殊考虑mid == 0的情况),最右元素同理。

vector<int> searchRange(vector<int>& nums, int target) {
    vector<int> res = {-1, -1};
    //开始位置
    int left = 0, right = nums.size() - 1, mid;
    while(left <= right){
        mid = (left + right) / 2;

        if(mid > 0){
            if(nums[mid] == target){
                if(nums[mid - 1] < target){//成功
                    res[0] = mid;
                    break;
                } else if(nums[mid - 1] == target){
                    right = mid - 1;
                }
            } else if(nums[mid] < target){
                left = mid + 1;
            } else if(nums[mid] > target){
                right = mid - 1;
            }
        } else if(mid == 0){ //防指针越界,特殊考虑
            if(nums[mid] == target){
                res[0] = mid;
                break;
            } else if(nums[mid] < target){
                left = mid + 1;
            } else
                break;
        }
    }
    //结束位置
    left = 0, right = nums.size() - 1;
    while(left <= right){
        mid = (left + right) / 2;

        if(mid < nums.size() - 1){
            if(nums[mid] == target){
                if(nums[mid + 1] > target){ //成功
                    res[1] = mid;
                    break;
                } else if(nums[mid + 1] == target){
                    left = mid + 1;
                }
            } else if(nums[mid] < target){
                left = mid + 1;
            } else if(nums[mid] > target){
                right = mid - 1;
            }
        } else if(mid == nums.size() - 1){ //特殊考虑
            if(nums[mid] == target){
                res[1] = mid;
                break;
            } else if(nums[mid] > target){
                right = mid - 1;
            } else
                break;
        }
    }
    return res;
}

2.6旋转图像

给定一个 n × n 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度。

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-image
先暴力解就完事了,结合学过的矩阵的知识,发现先转置,然后逐行反转,能解决问题而且能快速写出来。

void rotate(vector<vector<int>>& matrix) {
    //先进行转置
    int len = matrix.size();
    if(len <= 1)
        return;
    for (int i = 0; i < len; ++i) {
        for (int j = i + 1; j < len; ++j) {
            swap(matrix[i][j], matrix[j][i]);
        }
    }
    //逐行反转
    for (int i = 0; i < len; ++i) {
        int left = 0, right = len - 1;
        while(left < right){
            swap(matrix[i][left], matrix[i][right]);
            left++;
            right--;
        }
    }
}

2.7螺旋矩阵

给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1:

输入:
[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix
不需要记录是不是已经访问过了,就不断的更新这4个变量:行上界、行下界、列左界、列右界,然后依次按往右、往下、往左、往上的顺序遍历就行了,一旦有边界值交错,就break。

vector<int> spiralOrder(vector<vector<int>>& matrix) {
    vector<int> res;
    int rowBegin = 0, colBegin = 0;
    if(matrix.empty())
        return res;
    int rowLen = matrix.size(), colLen = matrix[0].size();

    while(rowBegin < rowLen && colBegin < colLen){
        for(int i = colBegin; i < colLen; i++){ //往右
            res.push_back(matrix[rowBegin][i]);
        }
        rowBegin++;//缩小行上界
        if(rowBegin >= rowLen)
            break;
        for(int i = rowBegin; i < rowLen; i++){//往下
            res.push_back(matrix[i][colLen - 1]);
        }
        colLen--;//缩小列右界
        if(colLen <= colBegin)
            break;
        for(int i = colLen - 1; i >= colBegin; i--){ //往左
            res.push_back(matrix[rowLen - 1][i]);
        }
        rowLen--;//缩小行下界
        if(rowLen <= rowBegin)
            break;
        for(int i = rowLen - 1; i >= rowBegin; i--){ //往上
            res.push_back(matrix[i][colBegin]);
        }
        colBegin++;//缩小列左界
    }
    return res;
}

2.8合并区间

给出一个区间的集合,请合并所有重叠的区间。

示例 1:

输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-intervals
根据区间左边界排序,首先把第一个区间加到res中去,这样就能保证res一直有值,如果下一个区间的左边界小于当前区间的右边界,合并;循环。合并中需要注意,当前区间右边界是否同时大于下一个区间的左右边界。

vector<vector<int>> merge(vector<vector<int>>& intervals) {
    vector<vector<int>> res;

    if (intervals.empty())
        return res;

    sort(intervals.begin(), intervals.end()); //按第一位排序

    res.push_back(intervals[0]);

    int i = 1;
    while(i < intervals.size()){
        int len = res.size(); //获取当前区间
        if(res[len - 1][1] >= intervals[i][0]){
            if(res[len - 1][1] < intervals[i][1]){
                vector<int> temp = {res[len - 1][0], intervals[i][1]};
                res.pop_back();
                res.push_back(temp);
            }
            i++;
        } else{
            res.push_back(intervals[i]);
            i++;
        }
    }

    return res;
}

2.9螺旋矩阵Ⅱ

给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

输入: 3
输出:
[
 [ 1, 2, 3 ],
 [ 8, 9, 4 ],
 [ 7, 6, 5 ]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix-ii

同螺旋矩阵一,边遍历边改变上下左右边界值。

vector<vector<int>> generateMatrix(int n) {
    vector<vector<int> > res(n, vector<int>(n));
    if (n == 0)
        return res;
    int num = 1;
    int rowBegin = 0, rowEnd = n - 1; //行边界值
    int colBegin = 0, colEnd = n - 1; //列边界值
    while(rowBegin <= rowEnd && colBegin <= colEnd){
        for (int i = colBegin; i <= colEnd; ++i) {
            res[rowBegin][i] = num++;
        }
        rowBegin++;
        for (int i = rowBegin; i <= rowEnd; ++i) {
            res[i][colEnd] = num++;
        }
        colEnd--;
        for (int i = colEnd; i >= colBegin; i--) {
            res[rowEnd][i] = num++;
        }
        rowEnd--;
        for (int i = rowEnd; i >= rowBegin; i--) {
            res[i][colBegin] = num++;
        }
        colBegin++;
    }
    return res;
}

3.困难

3.1缺失的第一个正数

给定一个未排序的整数数组,找出其中没有出现的最小的正整数。

示例 1:

输入: [1,2,0]
输出: 3
示例 2:

输入: [3,4,-1,1]
输出: 2

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-missing-positive
参考大佬的解法:

全文链接:https://leetcode-cn.com/problems/first-missing-positive/solution/tong-pai-xu-python-dai-ma-by-liweiwei1419/

int firstMissingPositive(vector<int>& nums) {
    for (int i = 0; i < nums.size(); i++) {
        while (nums[i] != i + 1) {
            if (nums[i] <= 0 || nums[i] > nums.size() || nums[i] == nums[nums[i] - 1])
                break;
            // 将nums[i] 放置到对应位置上[1,2,3...]
            int idx = nums[i] - 1;
            nums[i] = nums[idx];
            nums[idx] = idx + 1;
        }
    }
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] != (i + 1)) {
            return (i + 1);
        }
    }
    return (nums.size() + 1);
}

3.2插入区间

给出一个无重叠的 ,按照区间起始端点排序的区间列表。

在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

示例 1:

输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/insert-interval
这道题需要考虑的细节会多一些,步骤大概如下:

  1. 先插入一个区间到res中去,这个区间可能是intervals[0],或者newInterval,谁小插入谁
  2. 边往res填充区间边查找newInterval该插入的位置,查找成功后合并newInterval并退出循环,因为接下来该合并intervals里的区间了。
  3. 从第2步退出的位置开始,合并区间和56题方式一样。
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
    vector<vector<int>> res;
    if(intervals.empty()){
        res.push_back(newInterval);
        return res;
    }
    int len = intervals.size();
    int flag = 0;
    //首先插入一个区间到res
    if(newInterval[0] < intervals[0][0]){
        res.push_back(newInterval);
        flag = 1;
    } else{
        res.push_back(intervals[0]);
    }
    int i; //获取插入的位置
    for (i = 0; i < len; ++i) {
        if(flag == 1){
            break;
        }
        int resLen = res.size();
        //满足该条件就说明区间该插在这个位置,并插入该区间
        if(newInterval[0] >= res[resLen - 1][0] && newInterval[0] <= intervals[i][0]){
            if(res[resLen - 1][1] >= newInterval[0]){
                if(res[resLen - 1][1] >= newInterval[1]){
                    flag = 1;
                } else{
                    vector<int> temp = {res[resLen - 1][0], newInterval[1]};
                    res.pop_back();
                    res.push_back(temp);
                    flag = 1;
                }
            } else{
                res.push_back(newInterval);
                flag = 1;
            }
            break; //插入完成,退出循环
        } else{
            //插入intervals里的区间
            if(res[resLen - 1][1] < intervals[i][0])
                res.push_back(intervals[i]);
        }
    }
    //如果区间应该在末尾合并
    if(flag == 0){
        int resLen = res.size();
        if(res[resLen - 1][1] >= newInterval[0]){
            if(res[resLen - 1][1] < newInterval[1]){
                vector<int> temp = {res[resLen - 1][0], newInterval[1]};
                res.pop_back();
                res.push_back(temp);
            }
        } else{
            res.push_back(newInterval);
        }
    }
    //逐步合并intervals中剩余区间
    for (int j = i; j < len; ++j) {
        int resLen = res.size();
        if(res[resLen - 1][1] >= intervals[j][0]){
            if(res[resLen - 1][1] >= intervals[j][1]){
                continue;
            } else{
                vector<int> temp = {res[resLen - 1][0], intervals[j][1]};
                res.pop_back();
                res.push_back(temp);
            }
        } else{
            res.push_back(intervals[j]);
        }
    }
    return res;
}

执行用时:12 ms

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值