【Day 1】代码随想录训练营 | 数组01

704. 二分查找

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


示例 1:

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

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2输出: -1
解释: 2 不存在 nums中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。

        解题思路:遇到有序且无重复的序列,查找值的下标可用二分法。

        注意要点:

                1. 下标截止条件:while,对于左闭右开区间,left<right时均有意义

                2. 下标更新:left不加1会陷入循环。eg: {0, 1}, target = 2, 此时left将不停被复制为0.

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int n = nums.size();

        if ( target < nums[0] || target > nums[n-1] ) {
            return -1;
        }
        int left = 0;
        int right = n;

        // 选择区间为左闭右开

        while (left < right) {
            int middle = (left + right)/2;
            if (target < nums[middle]) {
                right = middle;
            }
            else if (target > nums[middle]) {
                left = middle + 1;  //加1很关键,不加1会导致程序陷入循环
            }
            else 
                return middle;
        }
        return -1;
    }
};

        附加左闭右闭版本,也较为简单。

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

相关题目:

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排列数组
  • -104 <= target <= 104

        左闭右开版本,返回下标时可直接用right优化。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0;
        int right = n;
        // 左闭右开
        while (left < right) {
            int middle = (left + right) / 2;
            if (target == nums[middle]) {
                return middle;
            }
            else if (target < nums[middle]) {
                right = middle;
            }
            else if (target > nums[middle]) {
                left = middle + 1;
            }
        }
        
        /*
        if (left >= n) 
            return n;
        else if (nums[left] < target)
            return left + 1;
        else
            return left;
        */

        // 令人惊奇的是上述代码可以直接优化为right, 仔细考虑一下四种情况即可

        return right;
    }
};

        左闭右闭版本

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

        return right + 1;
    }
};

34. 找元素的起终位置(难)

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

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

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

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

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一:target在数组范围之外,即最左或最右
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况二:在范围之内,且存在于数组中
        if (rightBorder - leftBorder >= 2) return {leftBorder+1, rightBorder-1};
        // 情况三:在范围之内,且不存在于数组中,此时左右边界无意义
        return {-1, -1};
    }

    //二分法找右边界, 左闭右闭版本
    int getRightBorder(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0;
        int right = n-1;
        int rightBorder = -2;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] > target)
                right = mid - 1;
            else {
                left = mid + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }

    //二分法寻找左边界,左闭右闭版本
    int getLeftBorder(vector<int>& nums, int target) {
        int n = nums.size();
        int left = 0;
        int right = n-1;
        int leftBorder = -2;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
                leftBorder = right;
            }
        }
        return leftBorder;
    }


};

69. x的平方根

本质:求出满足k*k <= x的最大值k

注意:mid*mid可能超出int 范围,需改成long long

class Solution {
public:
    int mySqrt(int x) {
        int left = 0;
        int right = x;
        int ans = -1;

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

        return ans;
    }
};

367. 有效完全平方数

给你一个正整数 num 。如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

完全平方数 是一个可以写成某个整数的平方的整数。换句话说,它可以写成某个整数和自身的乘积。

不能使用任何内置的库函数,如  sqrt 。

示例 1:

输入:num = 16
输出:true
解释:返回 true ,因为 4 * 4 = 16 且 4 是一个整数。

示例 2:

输入:num = 14
输出:false
解释:返回 false ,因为 3.742 * 3.742 = 14 但 3.742 不是一个整数。

提示:

  • 1 <= num <= 231 - 1
class Solution {
public:
    bool isPerfectSquare(int num) {
        long long int left = 1;
        long long int right = num;
        while (left <= right) {
            long long int mid = (left + right) / 2;
            if ( mid*mid == num)
                return true;
            else if ( mid*mid < num) {
                left = mid + 1;
            }
            else
                right = mid - 1;
        }
        return false;
    }
};

27. 移除元素

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k。

    示例 1:

    输入:nums = [3,2,2,3], val = 3
    输出:2, nums = [2,2,_,_]
    解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
    你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。

    示例 2:

    输入:nums = [0,1,2,2,3,0,4,2], val = 2
    输出:5, nums = [0,1,4,0,3,_,_,_]
    解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
    注意这五个元素可以任意顺序返回。
    你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
    

    提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

本质:双指针法(快慢指针法)

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

相关题目:

26. 删除重复项

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k .

注意:判定下标时,一定先将出界条件放在前面

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0;
        int fastIndex = 0;
        int n = nums.size();
        while (fastIndex < n) {
            nums[slowIndex] = nums[fastIndex];
            while (fastIndex < n && nums[fastIndex] == nums[slowIndex]) 
                fastIndex++;
            slowIndex++;
        }
        return slowIndex;
    }
};

283. 移动0

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

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

示例 2:

输入: nums = [0]
输出: [0]

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size();
        int slowIndex = 0;
        int fastIndex = 0;
        while (fastIndex < n) {
            if (nums[fastIndex] == 0) {
                fastIndex++;
                continue;
            }
            nums[slowIndex] = nums[fastIndex];
            slowIndex++;
            fastIndex++;
        }
        while (slowIndex < n) {
            nums[slowIndex] = 0;
            slowIndex++;
        }
    }
};

844. 比较含退格的字符串(易错)

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例 1:

输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。

示例 2:

输入:s = "ab##", t = "c#d#"
输出:true
解释:s 和 t 都会变成 ""。

示例 3:

输入:s = "a#c", t = "b"
输出:false
解释:s 会变成 "c",但 t 仍然是 "b"。

提示:

  • 1 <= s.length, t.length <= 200
  • s 和 t 只含有小写字母以及字符 '#'

注意:退格可能会退很多格! 

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int n = s.size();
        int slowIndex = 0;
        int fastIndex = 0;
        while (fastIndex < n){
            if (s[fastIndex] == '#') {
                if (slowIndex > 0)
                    slowIndex--;
            }
            else {
                s[slowIndex] = s[fastIndex];
                slowIndex++;
            }
            fastIndex++;
        }
        int len1 = slowIndex;

        n = t.size();
        slowIndex = 0;
        fastIndex = 0;
        while (fastIndex < n){
            if (t[fastIndex] == '#') {
                if (slowIndex > 0)
                    slowIndex--;
            }
            else {
                t[slowIndex] = t[fastIndex];
                slowIndex++;
            }
            fastIndex++;
        }
        int len2 = slowIndex;

        if (len1 != len2) return false;

        for (int i = 0; i < len1; i++) {
            if (s[i] != t[i])
                return false;
        }

        return true;
    }
};

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

注意:排序时注意变通,有时可以倒序排序,转换正逆向思维,如本题的双指针可以从外向内延申。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();

        // for (int i=0; i<n; i++) {
        //     if (nums[i] >= 0) 
        //         break;
        // }
        // int leftIndex = i;
        // int rightIndex = i;

        vector<int> ans(n, 0);
        int leftIndex = 0;
        int rightIndex = n-1;
        for (int i = n-1; i>-1; i--) {
            if (nums[leftIndex]*nums[leftIndex] > nums[rightIndex]*nums[rightIndex]) {
                ans[i] = nums[leftIndex]*nums[leftIndex];
                leftIndex++;
            }
            else {
                ans[i] =  nums[rightIndex]*nums[rightIndex];
                rightIndex--;
            }
        }
        return ans;
    }
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值