队列/优先队列的应用问题

最近因为考试耽搁了几天刷题,首先介绍下面这道题,这是有关图的问题:

127. Word Ladder


Given two words (beginWord and endWord), and a dictionary’s word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

  1. Only one letter can be changed at a time.
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Note:

  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

Example 1:

Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
Output: 5
Explanation: As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.

Example 2:

Input:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
Output: 0
Explanation: The endWord “cog” is not in wordList, therefore no possible transformation.


下面是我的代码:


public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if (wordList.size() == 0) return 0;
        LinkedList<String> queue = new LinkedList();   // 这个用来存适合的串
        LinkedList<Integer> levels = new LinkedList<>();  // 这个用来存层数
        levels.offer(1);  //初始时设置层数为1
        queue.offer(beginWord);  //初始把最开始的串放进去
        while (!queue.isEmpty()) {
            String peak = queue.poll();
            Integer level = levels.poll();
            if (peak.equals(endWord)) return level;  
            for (int i = 0; i < peak.length(); i++) {
                char[] p = peak.toCharArray();  
                // 这个串的每一个位置换一个字母,如果得到了wordlist,就继续放入队列中
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    p[i] = ch;
                    String xp = String.valueOf(p);  
                    if (wordList.contains(xp)) {
                        queue.offer(xp); 
                        levels.offer(level + 1);  // 记录层数
                        wordList.remove(xp);  //并且去掉wordlist中的那个
                    }
                }
            }
        }
        return 0;
    }

额!!很遗憾! 提交后:
在这里插入图片描述
先说一下思路:这个是个BFS类的问题,我们从beginword开始,每次换一个字母看看是否换完之后,是否在wordlist中,如果在里面,就继续放入队列中,并把wordlist中的去掉,防止闭环,并且记录层数,达到BFS的目的,直到换完得到endword,此时,返回层数,得到最短路径。理论上来说,这是没问题的!!!但是超时了!!!,我正在想还有什么优化的方法。

------------------------------分割线-------------这是一年后---------------------------------------------------------

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        if(endWord.equals(beginWord)) return 0;
        HashMap<String,Integer> map = new HashMap<>();
        for(String word: wordList) map.put(word, 1);
        if(!map.containsKey(endWord)) return 0;
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        int cur = 1, nexts = 0;
        int step = 0;
        boolean mark = false;
        while(!queue.isEmpty()){
            String u = queue.poll();
            cur--;
            if(u.equals(endWord)){
                mark = true;
                break;
            }
            char[] ch = u.toCharArray();
            for(int i = 0; i<ch.length; i++){
                char t = ch[i];
                for(int j = 0; j<26; j++){
                    ch[i] = (char)(j+97);
                    if(ch[i] == t) continue;
                    String rr = new String(ch);
                    if(map.containsKey(rr)){
                        map.remove(rr);
                        queue.offer(rr);
                        nexts++;
                    }
                }
                ch[i] = t;
            }
            if(cur == 0){
                step++;
                cur = nexts;
                nexts = 0;
            }
        }
        if(!mark) return 0;
        return step+1;
    }
}

我还是AC了,也还是用的BFS


下面是一个优先队列的问题:

347. Top K Frequent Elements


Given a non-empty array of integers, return the k most frequent elements.
Example 1:

Input: nums = [1,1,1,2,2,3], k = 2
Output: [1,2]

Example 2:

Input: nums = [1], k = 1
Output: [1]

Note:

  • You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
  • Your algorithm’s time complexity must be better than O(n log n), where n is the array’s size.

public List<Integer> top(int[] nums, int k) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for (int i : nums) {
            int count = map.getOrDefault(i, 0);
            map.put(i, count + 1);
        }

        PriorityQueue<Map.Entry<Integer, Integer>> priorityQueue = new PriorityQueue<>(new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            priorityQueue.offer(entry);
        }
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < k; i++) {
            list.add(priorityQueue.poll().getKey());
        }
        return list;
    }

这里不只是TreeMap进行改装,也是能实现的,对TreeMap的值进行倒排,然后输出。这个问题知道其原理,但是我在用java实现的时候,出现几处无法修复的错,可能是现阶段还是不能熟练使用优先队列。对于347,还是会二刷。


23. Merge k Sorted Lists

下面再来一个hard级别的优先队列题目:题目传送门 ,当然这道题不一定要用优先队列求解,还有其它的办法。

题目描述如下:
在这里插入图片描述
通过这道题目,我主要是学会了如何自定义优先队列,也就是如何指定优先队列的排序规则,比如说是大堆,小堆,或者你指定某个属性进行排序,这题中,我指定的其实就是ListNode中的val进行排序。

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        
        ListNode head = new ListNode(-1);
        head.next = null;
        ListNode dup = head;
        
        PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>(new Comparator<ListNode>() {
            @Override    
            public int compare(ListNode o1, ListNode o2) {   //指定从小到大进行排
                return o1.val - o2.val;
            }
        });
        for(int i = 0; i<lists.length; i++){
            if(lists[i] != null)
                priorityQueue.offer(lists[i]);
        }
        while(!priorityQueue.isEmpty()){
            ListNode node = priorityQueue.poll();
            if(node.next != null)
                priorityQueue.offer(node.next);
            dup.next = node;
            dup = dup.next;
        }
        return head.next;
    }
}

首先把所有的链表中首节点放入优先队列中,然后出列第一个节点,放入链表中,根据排序规则,第一个节点应该是val最小的,第一个节点出来后,把第一个节点的next节点继续放入优先队列中,重新进行建堆,最后队列为空,表示过程结束了。链表采用尾插法插入建立。
在这里插入图片描述
这个时间应该是一般般,只超过76.42% 的提交。应该有更好的优化方法,后面学习过程中再想想怎么优化。


----------------------------------------------------------------------.2020-12.1-----------------------------------------------------------------
补充一道打卡题:
在这里插入图片描述
用暴力没有AC,后来评论区看到有人用堆,惭愧😓,没有想到这么好的解法:

class Solution {
    public String reorganizeString(String S) {
        int len = S.length();
        if(len == 0) return "";
        int[] count = new int[26];
        char[] ch = S.toCharArray();
        for(char c: ch) count[c-'a'] ++;
        PriorityQueue<Character> priorityQueue = new PriorityQueue<>((a, b)->count[b-'a']-count[a-'a']);
        for(char c: ch){
            if(!priorityQueue.contains(c)){
                priorityQueue.offer(c);
            }
        }
        Character before = priorityQueue.poll();
        count[before-'a']--;
        if(count[before-'a'] > 0) priorityQueue.offer(before);
        String res = ""+before;
        while(!priorityQueue.isEmpty()){
            Character c = priorityQueue.poll();
            if(c == before && priorityQueue.isEmpty()) return "";
            else if(c == before){
                Character d = priorityQueue.poll();
                res += d;
                count[d-'a']--;
                if(count[d-'a'] > 0) priorityQueue.offer(d);
                priorityQueue.offer(c);
                before = d;
            }else{
                res += c;
                count[c-'a']--;
                if(count[c-'a'] > 0) priorityQueue.offer(c);
                before = c;
            }
        }
        return res;
    }
}

按照字母频率从大到小构建堆,频率第一大的和第二大的两两合并,先消耗完它们,按照频率从大到小依次消耗,用到了贪心的思想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值