leetcode算法入门

第一天(二分查找)

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

提示:

你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

思路:1.初始时,设立两个指针left,right分别指向数组最左和最右元素的下标。
2.当left<=right时(说明仍然有工作数组),设置i指针,值为left和right的均值,即指向工作数组的中间位置,并判断所指向元素的值和目标值的大小关系:
①若小于,说明目标值在i指针的右边,故将工作数组变为[i+1,right],重复步骤2
②若大于,说明目标值在i指针的左边,故将工作数组变为[left,i-1],重复步骤2
③若等于,说明找到了,直接返回i值
3.当left>right时,说明已无工作数组,即找不到,所以返回-1。

代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left,right;//分别用于记录左右下标
        left = 0;	//初始时指向最左元素小标
        right = nums.size()-1;	//初始时指向最右元素下标
        while(left<=right){	//存在工作数组时
            int i = (left+right)/2;	//工作指针指向工作数组中间位置
            if(nums[i]<target){	//说明值在工作指针右边,于是更新工作数组
                left = i+1;
            }
            else if(nums[i]>target){	//说明值在工作数组左边,更新工作数组
                right = i-1;
            }
            else		//值刚好在工作指针所在处
                return i;
        }
        return -1;	//当left>right时,说明整个数组都找不到该值
    }
};

需要注意的是,这里是以调用的方式传入一维vector,好处是在方便直接获取数组的长度:nums.size()。如果要在自己的编译器上面判断代码是否正确,需要按如下方式为vector赋值:

int main(){
	int b[]={-1,0,3,5,9,12};	//先定义一个数组
	vector<int> nums (b,b+6);	//把这个数组b的第1到第6个元素的值放入vector
	int tar = 9;
	Solution Sol;	//定义一个Solution实例
	int c = Sol.search(nums,tar);	//使用Sol实例的search对象,并传入对应参数(注意是nums而不是b),得到返回值
	printf("%d",c);
}

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
示例 2:

输入:n = 1, bad = 1
输出:1

提示:

1 <= bad <= n <= 2^31 - 1

思想:
1.n个版本中,前x个是正确的,后面的n-x个是错误的,那么第一个错误的版本就是第x-1个。如果从第一个依次往后判断(每次都判断当前位置i和后一位置i+1),找到第i个是正确,第i+1个错误,那么return i+1;若第一个就错误,返回1。这种类似于顺序查找,当然可以,但时间复杂度太高。
2.本题类似于二分查找(左边数全部小于它,右边数全部大于他),为了找某个位置,这个位置左、右两边的某种性质是不一样的,当然具体问题具体分析,我们以二分查找思想来考虑。
思路:
1.初始时,设立两个指针left,right分别指向数组最左和最右元素的序号(1和n)。
2.当left<right时(说明仍然有工作数组),设置middle指针,值为left和right的均值,即指向工作数组的中间位置,并判断所指向版本是不是错误的:
①若错误:说明middle可能是第1、2、3…个错误的,那么第一个错误的版本一定在区间[left,middle]内,故令right=middle,重复步骤2。
②若正确:说明第一个错误的版本一定在区间[middle+1,right]内,故令left=middle,重复步骤2。
注意:经过不断地循环,最终一定是在越发缩小的错误版本区间内找到了第一个错误的版本(middle所指),若循环条件是left<=right,这时将不断地执行if语句,无法退出循环,故循环条件是left<right。

// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        int left,right;
        left = 1;
        right = n;
        int middle;
        while(left<right){
            middle = left + (right - left)/2;	//这样做运行时不会产生一个right+middle的数,从而避免溢出
            if(isBadVersion(middle)){
                right = middle;
            }
            else{
                left = middle + 1;
            }
        }
        return left;
    }
};

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
示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:

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

提示:

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

思路:1.初始时,设立两个指针left,right分别指向数组最左和最右元素的序号(1和n)。
2.当left<=right时(说明仍然有工作数组),设置middle指针,值为left和right的均值,即指向工作数组的中间位置,并判断所指向元素值和目标值大小情况:
①若大于:说明目标值一定在区间[left,middle-1],即令right=middle-1,重复步骤2。
②若小于:说明目标值一定在区间[middle+1,right],即令left=middle+1,重复步骤2。
③若等于:返回middle
3.若目标值不在数组中,2中的循环执行到最后将退出循环,根据经验(自己手写模拟),此时left的值是目标值将被顺序插入的位置。

代码:

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

第二天(双指针)

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) 的算法解决本问题

思想:已经告知是双指针题目了,说明肯定要设立两个指针(初始一般都是分别指向最左、右侧元素)
指向数组最左、右侧元素。因为数组是按照非递减顺序排序的(且有正有负),所以平方后最大的数要么就是最左侧的数,要么就是最右侧的数。故可以将最两个指针所指元素的绝对值进行比较,选择更大的元素的平方数并按照逆序的顺序放入结果数组中,同时更新指针(left++或right–),重复循环直到没有工作数组。

思路:1.初始时,设立两个指针left,right分别指向数组最左和最右元素的下标(0和nums.size()-1),同时设置指针k指向结果数组当前空闲的最后位置。
2.当left<=right时(说明仍然有工作数组),在绝对值情况下判断nums[left]和nums[right]的大小情况:
①若大于或等于(已经只要求对平方数排序,所以可以等于):将nums[left]的平方数放入结果数组result[k],同时k左移一位,并将left指针右移一位(即left++),重复步骤2。
②若小于:将nums[right]的平方数放入结果数组result[k],同时k左移一位,并将right指针左移一位(即right–);重复步骤2。
③当退出循环时,说明所有排序已经结束,return result即可。

代码:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
    	
        int left,right;
        left=0;
        right=nums.size()-1;
        int k = right;
        vector<int> result(nums.size());
        while(left<=right){
            if(abs(nums[left])>abs(nums[right])){
                result[k--] = nums[left]*nums[left];
                left++;
            }
            else{
                result[k--] = nums[right]*nums[right];
                right--;
            }
        }
        return result;
    }
};

189. 轮转数组轮转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]
示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105

进阶:

尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?

思想:将长度为n的数组右轮转k个位置,可以发现,轮转后,原数组的后k个元素在结果数组的前k个位置,原数组的前n-k个元素在结果数组的后n-k个位置。所以可以直接对整个数组进行翻转,先保证两个“整体”间位置正确。之后发现,两个整体内的元素位置和正确位置是相反的,故再依次对两个整体内的元素进行翻转,为方便操作,最好先定义一个翻转函数。

class Solution {
public:
    void reverse(vector<int>& nums, int start, int end) {	//定义一个翻转函数
        while (start < end) {
            swap(nums[start], nums[end]);
            start += 1;
            end -= 1;
        }
    }

    void rotate(vector<int>& nums, int k) {
        k %= nums.size();	//若右轮转次数大于数组长度,则进行取余操作,避免进行无用的轮转,增加时间开销
        reverse(nums, 0, nums.size() - 1);
        reverse(nums, 0, k - 1);
        reverse(nums, k, nums.size() - 1);
    }
};

第三天(双指针)

283. 移动零

给定一个数组 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

进阶:你能尽量减少完成的操作次数吗?

思想:将数组分为3个区域,左边为处理好的区域,中间为0区域,右边为待处理区域。
通过right指针不断寻找待处理的非零元素并放在处理好的区域的后一位,设置left指针始终指向处理好区域的后一位来帮助操作(即有了一次交换后,再将left后移一位)
思路:
初始时设置两个指针left和right指向第一位,right指针判读当前元素是否是非零元素,若是则与left指针的元素交换并将left指针右移一位(这样在下次交换时能将下一个待处理的非零元素放在正确位置),若不是则将right指针右移,继续寻找。

代码:

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值