代码随想录算法训练营Day1 |704. 二分查找、27. 移除元素、977.有序数组的平方

数组理论基础:****************************************************************************************

C++数组内存地址连续,Java数组寻址交给虚拟机。

704.二分查找

mid=(left+right)>>1的含义

右移运算符>>,运算结果正好能对应一个整数的二分之一值,这就正好能代替数学上的除2运算,但是比除2运算要快。

mid=(left+right)>>1相当于mid=(left+right)/2

也可写成:  int mid =left + ((right-left)>>1);

计算中点:

在二分查找算法中,计算中点的正确公式是: mid = left + (right - left) / 2 这个公式用于计算 left 和 right 之间的中点,下面是详细解释: 公式解释: left + (right - left) / 2 实际上是 (left + right) / 2 的一种避免溢出的方式。 这里,right - left 计算了 left 和 right 之间的距离,然后 (right - left) / 2 计算了这个距离的一半。 将这一半的距离加到 left 上,可以得到 left 和 right 的中点。 防止溢出: 当 left 和 right 的值非常大时,直接使用 (left + right) / 2 可能会导致溢出。特别是在整数范围有限的编程语言中,这种溢出是比较常见的问题。 使用 left + (right - left) / 2 避免了直接将 left 和 right 相加,从而减小了溢出的风险,因为 right - left 的值通常比 left 和 right 的和要小得多。 示例 假设 left 为 2^31-1(即最大的 32 位整数),right 也是 2^31-1。如果直接计算 (left + right) / 2,会导致整数溢出: left + right = 2 * (2^31-1) = 2^32-2,这超出了 32 位整数的范围。 但如果使用 left + (right - left) / 2: right - left = 0 (right - left) / 2 = 0 left + (right - left) / 2 = 2^31-1 不会导致溢出,因为 right - left 不会大到超出整型范围。

left = 0,right = 6,mid = 3

left = 0,right = 2,mid = 1

left = 1,right = 2,mid = 1

left = 0,right = 1,mid = 1;

left = 0,right = 0,mid = 0;

int search(vector<int>& nums, int target) {
        // 数组有序,使用二分查找,左闭右开确定边界
        int length = nums.size();
        // 数组只有一个元素的特殊情况
        if (length == 1) {
            if(nums[0] == target) {
                return 0;
            }
            else {
                return -1;
            }
        }

        // 每次中值取左,(6、7都是3)
        int mid = length/2;
        int left = 0,right = length-1;

        int index = -1;
        while(left <= right) {

            if( nums[mid] == target) {
                index = mid;
                break;
            }
            else if (nums[mid] > target) {
                // 取左侧的区间,左闭右开
                right = mid - 1;
                mid = (left+right)/2;
            }
            else {
                // 当前值小于target,取右侧的区间,左闭右开
                left = mid;
                mid = (left + right)/2;
            }
            
            // 排除特殊情况
            if(right == left + 1) {
                if(nums[left] == target) {
                    return left;
                }
                break;
            }
        }

        return index;

    }

0 1 2 3 4 5 ,target = 5

left = 0,right = 6,mid = 3

left = 3,right = 6,mid = 4

left = 4,right = 6,mid = 5(可以

0 1 2 3 4 5 ,target = 0

left = 0,right = 6,mid = 3

left = 0,right = 3,mid = 1

left = 0,right = 1,mid = 0(可以

0 1 2 3 4 5 ,target = 6

left = 0,right = 6,mid = 3

left = 3,right = 6,mid = 4

left = 4,right = 6,mid = 5

left = 5,right = 6,mid = 5

0 1 2 3 4 5 ,target = -1

left = 0,right = 6,mid = 3

left = 0,right = 3,mid = 1

left = 0,right = 1,mid = 0

class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 数组有序,使用二分查找,左闭右开确定边界
        int length = nums.size();
        // 数组只有一个元素的特殊情况
        if (length == 1) {
            if(nums[0] == target) {
                return 0;
            }
            else {
                return -1;
            }
        }

        // 每次中值取左,(6、7都是3)
        int mid = length/2;
        int left = 0,right = length;
        int index = -1;

        do {

            if( nums[mid] == target) {
                index = mid;
                break;
            }
            else if (nums[mid] > target) {
                // 取左侧的区间,左闭右开
                right = mid;
                mid = (left+right)/2;
            }
            else {
                // 当前值小于target,取右侧的区间,左闭右开
                left = mid;
                mid = (left + right)/2;
            }

        } while(left <= right-1)


        return index;

    }
};

class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 数组有序,使用二分查找,左闭右开确定边界
        int length = nums.size();
        // 数组只有一个元素的特殊情况
        if (length == 1) {
            if(nums[0] == target) {
                return 0;
            }
            else {
                return -1;
            }
        }


        int left = 0,right = length;
        int index = -1;

        while(left<right) {
            int mid =left + ((right-left)>>1);

            if( nums[mid] == target) {
                index = mid;
                break;
            }
            else if (nums[mid] > target) {
                // 取左侧的区间,左闭右开
                right = mid;
            }
            else {
                // 当前值小于target,取右侧的区间,左闭右开,注意当前的mid不会再取到,因此left需要由mid+1
                left = mid + 1;
            }

        } 


        return index;

    }
};

27.移除元素

注意读题,返回的是不等于target的值。双指针法

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 暴力:新建数组,遍历旧数组,修改新数组
        // 双指针,左指针保存当前数组位置,右指针保存探索位置
        int left = 0,right = 0;
        int result = 0;
        while(right < nums.size()) {
            if (nums[right] != val) {
                // 保留当前的数值,并且给nums[left]赋值
                nums[left] = nums[right];
                left ++;
                right ++;
            } else {
                result ++;
                right ++;
            }
        }
        return nums.size() - result;
    }
};

977.有序数组的平方

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> res;
        int left = 0,right = nums.size()-1;

        while (left <= right){
            // 两个指针向中间靠拢,先插大值,再插小值
            if (abs(nums[left]) >= abs(nums[right]) ){
                res.push_back( pow(nums[left],2));
                left++;
            } else{
                res.push_back( pow(nums[right],2));
                right --;
            }
        }

        reverse(res.begin(),res.end());

        return res;
    }
};

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务