当前位置: 代码网 > it编程>软件设计>算法 > 贪心算法 Greedy Algorithm

贪心算法 Greedy Algorithm

2024年08月01日 算法 我要评论
一般来说,对于规模较小的问题,贪心算法的时间复杂度可以达到O(nlogn)或O(n^2);答:一个问题如果可以用递归的方式分解成若干个子问题,且每个子问题都有明确的最优解(即局部最优),那么这个问题就可以用贪心算法解决。贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。背包问题:给定一组物品和一个背包,每个物品有一定的重量和价值,要求在不超过背包容量的情况下,尽可能多地装入物品。找零问题:给定一定数量的硬币和需要找零的金额,求使用最少的硬币数。

1) 贪心例子

  v2已经不会更新v3因为v3更新过了

几个贪心的例子

dijkstra
// ...
while (!list.isempty()) {
    // 选取当前【距离最小】的顶点
    vertex curr = choosemindistvertex(list);
    // 更新当前顶点邻居距离
    updateneighboursdist(curr);
    // 移除当前顶点
    list.remove(curr);
    // 标记当前顶点已经处理过
    curr.visited = true;
}
prim
// ...
while (!list.isempty()) {
    // 选取当前【距离最小】的顶点
    vertex curr = choosemindistvertex(list);
    // 更新当前顶点邻居距离
    updateneighboursdist(curr);
    // 移除当前顶点
    list.remove(curr);
    // 标记当前顶点已经处理过
    curr.visited = true;
}
kruskal
// ...
while (list.size() < size - 1) {
    // 选取当前【距离最短】的边
    edge poll = queue.poll();
    // 判断两个集合是否相交
    int i = set.find(poll.start);
    int j = set.find(poll.end);
    if (i != j) { // 未相交
        list.add(poll);
        set.union(i, j); // 相交
    }
}

2) 零钱兑换问题

有几个解(零钱兑换 ii)leetcode 518

[1,2,5]  5  暴力递归

public int rec(int index,int[] coins,int remainder){
        //1.情况1:剩余金额 < 0 - 无解
        //2.情况2:剩余金额 > 0 - 继续递归
        //3.情况3:剩余金额 = 0 - 有解
        if(remainder < 0){
            return 0;
        }
        else if(remainder == 0){
            return 1;
        }
        else{
            int count = 0;
            for(int i = index;i<coins.length;i++){
                count+=rec(i,coins,remainder-coins[i]);
            }
            return count;
        }
    }

那这个递归是怎么运作的呢?

import java.util.arraylist;
import java.util.linkedlist;
import java.util.listiterator;

/**
 * 零钱兑换
 * 可以凑成总金额所需的所有组合可能数
 */
public class leetcode518 {
    public int coinchange(int[] coins, int amount) {
        return rec(0, coins, amount, new linkedlist<>(), true);
    }

    /**
     * 求凑成剩余金额的解的个数
     *
     * @param index     当前硬币索引
     * @param coins     硬币面值数组
     * @param remainder 剩余金额
     * @param stack     -
     * @param first     -
     * @return 解的个数
     */
    public int rec(int index, int[] coins, int remainder, linkedlist<integer> stack, boolean first) {
        if(!first) {//第一次不压栈
            stack.push(coins[index]);
        }
        // 情况1:剩余金额 < 0 - 无解
        int count = 0;
        if (remainder < 0) {
            print("无解:", stack);
//            if(!stack.isempty()){
//                stack.pop();
//            }
        }
        // 情况2:剩余金额 == 0 - 有解
        else if (remainder == 0) {
            print("有解:", stack);
//            if(!stack.isempty()){
//                stack.pop();
//            }
            count = 1;
        }
        // 情况3:剩余金额 > 0 - 继续递归
        else {
            for (int i = index; i < coins.length; i++) {
                count += rec(i, coins, remainder - coins[i], stack, false);
            }
        }
        if (!stack.isempty()) {
            stack.pop();
        }
        return count;
    }

    private static void print(string prompt, linkedlist<integer> stack) {
        arraylist<integer> print = new arraylist<>();
        listiterator<integer> iterator = stack.listiterator(stack.size());
        while (iterator.hasprevious()) {
            print.add(iterator.previous());
        }
        system.out.println(prompt + print);
    }

    public static void main(string[] args) {
        leetcode518 leetcode = new leetcode518();
//        int count = leetcode.coinchange(new int[]{1, 5, 10, 25}, 41);
//        int count = leetcode.coinchange(new int[]{25, 10, 5, 1}, 41);
//        int count = leetcode.coinchange(new int[]{5, 2, 1}, 5);
        int count = leetcode.coinchange(new int[]{1, 2, 5}, 5);
//        int count = leetcode.change(new int[]{15, 10, 1}, 21);
        system.out.println(count);
    }
}

但是这个代码放在leetcode上面跑会超时,因为重复处理了很多次相同的操作

我们可以考虑用记忆法 & 动态规划来优化

我们也可以考虑顺序优化 ==>规模下降明显

但即使是这样写在leetcode上也是会 stackoverflowerror

class solution {
    public int change(int amount, int[] coins1) {
        int[] coins = sort(coins1);
        return rec(0,coins,amount);

    }
    int rec(int index,int[] coins,int remainder){
        int count = 0;
        if(remainder == 0){
            return 1;
        }else if(remainder<0){
            return 0;
        }else{
            for(int i = index;i<coins.length;i++){
                count+=rec(index,coins,remainder);
            }
            return count;
        }
    }
    /**
    *传入一个有序的数组a(从小到大排序),返回一个从大到小的数组
    *@param a 传入的数组(有序)
    *@return 返回一个数组(从大到小)
     */
    
    int[] sort(int[] a){
        int[] temp = a;
        if(temp.length % 2 ==0){
            //数组里面的个数为偶数
            for(int i = 0;i<=temp.length/2;i++){
                int temp1 = a[i];
                temp[i] = temp[temp.length-1-i];
                temp[temp.length-1] = temp1;
            }
        }else{
            //数组里面的个数为奇数
            for(int i = 0;i<temp.length/2;i++){
                int temp1 = a[i];
                temp[i]=temp[temp.length-1-i];
                temp[temp.length-1-i] = temp1;
            }
        }
        return temp;
    }
}

import java.util.arrays;

public class main {
    public static void main(string[] args) {
        int[] arr = sort(new int[]{1,2,3});
        system.out.println(arrays.tostring(arr));
        //为什么我这么选择是因为用arrays.sort要将数组转化为integer[]
    }
    /**
     *传入一个有序的数组a(从小到大排序),返回一个从大到小的数组
     * @param a 传入的数组(有序)
     * @return 返回一个数组(从大到小)
     */
    public static int[] sort(int[] a){
        int[] temp = a;

        if(temp.length%2==0){
            //数组里面的个数为偶数
            for (int i = 0; i <= temp.length/ 2; i++) {
                int temp1 = a[i];
                temp[i]=temp[temp.length-1-i];
                temp[temp.length - 1-i] = temp1;
            }
        }else{
            //数组里面的个数为奇数
            for (int i = 0; i < temp.length / 2; i++) {
                int temp1 = a[i];
                temp[i]=temp[temp.length-1-i];
                temp[temp.length - 1-i] = temp1;
            }
        }
        return  temp;
    }
}

 动态规划->会在动态规划章节说明

最优解(零钱兑换)- 穷举法 leetcode 322
import java.util.linkedlist;
import java.util.concurrent.atomic.atomicinteger;

public class leetcode322 {
    static int min = -1; // 需要的最少硬币数  2 3

    public int coinchange(int[] coins, int amount) {
        rec(0, coins, amount, new atomicinteger(-1), new linkedlist<>(), true);
        return min;
    }

    // count 代表某一组合 钱币的总数 可变的整数对象
    public void rec(int index, int[] coins, int remainder, atomicinteger count, linkedlist<integer> stack, boolean first) {
        if (!first) {
            stack.push(coins[index]);
        }
        count.incrementandget(); // count++
        if (remainder == 0) {
            system.out.println(stack);
            if (min == -1) {
                min = count.get();
            } else {
                min = integer.min(min, count.get());
            }
        } else if (remainder > 0) {
            for (int i = index; i < coins.length; i++) {
                rec(i, coins, remainder - coins[i], count, stack, false);
            }
        }
        count.decrementandget(); // count--
        if (!stack.isempty()) {
            stack.pop();
        }
    }

    public static void main(string[] args) {
        leetcode322 leetcode = new leetcode322();
//        int count = leetcode.coinchange(new int[]{5, 2, 1}, 5);
        int count = leetcode.coinchange(new int[]{25, 10, 5, 1}, 41);
//        int count = leetcode.coinchange(new int[]{2}, 3);
//        int count = leetcode.coinchange(new int[]{15, 10, 1}, 21);
        system.out.println(count);
    }
}
最优解(零钱兑换)- 贪心法 leetcode 322

自己看看就行因为有些测试样例过不了

假定传过来的数据就是从大到小排序,因为java对int数组从大到小排序比较麻烦
public class leetcode322 {
    public int coinchange(int[] coins, int amount) {
        int remainder = amount;
        int count = 0;
        for (int coin : coins) {
            while (remainder - coin > 0) {
                remainder -= coin;
                count++;
            }
            if (remainder - coin == 0) {
                remainder = 0;
                count++;
                break;
            }
        }
        if (remainder > 0) {
            return -1;
        } else {
            return count;
        }
    }

    public static void main(string[] args) {
        leetcode322 leetcode = new leetcode322();
        int count = leetcode.coinchange(new int[]{5, 2, 1}, 5);
//        int count = leetcode.coinchange(new int[]{25, 10, 5, 1}, 41);
//        int count = leetcode.coinchange(new int[]{2}, 3);
        
        // 问题1 没有回头,导致找到更差的解
//        int count = leetcode.coinchange(new int[]{15, 10, 1}, 21);  
        // 问题2 没有回头,导致无解
//        int count = leetcode.coinchange(new int[]{15, 10}, 20);  
        system.out.println(count);
    }
}

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com