貪心算法思路總結及其java實現

1. 貪心算法條件與思路的解題步驟

1.一次性:某個狀態以後的過程不會影響以前的狀態,只與當前狀態有關。
2.局部最優集合能得到全局最優。
(如果某次選擇會對之後選擇產生影響,局部最優不能獲得全局最優可以直接KO貪心思路)
第一步,判斷是否屬於貪心算法的情況:對一組數據加了一定的限制,希望從中選出幾個數據,在滿足限制值的情況下,達到最大期望值。
第二步,用簡單例子檢測貪心算法是否最優。

2.貪心算法的分類

依據個人經驗分成了三大類
1.分餅乾,跳躍遊戲,環繞加油站等每次從最小的開始分配

從問題的某一初始解出發;
    while (到達目標邊界)
    {
         if(該階段的狀態滿足最優條件)加入此局部最優解;
          進入下一階段;
    }
    return 由所有解元素組合成問題的一個可行解;

  1. 教師排課器,任務調度等區間覆蓋的問題
    選擇幾個不相交的區間,從左到右將[lmin, rmax]覆蓋上。按照起始端從小到大對這 n 個區間排序。
    每次選擇:左端點跟前面已覆蓋區間不重合的、儘量小的區間,這樣可以讓剩下的未覆蓋區間儘可能的大,就可以放置更多的區間。
  2. 揹包價值和錢幣找零問題
    貢獻相同期望值的情況下,每次價值越大,總體的數量/重量就更少

注:求最小生成樹的Prim算法和Kruskal算法都是漂亮的貪心算法。

leetcode裏貪心算法的代碼實現

1.簡單題455分發餅乾
import java.util.Arrays;

 public class AssignCookie{
     public static int contentChildren(int[]g,int[]s){
         assert g.length>0||s.length>0;
         Arrays.sort(g);
         Arrays.sort(s);
         //定義第i個小孩的appetite對應第j塊餅乾
         int i=0,j=0;
         //當小孩沒滿足完或餅乾沒發完
         while(i<g.length&&j<s.length){
             if(g[i]<=s[j])
                 i++;
             j++;
         }
         //被滿足的小孩個數
         return i;
     }
 }
中等題134. 加油站
/**
 *在一條環路上有 N 個加油站,其中第 i 個加油站有汽油 gas[i] 升。
 * 你有一輛油箱容量無限的的汽車,從第 i 個加油站開往第 i+1 個加油站需要消耗汽油 cost[i] 升。你從其中的一個加油站出發,開始時油箱爲空。
 * 如果你可以繞環路行駛一週,則返回出發時加油站的編號,否則返回 -1。
 */
public class GasStation {
    /**
     *一旦遇到第一個無法到達的點 i,直接更換起始點爲 i+1。中間的 [1,i-1] 的點一定不是起始點
     * @param gas
     * @param cost
     */
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int sum = 0;//選定起點到現在的油缸量
        int total = 0;//總的加油與耗油差值
        int result = 0;//選定的起點
        for(int i = 0;i<gas.length;i++){
            total += gas[i]-cost[i];
            if(sum<0){
                sum = gas[i]-cost[i];
                result = i;
            }
            else
                sum += gas[i]-cost[i];
        }
        //邊界條件是能跑完全程
        return total>=0?result:-1;
    }
}
貪心算法簡單題392. 判斷子序列

public class isSubsequence {
    /*
     * 給定字符串 s 和 t ,判斷 s 是否爲 t 的子序列。
     * 你可以認爲 s 和 t 中僅包含英文小寫字母。字符串 t 可能會很長(長度 ~= 500,000),而 s 是個短字符串(長度 <=100)。
     *字符串的一個子序列是原始字符串刪除一些(也可以不刪除)字符而不改變剩餘字符相對位置形成的新字符串。(例如,"ace"是"abcde"的一個子序列,而"aec"不是)。
     */
    public boolean isSubsequence(String s, String t) {
        if(0==s.length()) return true;
        //母串序列索引i,子串j
        int sub = 0,j = 0;
        //不能用for循環是考慮到s比t長
        while( sub<s.length()&&j< t.length()){
            //如果不同只有母串後移
            if(s.charAt(sub)==t.charAt(j))
                sub++;
            j++;
        }
        //返回結果裏判別是否子串全找到了,很有技巧
        return  sub==s.length();
    }
}

192.跳躍遊戲

public class JumpGame {
    public static boolean canJump(int[] nums){
        assert nums.length>0;
        if(nums.length==1)return true;
        //數組首位爲0且0不是唯一元素肯定跳不到最後
        if(nums.length>1&&nums[0]==0)return false;
        for (int i = 1; i < nums.length-1; i++) {
            int j = i - 1;
            while (nums[i] == 0) {
                //前面兩個if是找到j的情況,說明這個0元素是可以被到達或跳過的
               // if (nums[j] >= i - j&&i==nums.length-1) break;
                if (nums[j] > i - j) break;
                if (j <= 0) return false;
                j--;
            }
        }
        return true;
    }

    //just for test
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String s = input.next();
        String[] arr = s.split(",");
        int[] arry = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arry[i] = Integer.parseInt(arr[i]);
        }
        boolean result =canJump(arry);
        System.out.println(result);
    }
}
  1. 最大子序和
import java.util.Scanner;

/**
 * 給定一個整數數組 nums ,找到一個具有最大和的連續子數組(子數組最少包含一個元素),返回其最大和。
 * 輸入[-2,1,-3,4,-1,2,1,-5,4];輸出6;因爲連續子數組 [4,-1,2,1] 的和最大,爲 6。
 */
public class MaxSubarray {
    public static int maxSubarray(int[] nums) {
        //sum存子序和,max表示選好子序手標的某次的和
        int sum = nums[0];
        int max = 0;
        for(int i=0;i<nums.length;i++){
            //if(max+nums[i]<nums[i])
            if(max<0) max = nums[i];
            else max+=nums[i];
            //如果不加這個結果就會子序數組首標找對,尾標可能加進沒有使整體<0但變小的數
            sum = Math.max(sum,max);
        }
        return sum;
    }

    //just for test
    public static void main(String[] args){
        Scanner input = new Scanner(System.in);
        String s= input.next();
        String[] arr = s.split(",");
        int[] arry = new int[arr.length];
        for(int i=0;i<arr.length;i++)
        {
            arry[i]=Integer.parseInt(arr[i]);
        }
        int result = maxSubarray(arry);
        System.out.println(result);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章