|
8 | 8 |
|
9 | 9 | 2、用一种局部最功利的标准,总是能做出在当前看来是最好的选择
|
10 | 10 |
|
11 |
| -3、难点在于证明局部最优解最功利的标准可以得到全局最优解 |
| 11 | +3、难点在于证明局部最优解可以最终得到全局最优解 |
12 | 12 |
|
13 |
| -4、对于贪心算法的学习主要是以增加阅历和经验为主 |
| 13 | +4、对于贪心算法的学习主要是以经验为主,尝试为主 |
14 | 14 |
|
15 | 15 | ### 1.2.1 贪心算法解释
|
16 | 16 |
|
17 | 17 | 正例:通过一个例子来解释,假设一个数组中N个正数,第一个挑选出来的数乘以1,第二个挑选出来的数乘以2,同理,第N次挑选出来的数乘以N,总的加起来是我们的分数。怎么挑选数字使我们达到最大分数?
|
18 | 18 |
|
19 | 19 | > 数组按从小到大的顺序排序,我们按顺序依次挑选,最终结果就是最大的。本质思想是因子随着挑选次数的增加会增大,我们尽量让大数去结合大的因子。
|
20 | 20 |
|
21 |
| -==贪心算法有时是无效的,后面会贪心算法无效的例子== |
| 21 | +> 贪心算法有时是无效的,下文会举贪心算法无效的例子 |
22 | 22 |
|
23 | 23 | ### 1.2.2 贪心算法的证明问题
|
24 | 24 |
|
25 | 25 | 如何证明贪心算法的有效性?
|
26 | 26 |
|
27 |
| -> 一般来说,贪心算法不推荐证明,很多时候证明是非常复杂的。通过下面例子来说明贪心算法证明的复杂性,从头到尾讲一道利用贪心算法求解的题目。 |
| 27 | +> 一般来说,贪心算法不推荐证明,很多时候证明是非常复杂的。通过下面例子来说明贪心算法证明的复杂性; |
28 | 28 |
|
29 | 29 | 例子:给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果。
|
30 | 30 |
|
31 | 31 | > 字典序概念:直观理解,两个单词放到字典中,从头开始查找这个单词,哪个先被查找到,哪个字典序小。
|
32 | 32 |
|
33 | 33 | > 字典序严格定义,我们把字符串当成k进制的数,a-z当成26进制的正数,字符长度一样,abk>abc,那么我们说abk的字典序更大。字符长度不一样ac和b,那么我们要把短的用0补齐,0小于a的accil,那么ac<b0,高位b>a即可比较出来大小。
|
34 | 34 |
|
35 |
| -Java中字符串的ComparTo方法,就是比较字典序。 |
| 35 | +常见的语言标准库中比较字符串的函数,大都是比较字典序。 |
36 | 36 |
|
37 |
| -本题思路1:按照单个元素字典序贪心,例如在[ac,bk,sc,ket]字符串数组中,我们拼接出来最终的字符串字典序最小,那么我们依次挑选字典序最小的进行拼接的贪心策略得到acbkketsc。 |
| 37 | +思路1:按照单个元素字典序贪心,例如在[ac,bk,sc,ket]字符串数组中,我们拼接出来最终的字符串字典序最小,那么我们依次挑选字典序最小的进行拼接的贪心策略得到acbkketsc。 |
38 | 38 |
|
39 |
| -==但是这样的贪心不一定是正确的,例如[ba,b]按照上述思路的贪心结果是bba,但是bab明显是最小的结果== |
| 39 | +> 但是这样的贪心不一定是正确的,例如[ba,b]按照上述思路的贪心结果是bba,但是bab明显是最小的结果 |
40 | 40 |
|
41 |
| -本题思路2:两个元素x和y,x拼接y小于等于x拼接y,那么x放前,否则y放前面。例如x=b,y=ba。bba大于bab的字典与,那么ba放前面 |
| 41 | +思路2:两个元素x和y,x拼接y小于等于y拼接x,那么x放前,否则y放前面。例如x=b,y=ba。bba大于bab的字典序,那么ba放前面 |
42 | 42 |
|
43 | 43 |
|
44 | 44 | 证明:
|
45 | 45 |
|
46 | 46 | 我们把拼接当成k进制数的数学运算,把a-z的数当成26进制的数,'ks'拼接'ts'实质是ks * 26^2 + te。
|
47 | 47 |
|
48 |
| -目标先证明我们比较的传递性:证明a拼接b小于b拼接a,b拼接c小于等于c拼接b,推出a拼接c小于等于c拼接a。 |
| 48 | +目标先证明我们比较的传递性:证明a拼接b小于b拼接a,b拼接c小于等于c拼接b,推导出a拼接c小于等于c拼接a。 |
49 | 49 |
|
50 | 50 | a拼接b等于a乘以k的b长度次方 + b。我们把k的x长度次方这个操作当成m(x)函数。所以:
|
51 | 51 |
|
@@ -79,135 +79,64 @@ m(c) * a + c <= m(a) * c + a
|
79 | 79 |
|
80 | 80 | 再证明任意三个交换都会变为更大的字典序,那么最终数学归纳法,得到思路二的正确性
|
81 | 81 |
|
82 |
| -==所以贪心算法的证明实质是比较复杂的,我们大可不必每次去证明贪心的正确性== |
| 82 | +> 所以贪心算法的证明实质是比较复杂的,我们大可不必每次去证明贪心的正确性 |
83 | 83 |
|
84 |
| -```Java |
85 |
| -package class09; |
| 84 | +```Go |
| 85 | +package main |
86 | 86 |
|
87 |
| -import java.util.ArrayList; |
88 |
| -import java.util.Arrays; |
89 |
| -import java.util.Comparator; |
90 |
| -import java.util.HashSet; |
| 87 | +import ( |
| 88 | + "sort" |
| 89 | + "strings" |
| 90 | +) |
91 | 91 |
|
92 |
| -public class Code01_LowestLexicography { |
| 92 | +// 方法1 暴力法穷举,排列组合。略 |
93 | 93 |
|
94 |
| - // 暴力法穷举,排列组合 |
95 |
| - public static String lowestString1(String[] strs) { |
96 |
| - if (strs == null || strs.length == 0) { |
97 |
| - return ""; |
98 |
| - } |
99 |
| - ArrayList<String> all = new ArrayList<>(); |
100 |
| - HashSet<Integer> use = new HashSet<>(); |
101 |
| - process(strs, use, "", all); |
102 |
| - String lowest = all.get(0); |
103 |
| - for (int i = 1; i < all.size(); i++) { |
104 |
| - if (all.get(i).compareTo(lowest) < 0) { |
105 |
| - lowest = all.get(i); |
106 |
| - } |
107 |
| - } |
108 |
| - return lowest; |
| 94 | +// LowestStringByGreedy 方法2 贪心法 |
| 95 | +func LowestStringByGreedy(strs []string) string { |
| 96 | + if len(strs) == 0 { |
| 97 | + return "" |
109 | 98 | }
|
110 | 99 |
|
111 |
| - // strs里放着所有的字符串 |
112 |
| - // 已经使用过的字符串的下标,在use里登记了,不要再使用了 |
113 |
| - // 之前使用过的字符串,拼接成了-> path |
114 |
| - // 用all收集所有可能的拼接结果 |
115 |
| - public static void process(String[] strs, HashSet<Integer> use, String path, ArrayList<String> all) { |
116 |
| - // 所有字符串都是用过了 |
117 |
| - if (use.size() == strs.length) { |
118 |
| - all.add(path); |
119 |
| - } else { |
120 |
| - for (int i = 0; i < strs.length; i++) { |
121 |
| - if (!use.contains(i)) { |
122 |
| - use.add(i); |
123 |
| - process(strs, use, path + strs[i], all); |
124 |
| - use.remove(i); |
125 |
| - } |
126 |
| - } |
127 |
| - } |
128 |
| - } |
| 100 | + Sort(strs, func(a, b string) int { |
| 101 | + return strings.Compare(a, b) |
| 102 | + }) |
129 | 103 |
|
130 |
| - public static class MyComparator implements Comparator<String> { |
131 |
| - @Override |
132 |
| - public int compare(String a, String b) { |
133 |
| - return (a + b).compareTo(b + a); |
134 |
| - } |
| 104 | + res := "" |
| 105 | + for i := 0; i < len(strs); i++ { |
| 106 | + res += strs[i] |
135 | 107 | }
|
| 108 | + return res |
| 109 | +} |
136 | 110 |
|
137 |
| - // 思路二,贪心解法 |
138 |
| - public static String lowestString2(String[] strs) { |
139 |
| - if (strs == null || strs.length == 0) { |
140 |
| - return ""; |
141 |
| - } |
142 |
| - Arrays.sort(strs, new MyComparator()); |
143 |
| - String res = ""; |
144 |
| - for (int i = 0; i < strs.length; i++) { |
145 |
| - res += strs[i]; |
146 |
| - } |
147 |
| - return res; |
148 |
| - } |
| 111 | +type Comparator func(a, b string) int |
149 | 112 |
|
150 |
| - // for test |
151 |
| - public static String generateRandomString(int strLen) { |
152 |
| - char[] ans = new char[(int) (Math.random() * strLen) + 1]; |
153 |
| - for (int i = 0; i < ans.length; i++) { |
154 |
| - int value = (int) (Math.random() * 5); |
155 |
| - ans[i] = (Math.random() <= 0.5) ? (char) (65 + value) : (char) (97 + value); |
156 |
| - } |
157 |
| - return String.valueOf(ans); |
158 |
| - } |
| 113 | +func Sort(values []string, comparator Comparator) { |
| 114 | + sort.Sort(sortable{values, comparator}) |
| 115 | +} |
159 | 116 |
|
160 |
| - // for test |
161 |
| - public static String[] generateRandomStringArray(int arrLen, int strLen) { |
162 |
| - String[] ans = new String[(int) (Math.random() * arrLen) + 1]; |
163 |
| - for (int i = 0; i < ans.length; i++) { |
164 |
| - ans[i] = generateRandomString(strLen); |
165 |
| - } |
166 |
| - return ans; |
167 |
| - } |
| 117 | +type sortable struct { |
| 118 | + values []string |
| 119 | + comparator Comparator |
| 120 | +} |
168 | 121 |
|
169 |
| - // for test |
170 |
| - public static String[] copyStringArray(String[] arr) { |
171 |
| - String[] ans = new String[arr.length]; |
172 |
| - for (int i = 0; i < ans.length; i++) { |
173 |
| - ans[i] = String.valueOf(arr[i]); |
174 |
| - } |
175 |
| - return ans; |
176 |
| - } |
| 122 | +func (s sortable) Len() int { |
| 123 | + return len(s.values) |
| 124 | +} |
177 | 125 |
|
178 |
| - public static void main(String[] args) { |
179 |
| - int arrLen = 6; |
180 |
| - int strLen = 5; |
181 |
| - int testTimes = 100000; |
182 |
| - String[] arr = generateRandomStringArray(arrLen, strLen); |
183 |
| - System.out.println("先打印一个生成的字符串"); |
184 |
| - for (String str : arr) { |
185 |
| - System.out.print(str + ","); |
186 |
| - } |
187 |
| - System.out.println(); |
188 |
| - System.out.println("test begin"); |
189 |
| - for (int i = 0; i < testTimes; i++) { |
190 |
| - String[] arr1 = generateRandomStringArray(arrLen, strLen); |
191 |
| - String[] arr2 = copyStringArray(arr1); |
192 |
| - if (!lowestString1(arr1).equals(lowestString2(arr2))) { |
193 |
| - for (String str : arr1) { |
194 |
| - System.out.print(str + ","); |
195 |
| - } |
196 |
| - System.out.println(); |
197 |
| - System.out.println("Oops!"); |
198 |
| - } |
199 |
| - } |
200 |
| - System.out.println("finish!"); |
201 |
| - } |
| 126 | +func (s sortable) Swap(i, j int) { |
| 127 | + s.values[i], s.values[j] = s.values[j], s.values[i] |
| 128 | +} |
202 | 129 |
|
| 130 | +func (s sortable) Less(i, j int) bool { |
| 131 | + return s.comparator(s.values[i], s.values[j]) < 0 |
203 | 132 | }
|
204 | 133 | ```
|
205 | 134 |
|
206 | 135 | > 全排列的时间复杂度为:O(N!)
|
207 | 136 |
|
208 | 137 | > 每一种贪心算法有可能都有属于他自身的特有证明,例如哈夫曼树算法,证明千变万化
|
209 | 138 |
|
210 |
| -==贪心策略算法,尽量不要陷入复杂的证明== |
| 139 | +> 贪心策略算法,尽量不要陷入复杂的证明 |
211 | 140 |
|
212 | 141 | ## 1.2 贪心算法求解思路
|
213 | 142 |
|
|
0 commit comments