[学习报告]《LeetCode零基础指南》(第三讲) 数组

本文探讨了多种算法在处理旋转排序数组、寻找最小值、爬楼梯问题等数组操作中的应用,包括搜索策略如二分查找和线性搜索,以及斐波那契数列和泰波那契数的计算。通过这些算法,可以高效地解决数组中的目标值搜索和数值计算问题。
摘要由CSDN通过智能技术生成


解题报告

搜索旋转排序数组

题目链接:33. 搜索旋转排序数组

题目描述

整数数组 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,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

解题思路1

遍历数组的每一个元素,如果某个元素等于target,返回下标i;遍历完所有元素后没有找到,返回 -1

class Solution {
public:
    int search(vector<int>& nums, int target) {
        for(int i = 0; i < nums.size(); i ++ ){
            if(nums[i] == target){
                return i;
            }
        }
        return -1;
    }
};

(贴通过的截图)
解题思路2

二分查找
翻转数组一定可以分为两个部分,一个部分是有序的,另一个部分是有序或部分有序的
有序部分用二分查找,无序部分又可分为两个部分,如此循环
( 1 ) (1) (1) nums[mid]正好等于 target,返回索引 mid。
( 2 ) (2) (2) [l, mid]`部分有序。
( 3 ) (3) (3) target 在 [l, mid) 之间,r 向左端收缩区间。
( 4 ) (4) (4) target 不在 [l, mid) 之间,l 向右端收缩区间。
( 5 ) (5) (5) [mid, r] 部分有序。
( 6 ) (6) (6) target 在 (mid, r] 之间,l 向右端收缩区间,否则 r 向左端收缩区间。
( 7 ) (7) (7) 没有找到,返回 -1。

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

在这里插入图片描述

搜索旋转排序数组 II

题目链接: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 。
你必须尽可能减少整个操作步骤。

解题思路1

循环遍历数组中的每个元素,若找到某个元素等于 target,返回 true;没有找到返回 false。

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        for(int i = 0; i < nums.size(); i ++ ){
            if(nums[i] == target){
                return true;
            }
        }
        return false;
    }
};

(贴通过的截图)
解题思路2

数组中存在相同的元素,当二分查找时,可能存在 a[l] = a[mid] = a[r] 的情况,此时无法判断 [0, mid] 和区间 [mid+1, n - 1] 哪个是有序的,例如 [1,0,1,1,1]。
可以通过去除数组最右端与最左端重复的元素,恢复二分的二段性,然后就可以使用第一题的方法。
( 1 ) (1) (1) 去除数组最右端与最左端重复的元素。
( 2 ) (2) (2) 与第一题相同。

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

在这里插入图片描述

寻找旋转排序数组中的最小值

题目链接:153. 寻找旋转排序数组中的最小值

题目描述

已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

解题思路1

枚举数组中的每一个数,定义一个 min 存储当前枚举过的数中最小的数。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int min = 5001;
        for(int i = 0; i < nums.size(); i ++ ){
            if(nums[i] < min)
                min = nums[i];
        }
        return min;
    }
};

在这里插入图片描述
解题思路2

二分查找
反转后数组从最小值的左右分为两个部分,左边部分的元素值一定大于右边的元素值,用二分查找的下标 r 标记最小值。
( 1 ) (1) (1) 此时区间 [mid, r] 范围内的元素大于最小值,最小值应在 mid 左侧,r 向左端收缩区间。
( 2 ) (2) (2) 此时区间 [l, mid] 范围内的元素大于最小值,最小值应在 mid 右侧,l 向右端收缩区间。
( 3 ) (3) (3) 此时 l == r 并同时指向最小值,返回最小值。

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

(贴通过的截图)

爬楼梯

题目链接:70. 爬楼梯

题目描述

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

解题思路

初始化方案 f[0] = f[1] = 1,第 i 层的方案等于从第 i - 1 层和第 i - 2 层的方案之和。

class Solution {
public:
    int climbStairs(int n) {
        int f[46];
        f[0] = f[1] = 1;
        for(int i = 2; i <= 45; i ++ ){
            f[i] = f[i - 1] + f[i - 2];
        }
        return f[n];
    }
};

(贴通过的截图)

斐波那契数

题目链接:509. 斐波那契数

题目描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。
给定 n ,请计算 F(n) 。

解题思路

初始化前两个数字 f[0] = 1, f[1] = 1,循环按照递推公式求出 f[n]。

class Solution {
public:
    int fib(int n) {
        int f[31];
        f[0] = 0, f[1]  = 1;
        for(int i = 2; i <= n; i ++ ){
            f[i] = f[i - 1] + f[i - 2];
        }
        return f[n];
    }
};

(贴通过的截图)

第 N 个泰波那契数

题目链接:1137. 第 N 个泰波那契数

题目描述

泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

解题思路

初始化前三个数字,再按递推公式循环求出 f[n]。

class Solution {
public:
    int tribonacci(int n) {
        int f[38];
        f[0] = 0, f[1] = f[2] = 1;
        for(int i = 3; i <= n; i ++ ){
            f[i] = f[i - 3] + f[i - 2] + f[i - 1];
        }
        return f[n];
    }
};

(贴通过的截图)

差的绝对值为 K 的数对数目

题目链接:2006. 差的绝对值为 K 的数对数目

题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回数对 (i, j) 的数目,满足 i < j 且 |nums[i] - nums[j]| == k 。

解题思路

双重循环,枚举两个元素的差值,遇到等于 k 的数对,计数器加 1。

class Solution {
public:
    int countKDifference(vector<int>& nums, int k) {
        int cnt = 0;
        for(int i = 0; i < nums.size(); i ++ ){
            for(int j = i + 1; j < nums.size(); j ++ ){
                if(abs(nums[i] - nums[j]) == k)
                    cnt ++ ;
            }
        }
        return cnt;
    }
};

(贴通过的截图)

猜数字

题目链接:LCP 01. 猜数字

题目描述

小A 和 小B 在玩猜数字。小B 每次从 1, 2, 3 中随机选择一个,小A 每次也从 1, 2, 3 中选择一个猜。他们一共进行三次这个游戏,请返回 小A 猜对了几次?
输入的guess数组为 小A 每次的猜测,answer数组为 小B 每次的选择。guess和answer的长度都等于3

解题思路

分别枚举两个数组的元素,相同计数器加 1。

class Solution {
public:
    int game(vector<int>& guess, vector<int>& answer) {
        int cnt = 0;
        for(int i = 0; i < 3; i ++ ){
            if(guess[i] == answer[i]){
                cnt ++ ;
            }
        }
        return cnt;
    }
};

(贴通过的截图)

拿硬币

题目链接:LCP 06. 拿硬币

题目描述

桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。

解题思路

枚举数组,偶数堆至少拿 coins / 2 次,奇数堆至少拿 (coins + 1) / 2 次,C++整数除法自动向下取整,两个式子可合并为 (coins + 1) / 2。

class Solution {
public:
    int minCount(vector<int>& coins) {
        int cnt = 0;
        for(int i = 0; i < coins.size(); i ++){
            cnt += ((coins[i] + 1) / 2);
        }
        return cnt;
    }
};

(贴通过的截图)

山峰数组的顶部

题目链接:剑指 Offer II 069. 山峰数组的顶部

题目描述

符合下列属性的数组 arr 称为 山峰数组(山脉数组) :
arr.length >= 3
存在 i(0 < i < arr.length - 1)使得:
arr[0] < arr[1] < … arr[i-1] < arr[i]
arr[i] > arr[i+1] > … > arr[arr.length - 1]
给定由整数组成的山峰数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i ,即山峰顶部。

解题思路

枚举每个数组元素,满足 arr[i - 1] < arr[i] << arr[i + 1] 即是山顶。注意不要越界。

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
        int loc = -1;
        for(int i = 1; i < arr.size() - 1; i ++ ){
            if(arr[i - 1] < arr[i] && arr[i] > arr[i + 1]){
                loc = i;
                break;
            }
        }
        return loc;
    }
};

(贴通过的截图)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值