刷题记录Day1-数组(二分查找,移除元素)

刷题记录Day1(二分查找,移除元素)

简单记录


文章目录

  • 刷题记录Day1(二分查找,移除元素)
  • 前言
  • 一、二分查找
    • 1. 704二分查找
    • 2. 35搜索插入位置
    • 3. 34在排序数组中查找元素的第一个和最后一个位置
    • 4. 69x的平方根
    • 5. 81搜索旋转排序数组 II
  • 二、移除元素
    • 1. 27移除元素
    • 2. 26删除排序数组中的重复项
    • 3. 283移动零
  • 总结


前言

题目来源:leetcode
刷题顺序:代码随想录
刷题工具:VSCode+leetcode插件
补充:延毕时间充裕,会结合LeetCode 101: A LeetCode Grinding Guide (C++ Version)相似题目一起做。


一、二分查找

二分查找也常被称为二分法或者折半查找,每次查找时通过将待查找区间分成两部分并只取一部分继续查找,将查找的复杂度大大减少。对于一个长度为O(n) 的数组,二分查找的时间复杂度为O(logn)。

1. 704二分查找

题目:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

代码:

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

要注意二分查找中的区间问题:
如果题目为左闭右闭。那么while的范围应该在l<=r,每次右边缩减范围时应该将r = middle-1。
如果题目为左闭右开。那么while范围是l

2. 35搜索插入位置

题目:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

示例:

输入: nums = [1,3,5,6], target = 2
输出: 1

代码:

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

3. 34在排序数组中查找元素的第一个和最后一个位置

题目:
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例:

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

代码:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int l =0, r = nums.size()-1;
        while (l <= r){
            int m = (l+r)/2;
            if ( nums[m] > target){
                r = m-1;
            }
            else if (nums[m] < target){
                l = m+1;
            }
            else{
                int first = m, last = m;
                while (first >=0 && nums[first] == target){
                    --first;
                }
                while (last <= nums.size()-1 && nums[last] == target){
                    ++last;
                }
                return {first+1, last-1};
            }
        }
        return {-1, -1};
    }
};

4. 69x的平方根

题目:
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例:

输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

代码:

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) return 0;
        int l = 1, r= x;
        while (l <= r){
            int m =l+(r-l)/2;  //注意,这里要用m=l+(r-l)/2;
            int sqrt = x/m;
            if(sqrt < m){
                r = m-1;
            }
            else if (sqrt > m){
                l = m+1;
            }
            else return m;
        }
        return r;
    }
};

注意:这道题不能用m=(l+r)/2,因为l+r可能会出现超出int范围的情况。这么看来一般情况下还是用m=l+(r-l)/2比较保险。

5. 81搜索旋转排序数组 II

题目:
已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
你必须尽可能减少整个操作步骤。

示例:

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

代码:

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        int l = 0, r = nums.size()-1;
        while (l <= r){
            int m = (l+r)/2;
            if (nums[m] == target){  //找到了直接返回成功
                return true;
            }
           //这里有三个判断条件,如果mid大于左端,说明左半边是升序,如果mid小于左端,说明右半边是升序。如果相等
           //将无法判断哪半边升序,所以将左端+1。     
            if (nums[m] > nums[l]){
                if (nums[m] > target && target >= nums[l]){
                    r = m-1;
                }
                else{
                    l = m+1;
                }
            }
            else if (nums[m] < nums[l]){
                if (nums[m]< target && target <= nums[r]){
                    l = m+1;
                }
                else{
                    r = m-1;
                }
            }
            else{
                ++l;
            }
        }
        return false;
    } 
};

这道题要好好理解一下,因为有相同元素的存在。

二、移除元素

1. 27移除元素

题目:
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

代码1 (双指针,一快一慢):

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){
                nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
};

代码2(自己的解法,也用的双指针,一前一后):

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        if (nums.size() == 0){
            return 0;
        }
        int l = 0, r = nums.size()-1;
        while (l < r){
            if (nums[l] == val){
                nums[l] = nums[r];
                nums[r] = val;
                --r;
            }
            else{
                ++l;
            }  
        }
        if (nums[l] == val){
            --l;
        }
        return l+1;
    }
};

2. 26删除排序数组中的重复项

题目:

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。

示例:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

代码:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        size_t s = 0;
        for (size_t f = 0; f <nums.size(); ++f){
            if (nums[f] != nums[s]){
                nums[++s] = nums[f];
            }
        }
        return s+1;

    }
};

快慢指针太好用了。

3. 283移动零

题目:
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

代码:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        size_t s = 0;
        for (size_t f = 0; f <nums.size(); ++f){
            if (nums[f] != 0){
                nums[s++] = nums[f];
            }
        }
        for (size_t i =s; i < nums.size(); ++i){
            nums[i] = 0;
        }
    }
};

题解上用的swap,性能应该比我这个方法好。偷懒了。

还有两道不想做了。


总结

提示:如有错误欢迎指正交流。

本日的刷题主要集中在数组方面:
1.学习了二分查找,重点注意区间的开闭。
2.学习了删除元素,使用双指针的方法。
3.算法思路能写个七七八八,但每次都处理不好边界情况,还经常出现数组越界的情况,要注意。

你可能感兴趣的:(刷题记录,c++,c语言,leetcode,数据结构,算法)