點個贊,看一看,好習慣!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收錄,這是我花了 3 個月總結的一線大廠 Java 面試總結,本人已拿大廠 offer。 另外,原創文章首發在個人我的博客:blog.ouyangsihai.cn,歡迎訪問。java
今天介紹一種解決常規的貪心策略或者字典排序的題目的通用解題方法。git
第一題,leetcode中等難度題目
先來一道簡單的字典序排列的問題,這個題目我這裏不會用最優解來解決這個問題,這個是leetcode的中等難度的題目,最優解仍是須要再思考一下的,這道題目做爲文章開頭只是爲了介紹我想要介紹的貪心的解題的一種思路而已,大佬請勿噴!!github
看到這個題目,我就是想用暴力的方法解決,以便更好的理解這種解題思路。面試
先給出個人答案,很是暴力,可是很是好理解。數組
public List<Integer> lexicalOrder(int n) { List<String> list = new ArrayList<>(); for(int i = 1; i <= n; i++){ list.add(i + ""); } Collections.sort(list,(o1,o2)->{ return o1.compareTo(o2); }); List<Integer> iList = new ArrayList<>(); list.stream().forEach((str)->{ iList.add(Integer.parseInt(str)); }); return iList; }
這個解題方法很簡單,用的就是Collections.sort()方法的排序,而後重寫一下Comparator類而已,這裏用的是lambda表達式,使得代碼更加的簡潔。this
最優解你們能夠去leetcode看看哈,本身動手,豐衣足食。code
因此,經過這個題目我想給出的信息就是:一般涉及到字符串排序,字典序,數字排序等等的題目,都是能夠用這種思路來解決問題的
。blog
不信,咱們再看看其餘題目。排序
第二題,leetcode中等難度題目
這是一道常見的topk問題,最優解也不是我給出的答案,目的只是爲了闡述這種解題方法。隊列
個人解題方法:用優先級隊列,維護一個大小爲k小頂堆,每次堆的元素到達k時,先彈出堆頂元素,這樣就堆老是維持着k個最大值,最終能夠的到前k高的元素。
下面看看個人解答(注意:個人答案絕對不是最優解,只是爲了闡述這種方法)
class Solution { public int[] topKFrequent(int[] nums, int k) { Queue<Obj> queue = new PriorityQueue<>(k,(o1,o2)->{ return o2.num - o1.num; }); HashMap<Integer,Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++){ map.put(nums[i],map.getOrDefault(nums[i],0) + 1); } for(int key : map.keySet()){ queue.offer(new Obj(key,map.get(key))); } int[] ans = new int[k]; int i = 0; while(i < k){ ans[i] = queue.poll().target; i++; } return ans; } class Obj { public int target; public int num; public Obj(int target, int num){ this.target = target; this.num = num; } } }
這種方法沒有維護k的最大的堆。
class Solution { public List<Integer> topKFrequent(int[] nums, int k) { HashMap<Integer, Integer> map = new HashMap(); for (int n: nums) { map.put(n, map.getOrDefault(n, 0) + 1); } PriorityQueue<Integer> heap = new PriorityQueue<Integer>((n1, n2) -> map.get(n1) - map.get(n2)); for (int n: map.keySet()) { heap.add(n); if (heap.size() > k) heap.poll(); } List<Integer> top_k = new LinkedList(); while (!heap.isEmpty()) top_k.add(heap.poll()); Collections.reverse(top_k); return top_k; } }
這種方法維護k的最大的堆。
對比發現:無論維護k的最大堆仍是不維護,核心的思想都是
Queue<Obj> queue = new PriorityQueue<>(k,(o1,o2)->{ return o2.num - o1.num; });
和這段代碼
PriorityQueue<Integer> heap = new PriorityQueue<Integer>((n1, n2) -> map.get(n1) - map.get(n2));
對比第一題中的
Collections.sort(list,(o1,o2)->{ return o1.compareTo(o2); });
用的都是內部類:Comparator
,而後進行構建符合題意的排序規則。
第三題,更復雜點的
這個題目就更能明白什麼是構建符合題意的排序規則。
由於不少題目不止讓你根據一個字段進行排序,多是兩個字段進行排序,或者三個字段進行排序,因此就須要進行「構建」。
這個題目的解題思路:先排序再插入
- 排序規則:按照先H高度降序,K個數升序排序
- 遍歷排序後的數組,根據K插入到K的位置上
核心思想:高個子先站好位,矮個子插入到K位置上,前面確定有K個高個子,矮個子再插到前面也知足K的要求。
再看看解答
public int[][] reconstructQueue(int[][] people) { // [7,0], [7,1], [6,1], [5,0], [5,2], [4,4] // 再一個一個插入過程 // [7,0] // [7,0], [7,1] // [7,0], [6,1], [7,1] // [5,0], [7,0], [6,1], [7,1] // [5,0], [7,0], [5,2], [6,1], [7,1] // [5,0], [7,0], [5,2], [6,1], [4,4], [7,1] Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]); LinkedList<int[]> list = new LinkedList<>(); for (int[] i : people) { //在i位置,插入數:i[1]是[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]的第一個數,表示前面有幾個比我高的。 list.add(i[1], i); } return list.toArray(new int[list.size()][2]); }
你會發現,核心代碼仍是跟第一題和第二題同樣,只是複雜一點點。
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);
這是什麼意思呢:o1和o2是一個相似這樣[7,0]
的一位數組,當第一個數相等時,再比較一維數組的第二個數的大小,不相等,固然先比較第一個數了。
這個就是多個字段比較的例子,是否是仍是跟前面的思路是同樣的。
總結
最後發現,關於排序的,無論是,數組的排序,數字的排序,字符串的排序,仍是優先級隊列的排序,咱們都是能夠用Java的Comparator來解決的。
就說這麼多,只是思路,不要死磕最優解!!!