【算法】贪心算法及例题

贪心算法

贪心算法是通过局部最优解来达到全局最优解。

 

实例 Jump Game(系列)

Jump Game
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum

jump length is 0, which makes it impossible to reach the last index.

思路:

### 解题思路
自左向右的贪心算法。不断记录从左边0号index元素向右能访问到的最远的index。
首先,自左向右要保证当前的这个元素本身是可以被访问到的,也就是要求i <= max_reach。(如果i > max_reach,则访问不到,return false)
其次,为了减少不必要的计算,一旦max_reach >= len - 1也就是能够抵达最后一个元素,则终止循环跳出这个这个函数。

/*
问题:数组按值跳到终点(有点像大富翁,不过这里数值只是限制了最大值)
有一个非负整数的数组,每个数字表示在当前位置的基础上最多可以走的步数,求判断能不能到达最后一个位置
*/
 
/*
方法一:动态规划
dp[i]表示到达位置i时,之前的跳力还可以走的步数
状态转移方程:dp[i] = max(dp[i-1], nums[i-1]) - 1
初始值dp[0] = 0, nums[i-1]表示i-1位置处的跳力,dp[i]取决于前一个数num[i-1],也取决于前一个数前面的数dp[i-1]
如果当某一个时刻dp数组的值为负了,说明无法抵达当前位置,则直接返回false
*/
class Solution
{
public:
    bool canJump(vector<int>& nums)
    {
        vector<int> dp(nums.size()); //初始化为0,dp[0] = 0;
       
        for (int i = 1; i < nums.size(); i++)
        {
            dp[i] = max(dp[i-1], nums[i-1]) - 1;
            if (dp[i] < 0) return false;
        }
       
        return true;
    }
};
 
/*
方法二:贪心算法
这题最好的解法不是DP,而是贪婪算法Greedy Algorithm,因为我们并不是很关心每一个位置上的剩余步数,我们只希望知道能否到达末尾
 
维护一个变量can_reach,表示最远能到达的位置,初始化为0
从索引为0的数开始扫描,计算最大can_reach(即i+nums[i]的最大值),不断更新最大跳跃距离,
1.当can_reach小于i也就是当前最大跳跃距离小于当前索引(跳不到当前索引处),也就不能继续往后面跳了,return false。
2.终结处,i = num.size()-1时,如果前面的最大步数小于总步数,还是false,如果大于等于,循环结束,return true
*/
class Solution {
public:
    bool canJump(vector<int>& nums) {
        int maxreach = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (i > maxreach) return false;    //此处思维太厉害:表示前面的最大距离无法到这,也就是说无法往下了,直接return
            maxreach = max(maxreach, i + nums[i]);//此处的max是关键:前面的最大跳跃数,更新
        }
        return true;
    }
};

Jump Game II
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Your goal is to reach the last index in the minimum number of jumps.
Example:
Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
Jump 1 step from index 0 to 1, then 3 steps to the last index.
Note:
You can assume that you can always reach the last index.

/*
问题:跳跃游戏(求跳到末尾最少的步数)
方法:贪婪法
我们这里贪的是一个能到达的最远范围,我们遍历当前能跳到的位置,然后根据该位置上的跳力来预测下一步能跳到的最远距离,贪出一个最远的范围,一旦当这个范围到达末尾时,当前所用的步数一定是最小步数
(选取各步中可以跳到的最远位置进行接力)
 
将游戏看成一种接力赛,用cur和next来接力
*/
class Solution
{
public:
    int jump(vector<int>& nums)
    {
        if(nums.empty()) return -1;
       
        int res = 0, n = nums.size();
        int cur = 0, next = 0; //cur和next分别表示当前和之后能到达的最远位置
        int i = 0; //位置索引
        while (next < n - 1)
        {
            cur = next;
            for (; i <= cur; i++) //遍历到当前能跳到的位置
            {
                next = max(next, i + nums[i]);//找区间[prev,cur]中最大的跳力,更新之后能跳到的最远位置
            }
            if (next == cur) 
                return -1; // 如果相等说明next没有更新,即无法越过cur,返回-1
            else
                res++; //跳的步数加1(一个for循环,next的更新代表一次跳跃)
        }//退出循环时,next >= n-1,代表最后一次跳跃
        return res;
    }
};

实例 重构字符串

题解https://blog.csdn.net/qq_41855420/article/details/89913987

算法描述
令字符串长度为n,遍历字符串,统计每个字符出现的次数 count[ch];
如果某个字符的count[ch] > len + 1 / 2,则无解。
在有解的情况下,将字符按照count[ch] 从大到小排序,先填偶数坑,再填奇数坑即可。
因为count[ch] < len + 1 / 2,所以对任意ch,从任意位置开始填入,都不会出现偶数坑和奇数坑撞上的情况。
从大到小排序,是为了避免一种特殊的情况,既len为奇数,且count[ch] == len / 2 + 1的情况。

c++. 贪心算法。 对于这类题,其实很好考虑,往往考虑出现次数最多的数, 其余的数往最多的间隔之间插入,
如aaabb的间隔插入为: a b a b a 当可插入的元素小于最多次数字母的间隔数时,则不可排布。 其他情况无论剩余字母是多少,都可以往间隔中循环插入,从而保证相邻不相同

//思路:
//1.运用桶排序:放入数组中,按重复次数由大到小排序
//2.例:a 1 a 1 a   2*max -1 >length return ""
//2.然后再由大到小拿出来间隔1插入(插入条件如上)
//3.无效条件:big*2 > small +1,无法插空了
class Solution {
public:
    //类内的sort函数需要声明为静态成员函数
    static bool compare(pair<char,int> left,pair<char,int> right)
    {
        return (left.second>right.second);
    }

    string reorganizeString(string S) {
        //step 1,用桶排序放入所有元素
        vector<pair<char,int> > arr (26,make_pair('a',0));
        int length = S.length();
        vector<string> temp (length);
        int maxLength = 0;
        for(char ch:S)
        {
            arr[ch - 'a'].second++;
            arr[ch - 'a'].first = ch;
            if(maxLength<arr[ch - 'a'].second)
                maxLength = arr[ch-'a'].second;
        }
        if (maxLength*2 - 1 >S.length())
            return "";
        sort(arr.begin(),arr.end(),Solution::compare);
        
        //填坑,先填偶数坑(偶数坑是最长的),到最大长度后再填奇数坑
         int offset = 0;
         string result;
        for (int i = 0; i < 26; i++) {
            while(arr[i].second--) {
                temp[offset] = arr[i].first;
                offset += 2;
    
                if (offset >= length) {
                    offset = 1;
                }
            }
        }
        for(string str :temp)
        {
            result +=str;
        }
        return result;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值