算法导论(六)之贪婪算法

版权声明:欢迎大家转载,指正。 https://blog.csdn.net/yin__ren/article/details/83420803

动态规划是先分析子问题,再做选择。而贪心算法则是先做贪心选择,做完选择后,生成了子问题,然后再去求解子问题

  • 与动态规划方法相似,是更简单的解决优化问题的方法,通常用于求解优化问题
  • 贪婪算法不能保证一定得到最优解
  • 对于具有某些特征的问题,贪婪算法有最优解

1. 作业(活动)选择问题

  • 对n个作业进行排程,这些作业在执行期间需要专用某个共同的资源
  • 选出最多个不冲突的作业

活动集合S( S i f i S_i为开始时间,f_i 为结束时间 ):

i 1 2 3 4 5 6 7 8 9 10 11
S i S_i 1 3 0 5 3 5 6 8 8 2 12
f i f_i 4 5 6 7 8 9 10 11 12 13 14

1. 递归实现

1. 伪代码

//***********活动编号已经按结束时间排序**********
//递归
REC-ACT-SEL (s, f, i, n)
	m ← i + 1
	while m ≤ n and s[m] < f[i] //find the first activity in S(i) to finish
		do m ← m + 1
	if m ≤ n
		then return {a(m)} and REC-ACT-SEL(s, f, m, n)
	else return null

2. 时间复杂度

T ( n ) = θ ( n ) T(n) = \theta(n)

3. java 代码实现

	/**
     * 贪心算法的递归解:活动编号已经按结束时间排序
     *
     * @param s          活动的开始时间
     * @param f          活动的结束时间
     * @param i          要求解的子问题 S(i)
     * @param n          活动集合S的数量
     * @param activities 结果记录
     * @return 返回 S(i) 的最大兼容活动集
     */
    public static ArrayList<Integer> recursiveActivitySelection(int[] s, int[] f, int i, int n, ArrayList<Integer> activities) {
        //初始调用时 i = 0
        int m = i;
        activities.add(m + 1);//保证与活动序号一致(数组从0开始)
        while (m < n && s[m] < f[i]) {//查找 S(i) 中最早结束活动
            m++;//选择下一个活动
        }
        if (m < n) {
            recursiveActivitySelection(s, f, m, n, activities);
        }
        return activities;
    }

	public static void main(String[] args) {
        int[] s = {1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12};
        int[] f = {4, 5, 6, 7, 9, 9, 10, 11, 12, 14, 16};

        ArrayList arr = JobSchedule.recursiveActivitySelection(s, f, 0, s.length, new ArrayList<>());
        for (int i = 0; i < arr.size(); i++) {
            System.out.println(arr.get(i));
        }
    }

2. 迭代实现

1. 伪代码

//***********活动编号已经按结束时间排序**********
//迭代
GREEDY-ACTIVITY-SELECTOR(s, f)
	n = s.length
	A ← {a1}
	k ← 1
	for m ← 2 to n
		do if s[m] ≥ f[k]  //activity a(m) is compatible with a(k)
			then A ← A and {a(m)}
			k ← m // a(i) is most recent addition to A
	return A

2. 时间复杂度

T ( n ) = θ ( n ) T(n) = \theta(n)

3. Java 代码实现

	/**
     * 贪心算法的迭代解:活动编号已经按结束时间排序
     *
     * @param s          活动的开始时间
     * @param f          活动的结束时间
     * @param activities 结果记录
     * @return 返回 S(i) 的最大兼容活动集
     */
    public static ArrayList<Integer> greedyActivitySelection(int[] s, int[] f, ArrayList<Integer> activities) {
        //所有真正的活动(不包括 活动0和 活动n+1)中,结束时间最早的那个活动一定是最大兼容活动集合中的 活动.
        int n = s.length;
        int m = 0;//从 1 开始(数组序号为0)
        activities.add(m + 1);//保证与活动序号一致(数组从0开始)

        for (int actId = 1; actId < n; actId++) {
            if (s[actId] >= f[m])//actId的开始时间在 m 号活动之后.--actId 与 m 没有冲突
            {
                m = actId;
                activities.add(m + 1);
            }
        }
        return activities;
    }

    public static void main(String[] args) {
        int[] s = {1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12};
        int[] f = {4, 5, 6, 7, 9, 9, 10, 11, 12, 14, 16};
        ArrayList arr = JobSchedule.greedyActivitySelection(s, f, new ArrayList<>());
        for (int i = 0; i < arr.size(); i++) {
            System.out.println(arr.get(i));
        }
    }
}

3. 动态规划实现

//笔者为看懂,望大神指教

2. Huffman 编码

编码文件需要的二进制位:

  • c.freq 表示 c 在文件中出现的频率
  • d T ( c ) d_T(c) 表示 c 的叶节点在树中的深度
  • B ( T ) B(T) 定义为 T 的代价

B ( T ) = c C c . f r e q d T ( c ) B(T) = \sum_{c\in C} {c.freq * d_T(c)}

1. 伪代码

HUFFMAN(C)
	n = |C|
	Q = C
	for i = 1 to n – 1
		do allocate a new node z
		z.left = x = EXTRACT-MIN(Q)
		z.right = y = EXTRACT-MIN(Q)
		z.freq = x.freq + y.freq
		INSERT (Q, z)
	return EXTRACT-MIN(Q)

2. 时间复杂度

T ( n ) = O ( n l o g n ) T(n) = O(n * logn)

3. Java 代码实现

public class Huffman {

    //定义TreeNode节点
    private static class TreeNode implements Comparable<TreeNode> {
        TreeNode left;
        TreeNode right;
        int weight;
        char ch;//保存相应字符
        String code;//code保存0或1
        public TreeNode(int weight, TreeNode left, TreeNode right) {
            this.weight = weight;
            this.left = left;
            this.right = right;
            this.code = "";
        }
        @Override
        public int compareTo(TreeNode o) {
            if (this.weight > o.weight) {
                return 1;
            } else if (this.weight < o.weight) {
                return -1;
            } else {
                return 0;
            }
        }
    }

    public static TreeNode huffman(TreeMap<Integer, Character> data) {
        TreeSet<TreeNode> tNodes = new TreeSet<>();
        Set<Integer> weights = data.keySet();
        Iterator<Integer> iterator = weights.iterator();
        while (iterator.hasNext()) {
            int weight = iterator.next();
            TreeNode tmp = new TreeNode(weight, null, null);
            tmp.ch = data.get(weight);
            tNodes.add(tmp);
        }
        while (tNodes.size() > 1) {
            TreeNode leftNode = tNodes.pollFirst();
            leftNode.code = "0";
            TreeNode rightNode = tNodes.pollFirst();
            rightNode.code = "1";
            TreeNode newNode = new TreeNode(leftNode.weight + rightNode.weight, leftNode, rightNode);
            tNodes.add(newNode);
        }
        return tNodes.first();
    }

    //得到字符编码
    private static void code(TreeNode t) {
        if (t.left != null) {
            t.left.code = t.code + t.left.code;
            code(t.left);
        }
        if (t.right != null) {
            t.right.code = t.code + t.right.code;
            code(t.right);
        }
    }

    //打印字符编码结果
    public static void print(TreeNode root) {
        if (root != null) {
            if (root.left == null && root.right == null) {
                System.out.println(root.ch + " 编码:" + root.code);
            } else {
                print(root.left);
                print(root.right);
            }
        }
    }

    public static void main(String[] args) {
        TreeMap<Integer, Character> test = new TreeMap<>();
        test.put(5, 'f');
        test.put(9, 'e');
        test.put(12, 'c');
        test.put(13, 'b');
        test.put(16, 'd');
        test.put(45, 'a');
        TreeNode root = huffman(test);
        code(root);
        print(root);
    }
}

3. 0/1背包问题

问题描述:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高

  • 如果你作出贪婪选择,即装入平均价值最高的物件,你最终不可能获得最大化的价值总量

猜你喜欢

转载自blog.csdn.net/yin__ren/article/details/83420803