解题报告
搜索旋转排序数组
题目链接: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;
}
};
山峰数组的顶部
题目描述
符合下列属性的数组 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;
}
};