【c/c++算法刷题笔记】—— LeetCode打卡05

【前言】二分复习巩固 。OJ:acwing + leetcode

1 第一个错误的版本

题目描述

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

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

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

样例

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。

笔记

算法思想:参照模板2(l+r>>1 不用 +1),此题应当选择右半边第一个为 true 的版本

代码
// Forward declaration of isBadVersion API.
bool isBadVersion(int version);
class Solution {
public:
    int firstBadVersion(int n) {
        long long  l=1,r=n;       //注意是否可能越界
        while(l<r){
            int mid=l+r>>1;
            if(isBadVersion(mid)) r=mid;
            else l=mid+1;
        }
        return r;
    }
};

2 寻找峰值

题目描述

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞。

说明:

你的解法应该是 O(logN) 时间复杂度的。

样例

示例 1:

输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。
示例 2:

输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。

笔记
  1. 普通解法:判断第一个和最后一个数——>判断中间数字 num[i]大于num[i-1]和num[i+1],返回i。线性,时间复杂度为 o(n), 大佬说是最辣眼睛的做法。。。
  2. 95%的二分题目存在两段性,直接可以看出使用二分法,而此题属于虽不存在此性质,但刚好可以通过二分把答案缩小一半!
  3. 二分 :时间复杂度o(logn) , 不论何种情况一定存在一个峰值 。某个结点小于下一个结点,那么从下一个结点往后,一定有一个峰值,极端情况,当持续递增时,最后一个结点为峰值;某个结点大于下一个结点,则从 0 到此结点种一定存在一个峰值,极端情况为,持续递减时,第一个结点为峰值

在这里插入图片描述

  1. while(l<r) 当 l=r 直接跳出,不进入循环,所以不存在mid = l = r =最后结点的情况
代码

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int l=0,r=nums.size()-1;
        while(l<r){      
            int mid=l+r>>1;
            if(nums[mid+1]>nums[mid]) l=mid+1;
            else r=mid;
        }
        return l;  //边界不需单独判断,若递增到最后直接返回最后一个元素
    }
};

3 寻找重复数

题目描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
说明:

不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。

样例

示例 1:

输入: [1,3,4,2,2]
输出: 2
示例 2:

输入: [3,1,3,4,2]
输出: 3

笔记
  1. 抽屉原理。一分为二,统计左边的个数和右边的个数,至少会有一边个数大于抽屉个数,在这里面找答案,直到l==r
  2. 两边可能同时有答案,也可能一边有答案,至少一边存在答案
代码

普通方法:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n= nums.size();
        int ans;
        int used[n]={0};
        for(int i=0;i<n;i++){
            if(used[nums[i]]==0) used[nums[i]] ++;
            else {
                ans=nums[i];
                break;
            }
        }
        return ans;
    }
};

二分法:


class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n=nums.size()-1;
        int l=1,r=n;   //下标0——size-1 装的 1——n, 下标+1
        while(l<r){
            int mid=l+r>>1;  //l==1 不用l+r+1>>1
           
            int cnt=0;  //统计左边的个数
            for(auto x:nums) 
                if(x>=1 && x<=mid) cnt++;
            if(cnt > mid-l+1) r=mid;
            else l=mid+1;
        }
        return r;     //为避免越界,选左边return r;选右边return l
    }
};

4 H指数 II

题目描述

给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列。编写一个方法,计算出研究者的 h 指数。

h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)至多有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)"

说明:

如果 h 有多有种可能的值 ,h 指数是其中最大的那个。

样例

示例:

输入: citations = [0,1,3,5,6]
输出: 3
解释: 给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 0, 1, 3, 5, 6 次。
由于研究者有 3 篇论文每篇至少被引用了 3 次,其余两篇论文每篇被引用不多于 3 次,所以她的 h 指数是 3。

笔记
  1. 二分法:【至少存在h个数,大于h,求最大的h】
  2. 用模板1,选择左边小那部分继续循环
代码

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

5 P1090 合并果子

题目描述

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n-1n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。

样例

输入 #1
3
1 2 9
输出 #1
15

笔记

哈夫曼树/最小生成树

代码
#include<iostream>
#include<algorithm>
using namespace std;

int main(){
	int n,val[10000],ans=0;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>val[i];
	}
	sort(val,val+n);
	for(int i=0;i<n-1;i++){
		val[i+1]=val[i]+val[i+1];
		ans+=val[i+1];
		int j=i+1,temp=val[i+1];
		while(temp>val[j+1]&&j<n-1){
			val[j]=val[j+1];
			j++;
		}
		val[j]=temp;
	}
	cout<<ans<<endl;
	return 0;
} 
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值