點個贊,看一看,好習慣!本文 GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收錄,這是我花了 3 個月總結的一線大廠 Java 面試總結,本人已拿騰訊等大廠 offer。
另外,原創文章首發在我的個人博客:blog.ouyangsihai.cn,歡迎訪問。
今天介紹一種解決常規的貪心策略或者字典排序的題目的通用解題方法。
第一題,leetcode中等難度題目
先來一道簡單的字典序排列的問題,這個題目我這里不會用最優解來解決這個問題,這個是leetcode的中等難度的題目,最優解還是需要再思考一下的,這道題目作為文章開頭只是為了介紹我想要介紹的貪心的解題的一種思路而已,大佬請勿噴!!
看到這個題目,我就是想用暴力的方法解決,以便更好的理解這種解題思路。
先給出我的答案,非常暴力,但是非常好理解。
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表達式,使得代碼更加的簡潔。
最優解大家可以去leetcode看看哈,自己動手,豐衣足食。
所以,通過這個題目我想給出的信息就是:通常涉及到字符串排序,字典序,數字排序等等的題目,都是可以用這種思路來解決問題的
。
不信,我們再看看其他題目。
第二題,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來解決的。
就說這么多,只是思路,不要死磕最優解!!!
最后,再分享我歷時三個月總結的 Java 面試 + Java 后端技術學習指南,這是本人這幾年及春招的總結,已經拿到了大廠 offer,整理成了一本電子書,拿去不謝,目錄如下:
現在免費分享大家,在下面我的公眾號 程序員的技術圈子 回復 面試 即可獲取。
有收獲?希望老鐵們來個三連擊,給更多的人看到這篇文章
1、老鐵們,關注我的原創微信公眾號「程序員的技術圈子」,專注于 Java、數據結構和算法、微服務、中間件等技術分享,保證你看完有所收獲。
2、給俺點個贊唄,可以讓更多的人看到這篇文章,順便激勵下我繼續寫作,嘻嘻。
3、另外,原創文章首發在我的個人博客:blog.ouyangsihai.cn,歡迎訪問。
點贊是對我最大的鼓勵
↓↓↓↓↓↓