zoukankan      html  css  js  c++  java
  • 算法题解之数据结构

    Animal Shelter

     宠物收养所

    思路:要使各项操作都为O(1),应该使用linkedHashMap(插入查找删除都是O(1),还记录了顺序关系)及两个队列。

    public class AnimalShelter {
        
        ListNode head;
        ListNode tail;
        Queue<Integer> cat_indexs;
        Queue<Integer> dog_indexs;
        Map<Integer, ListNode> map;
        int count;
        
        public AnimalShelter() {
            // do initialize if necessary
            head = new ListNode(-1, "");
            tail = head;
            cat_indexs = new LinkedList<Integer>();
            dog_indexs = new LinkedList<Integer>();
            map = new HashMap<Integer, ListNode>();
        }
    
        /**
         * @param name a string
         * @param type an integer, 1 if Animal is dog or 0
         * @return void
         */
        void enqueue(String name, int type) {
            // Write your code here
            ListNode cur = new ListNode(count, name);
            cur.pre = tail;
            tail.next = cur;
            tail = cur;
            map.put(count, cur);
            if (type == 1) {
                dog_indexs.offer(count);
            } else {
                cat_indexs.offer(count);
            }
            count++;
        }
    
        public String dequeueAny() {
            // Write your code here
            ListNode del = head.next;
            head.next = del.next;
            
            if (del != tail) {
                del.next.pre = del.pre;
            } else {
                 tail = del.pre;
            }
            map.remove(del.key);
            if ((!cat_indexs.isEmpty()) && cat_indexs.peek() == del.key) {
                cat_indexs.poll();
            } else {
                dog_indexs.poll();
            }
            return del.name;
        }
    
        public String dequeueDog() {
            // Write your code here
            int dog_key = dog_indexs.poll();
            ListNode dog = map.get(dog_key);
            map.remove(dog_key);
            dog.pre.next = dog.next;
            if (dog != tail) {
                dog.next.pre = dog.pre;
            } else {
                 tail = dog.pre;
            }
             
            return dog.name; 
            
        }
    
        public String dequeueCat() {
            // Write your code here
            int cat_key = cat_indexs.poll();
            ListNode cat = map.get(cat_key);
            map.remove(cat_key);
            cat.pre.next = cat.next;
            if (cat != tail) {
                cat.next.pre = cat.pre;
            } else {
                 tail = cat.pre;
            }
             
            return cat.name; 
        }
    }
    
    class ListNode {
        ListNode pre;
        ListNode next;
        int key;
        String name;
        ListNode(int key, String name) {
            this.key = key;
            this.name = name;
        }
    }
    View Code

    Basic Calculator II           

    实现计算器II

    思路:这道题没用什么数据结构,放在这为了跟I对比。这种题目就是想清楚碰到每种符号该做什么事:

    碰到'  ',直接pass;

    碰到'+',如果之前有乘除没做,先做乘除,得到当前数,然后把当前数乘以当前符号,加到res上,符号更新为1;

    碰到 '-',与碰到+一样,符号更新为-1;

    碰到' *', 如果之前有乘除没做,先做乘除,得到当前数,然后把pn记为当前数,po记为'*';

    碰到' /',与'*'一样,po记为'/'。

     1 public class Solution {
     2     public int calculate(String s) {
     3         int num = 0;
     4         int pn = 0;
     5         char po = '0';
     6         int sign = 1;
     7         int res = 0;
     8         
     9         for (int i = 0; i < s.length(); i++) {
    10             char c = s.charAt(i);
    11             if (c == ' ') {
    12                 continue;
    13             } else if (c >= '0' && c <= '9') {
    14                 num = num * 10 + c - '0';
    15             } else if (c == '+' || c == '-') {
    16                 num = doMultOrDiv(num, pn, po);
    17                 po = '0';
    18                 res += sign * num;
    19                 num = 0;
    20                 sign = c == '+' ? 1 : -1;
    21             } else if (c == '*' || c == '/') {
    22                 num = doMultOrDiv(num, pn, po);
    23                 pn = num;
    24                 num = 0;
    25                 po = c == '*' ? '*' : '/';
    26             }
    27         }
    28         num = doMultOrDiv(num, pn, po);
    29         res += sign * num;
    30         return res;
    31     }
    32     
    33     public int doMultOrDiv(int num, int pn, char po) {
    34         if (po == '*') {
    35             return pn * num;
    36         } else if (po == '/') {
    37             return pn / num;
    38         }
    39         return num;
    40     }
    41 }
    View Code

    Contains Duplicate III

    检测重复数III

    思路:这道题要求找到每个数最接近的数,因此想到使用treemap。treemap大小为k,存储当前结点前面的k个数,找最接近的数就是找小于当前数的最大值和大于当前数的最小值。

     1 public class Solution {
     2     public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
     3         if (nums == null || nums.length == 0 || t < 0) {
     4             return false;
     5         }
     6         
     7         TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
     8         for (int i = 0; i <= Math.min(k, nums.length - 1); i++) {
     9             if (map.containsKey(nums[i])) {
    10                 return true;
    11             }
    12             map.put(nums[i], i);
    13         }
    14         for (int i = 0; i < nums.length; i++) {
    15             Integer lower = map.lowerKey(nums[i]);
    16             Integer higher = map.higherKey(nums[i]);
    17 
    18             if (lower != null && (long)nums[i] - (long)lower <= t) {
    19                 return true;
    20             }
    21             if (higher != null && (long)higher - (long)nums[i] <= t) {
    22                 return true;
    23             }
    24             map.remove(nums[i]);
    25             if (i + k + 1 < nums.length) {
    26                 if (map.containsKey(nums[i + 1 + k])) {
    27                     return true;
    28                 }
    29                 map.put(nums[i + 1 + k], i + k + 1);
    30             }
    31         }
    32         return false;
    33     }
    34 }
    View Code

    Design Twitter

    设计推特

    思路: 用hashmap来存储用户与用户之间,用户与推特之间的关系;用一个count来表示时间;获取最近十条twitter有点类似merge k sorted array,需要新定义一个类型存数组号和当前索引,然后用heap。

     1 public class Twitter {
     2     Map<Integer, Set<Integer>> user2users;
     3     Map<Integer, List<Integer>> user2tweets;
     4     Map<Integer, Integer> tweet2time;
     5     int count;
     6     
     7     class Pair implements Comparable<Pair> {
     8         int userId;
     9         int curIndex;
    10         public Pair(int userId, int curIndex) {
    11             this.userId = userId;
    12             this.curIndex = curIndex;
    13         }
    14         public int compareTo(Pair o) {
    15             int tweetIdc = user2tweets.get(userId).get(curIndex);
    16             int tweetIdo = user2tweets.get(o.userId).get(o.curIndex);
    17             int timec = tweet2time.get(tweetIdc);
    18             int timeo = tweet2time.get(tweetIdo);
    19             return timeo - timec;
    20         }
    21     }
    22     
    23     /** Initialize your data structure here. */
    24     public Twitter() {
    25         user2users = new HashMap<Integer, Set<Integer>>();
    26         user2tweets = new HashMap<Integer, List<Integer>>();
    27         tweet2time = new HashMap<Integer, Integer>();
    28     }
    29     
    30     /** Compose a new tweet. */
    31     public void postTweet(int userId, int tweetId) {
    32         if (!user2users.containsKey(userId)) {
    33             user2users.put(userId, new HashSet<Integer>());
    34             user2users.get(userId).add(userId);
    35         }
    36         if (!user2tweets.containsKey(userId)) {
    37             user2tweets.put(userId, new ArrayList<Integer>());
    38         }
    39         user2tweets.get(userId).add(tweetId);
    40         tweet2time.put(tweetId, count++);
    41     }
    42     
    43     /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */
    44     public List<Integer> getNewsFeed(int userId) {
    45         if (!user2users.containsKey(userId)) {
    46             return new ArrayList<Integer>();
    47         }
    48         
    49         PriorityQueue<Pair> q = new PriorityQueue<Pair>();
    50         List<Integer> res = new ArrayList<Integer>();
    51         for (int id : user2users.get(userId)) {
    52             List<Integer> tweetList = user2tweets.get(id);
    53             if (tweetList != null && tweetList.size() > 0) {
    54                 q.offer(new Pair(id, tweetList.size() - 1));
    55             }
    56         }
    57         for (int i = 1; i <= 10; i++) {
    58             if (!q.isEmpty()) {
    59                 Pair cur = q.poll();
    60                 res.add(user2tweets.get(cur.userId).get(cur.curIndex));
    61                 if (cur.curIndex != 0) {
    62                     q.offer(new Pair(cur.userId, cur.curIndex - 1));
    63                 }
    64             }
    65         }
    66         return res;
    67     }
    68     
    69     /** Follower follows a followee. If the operation is invalid, it should be a no-op. */
    70     public void follow(int followerId, int followeeId) {
    71         if (!user2users.containsKey(followerId)) {
    72             user2users.put(followerId, new HashSet<Integer>());
    73             user2users.get(followerId).add(followerId);
    74         }
    75         user2users.get(followerId).add(followeeId);
    76     }
    77     
    78     /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */
    79     public void unfollow(int followerId, int followeeId) {
    80         if (user2users.containsKey(followerId) && followerId != followeeId) {
    81             user2users.get(followerId).remove(followeeId);
    82         }
    83     }
    84 }
    View Code

    Evaluate Reverse Polish Notation

    计算后缀表达式(逆波兰表达式)

    思路:用栈,以前就见过这题。

     1 public class Solution {
     2     public int evalRPN(String[] tokens) {
     3         Stack<Integer> s = new Stack<Integer>();
     4         for (String item : tokens) {
     5             if (item.equals("+")) {
     6                 s.push(s.pop() + s.pop());
     7             } else if (item.equals("-")) {
     8                 int a = s.pop();
     9                 s.push(s.pop() - a);
    10             } else if (item.equals("*")) {
    11                 s.push(s.pop() * s.pop());
    12             } else if (item.equals("/")) {
    13                 int a = s.pop();
    14                 s.push(s.pop() / a);
    15             } else {
    16                 s.push(Integer.parseInt(item));
    17             }
    18         }
    19         return s.pop();
    20     }
    21 }
    View Code

    Find Right Interval

    找到右边的区间

    思路:使用treemap。区间的start为key,value是区间的索引。找一个区间的右边第一个区间时,就是在treemap中找大于等于end的第一个区间。 

     1 /**
     2  * Definition for an interval.
     3  * public class Interval {
     4  *     int start;
     5  *     int end;
     6  *     Interval() { start = 0; end = 0; }
     7  *     Interval(int s, int e) { start = s; end = e; }
     8  * }
     9  */
    10 public class Solution {
    11     public int[] findRightInterval(Interval[] intervals) {
    12         TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
    13         for (int i = 0; i < intervals.length; i++) {
    14             map.put(intervals[i].start, i);
    15         }
    16         int[] res = new int[intervals.length];
    17         for (int i = 0; i < res.length; i++) {
    18             int key = intervals[i].end;
    19             Integer next = map.containsKey(key) ? key : map.higherKey(key);
    20             res[i] = next != null ? map.get(next) : -1;
    21         }
    22         return res;
    23     }
    24     
    25     
    26 }
    View Code

    Fraction to Recurring Decimal

    计算循环小数

    思路:本题是用程序模拟人做除法。出现循环小数的情况是:当前的余数前面出现过。因此用一个hashmap来记住余数与其产生的小数位数的映射。为了考虑负数并避免溢出,要使用long类型。

     1 public class Solution {
     2     public String fractionToDecimal(int numerator, int denominator) {
     3         boolean pos = (numerator >= 0 && denominator >= 0) || (numerator <= 0 && denominator <= 0);
     4         long num = Math.abs((long)numerator);
     5         long den = Math.abs((long)denominator);
     6         
     7         StringBuilder sb = new StringBuilder();
     8         long intPart = num / den;
     9         if (!pos) {
    10             sb.append("-");
    11         }
    12         sb.append(String.valueOf(intPart));
    13         
    14         long remainder = num % den;
    15         if (remainder == 0) {
    16             return sb.toString();
    17         }
    18         
    19         sb.append(".");
    20         Map<Long, Integer> remainder2index = new HashMap<Long, Integer>();
    21         
    22         while (remainder != 0) {
    23             if (remainder2index.containsKey(remainder)) {
    24                 sb.insert(remainder2index.get(remainder), "(");
    25                 sb.append(")");
    26                 break;
    27             }
    28             long nextNumerator = remainder * 10;
    29             long decimalPart = nextNumerator / den;
    30             remainder2index.put(remainder, sb.length());
    31             remainder = nextNumerator % den;
    32             sb.append(String.valueOf(decimalPart));
    33         }
    34         return sb.toString();
    35          
    36     }
    37 }
    View Code

    4Sum II

    4sum II

    思路:遍历A和B每一对,建一个hash表,表示和为key的A[i],B[j]有value对。然后再遍历C和D的每一对,看hash表里面存不存在-(C[i]+D[j])。时间复杂度O(n^2)。

     1 public class Solution {
     2     public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
     3         int res = 0;
     4         int N = A.length;
     5         Map<Integer, Integer> ABmap = new HashMap<Integer, Integer>();
     6         for (int i = 0; i < N; i++) {
     7             for (int j = 0; j < N; j++) {
     8                 int ABsum = A[i] + B[j];
     9                 if (!ABmap.containsKey(ABsum)) {
    10                     ABmap.put(ABsum, 0);
    11                 }
    12                 ABmap.put(ABsum, ABmap.get(ABsum) + 1);
    13             }
    14         }
    15         
    16         for (int i = 0; i < N; i++) {
    17             for (int j = 0; j < N; j++) {
    18                 int CDsum = C[i] + D[j];
    19                 if (ABmap.containsKey(-CDsum)) {
    20                     res += ABmap.get(-CDsum);
    21                 }
    22             }
    23         }
    24         return res;
    25     }
    26 }
    View Code

     

    Hash Function

     哈希函数

    思路:由于hashcode("abcd") = (ascii(a) * 33^3+ascii(b)*33^2+ascii(c)*33+ascii(d)*1) % hashsize。当字符串达到一定长度时,直接计算一定会溢出。这时需要用到以下几个模运算的法则:

    (a+b)%c = (a%c+b%c)%c

    (a*b)%c = (a%c * b%c)%c

    class Solution {
        /**
         * @param key: A String you should hash
         * @param HASH_SIZE: An integer
         * @return an integer
         */
        public int hashCode(char[] key,int HASH_SIZE) {
            // write your code here
            int factor = 33 % HASH_SIZE;
            long cur33nModSzie = 1;
            long mod_sum = 0;
    
            for (int i = key.length - 1; i >= 0; i--) {
                if (i != key.length - 1) {
                    cur33nModSzie = (factor * cur33nModSzie) % HASH_SIZE;
                }
                long tmp = (key[i] % HASH_SIZE) * cur33nModSzie;
                mod_sum += tmp % HASH_SIZE;
            }
    
            return (int)(mod_sum % HASH_SIZE);
        }
    }
    View Code

    Heapify

     堆化

    思路:把根节点的左右子堆分别堆化出来,然后再把根节点作siftdown就把整个堆堆化出来了。

    public class Solution {
        /**
         * @param A: Given an integer array
         * @return: void
         */
        public void heapify(int[] A) {
            // write your code here
            heapify_helper(A, 0);
        }
        
        public void heapify_helper(int[] A, int root) {
            if (root * 2 + 1 <= A.length - 1) {
                heapify_helper(A, root * 2 + 1);
            }
            
            if (root * 2 + 2 <= A.length - 1) {
                heapify_helper(A, root * 2 + 2);
            }
            
            int hole = root;
            int left = hole * 2 + 1;
            int right = hole * 2 + 2;
            while (right <= A.length - 1 && 
                   (A[hole] > A[left] || A[hole] > A[right])) {
                int tmp = A[hole];
                int next_hole = A[left] < A[right] ? left : right;
                A[hole] = A[next_hole];
                A[next_hole] = tmp;
                hole = next_hole;
                left = hole * 2 + 1;
                right = hole * 2 + 2;
            }
            
            if (left <= A.length - 1 && A[hole] > A[left]) {
                int tmp = A[hole];
                A[hole] = A[left];
                A[left] = tmp;
            }
        }
    }
    View Code

    Isomorphic Strings

    同构字符串

    思路:用hash map和set。

     1 public class Solution {
     2     public boolean isIsomorphic(String s, String t) {
     3         Set<Character> set = new HashSet<Character>();
     4         Map<Character, Character> map = new HashMap<Character, Character>();
     5         
     6         for (int i = 0; i < s.length(); i++) {
     7             char char_s = s.charAt(i);
     8             char char_t = t.charAt(i);
     9             if (map.containsKey(char_s)) {
    10                 if (map.get(char_s) != char_t) {
    11                     return false;
    12                 }
    13             } else {
    14                 if (set.contains(char_t)) {
    15                     return false;
    16                 }
    17                 map.put(char_s, char_t);
    18                 set.add(char_t);
    19             }
    20         }
    21         return true;
    22     }
    23 }
    View Code

      

    Implement Queue by Two Stacks

     队列实现栈

    思路:入队时压入stack1,出队时检查stack2,若stack2为空则将stack1中所有元素压入stack2,若不为空则弹出元素。

    public class Queue {
        private Stack<Integer> stack1;
        private Stack<Integer> stack2;
    
        public Queue() {
           // do initialization if necessary
           stack1 = new Stack<Integer>();
           stack2 = new Stack<Integer>();
        }
        
        public void push(int element) {
            // write your code here
            stack1.push(element);
        }
    
        public int pop() {
            // write your code here
            if (stack2.isEmpty()) {
                moveStack1toStack2(stack1, stack2);
            }
            return stack2.pop();
        }
    
        public int top() {
            // write your code here
            if (stack2.isEmpty()) {
                moveStack1toStack2(stack1, stack2);
            }
            return stack2.peek();
        }
        
        public void moveStack1toStack2(Stack<Integer> s1, Stack<Integer> s2) {
            while(!s1.isEmpty()) {
                s2.push(s1.pop());
            }
        }
    }
    View Code

    LRU Cache

     LRU缓存

    思路:使用linkedHashMap,每次操作就把当前结点移到链表尾部,删除元素时删除链表头部的元素。

    public class Solution {
        Map<Integer, ListNode> map;
        ListNode head;
        ListNode tail;
        int capacity;
        int size;
        // @param capacity, an integer
        public Solution(int capacity) {
            // write your code here
            map = new HashMap<Integer, ListNode>();
            head = new ListNode(0, -1);
            tail = new ListNode(0, -1);
            this.capacity = capacity;
        }
    
        // @return an integer
        public int get(int key) {
            // write your code here
            ListNode cur = map.get(key);
            if (cur == null) {
                return -1;
            }
            move2End(cur);
            return cur.val;
        }
    
        // @param key, an integer
        // @param value, an integer
        // @return nothing
        public void set(int key, int value) {
            // write your code here
            if (map.containsKey(key)) {
                ListNode cur = map.get(key);;
                cur.val = value;
                move2End(cur);
            } else {
                if (size < capacity) {
                    size++;
                } else {
                    //delete dummy's next
                    ListNode del = head.next;
                    head.next = del.next;
                    del.next.pre = head;
                    map.remove(del.key);
                }
                //put in the end
                ListNode newData = new ListNode(value, key);
                ListNode preTail = (tail.pre == null) ? head : tail.pre;
                preTail.next = newData;
                tail.pre = newData;
                newData.pre = preTail;
                newData.next = tail;
                map.put(key, newData);
            }
        }
        
        private void move2End(ListNode cur) {
            cur.pre.next = cur.next;
            cur.next.pre = cur.pre;
            
            ListNode preTail = tail.pre;
            preTail.next = cur;
            tail.pre = cur;
            cur.pre = preTail;
            cur.next = tail;
        }
    }
    
    class ListNode {
        int val;
        int key;
        ListNode next;
        ListNode pre;
        ListNode(int val, int key) {
            this.val = val;
            this.key = key;
        }
    }
    View Code

    LFU Cache

     LFU缓存

    思路:使用hash表和平衡树结合,这里用平衡树是因为它 找最小值 以及 删除任意节点 性能都是O(lgn),这是堆和哈希表达不到的。

    public class LFUCache {
        Map<Integer, TreeSetElement> map;
        TreeSet<TreeSetElement> set;
        long TIME_STAMP;
        int capacity;
        
        // @param capacity, an integer
        public LFUCache(int capacity) {
            // Write your code here
            map = new HashMap<Integer, TreeSetElement>();
            set = new TreeSet<TreeSetElement>();
            this.capacity = capacity;
        }
    
        // @param key, an integer
        // @param value, an integer
        // @return nothing
        public void set(int key, int value) {
            // Write your code here
            if (map.containsKey(key)) {
                TreeSetElement cur = map.get(key);
                TreeSetElement copy = new TreeSetElement(TIME_STAMP++, cur.count + 1, key, value);
                map.put(key, copy);
                set.remove(cur);
                set.add(copy);
            } else {
                if (map.size() < capacity) {
                    TreeSetElement cur = new TreeSetElement(TIME_STAMP++, 1, key, value);
                    map.put(key, cur);
                    set.add(cur);
                } else {
                    TreeSetElement min = set.first();
                    set.remove(min);
                    map.remove(min.hashMapKey);
                    TreeSetElement cur = new TreeSetElement(TIME_STAMP++, 1, key, value);
                    map.put(key, cur);
                    set.add(cur);
                }
            }
        }
    
        public int get(int key) {
            // Write your code here
            if (map.containsKey(key)) {
                TreeSetElement cur = map.get(key);
                TreeSetElement copy = new TreeSetElement(TIME_STAMP++, 
                                                cur.count + 1, key, cur.value);
                map.put(key, copy);
                set.remove(cur);
                set.add(copy);
                return cur.value;
            } else {
                return -1;
            }
        }
        
        
    }
    
    class TreeSetElement implements Comparable<TreeSetElement>{
        long time_stamp;
        int count;
        int hashMapKey;
        int value;
        TreeSetElement(long time_stamp, int count, int hashMapKey, int value) {
            this.time_stamp = time_stamp;
            this.count = count;
            this.hashMapKey = hashMapKey;
            this.value = value;
        }
        
        public int compareTo(TreeSetElement e) {
            if (count < e.count) {
                return -1;
            } else if (count > e.count) {
                return 1;
            } else {
                if (time_stamp > e.time_stamp) {
                    return 1;
                } else if (time_stamp < e.time_stamp) {
                    return -1;
                } else {
                    return 0;
                }    
            }
        }
    }
    View Code

    Longest Consecutive Sequence

     最长连续序列

    思路:还没做出来。。

    Largest Rectangle in Histogram

    直方图最大矩形覆盖

    思路: 使用单调栈。每个柱形弹出单调栈时可以知道左边第一个低于它的柱形以及右边第一个低于它的柱形,可算出最大面积。

    public class Solution {
        /**
         * @param height: A list of integer
         * @return: The area of largest rectangle in the histogram
         */
        public int largestRectangleArea(int[] height) {
            // write your code here
            int res = 0;
            Stack<Integer> s = new Stack<Integer>();
            
            for (int i = 0; i <= height.length; i++) {
                int cur = i == height.length ? -1 : height[i];
                while (!s.isEmpty() && height[s.peek()] >= cur) {
                    int h = height[s.pop()];
                    int w = s.isEmpty() ? i : i - s.peek() - 1;
                    res = Math.max(res, w * h);
                }
                s.push(i);
            }
            return res;
        }
    }
    View Code

    Maximal Rectangle

    最大矩形

    思路:这个问题可以转化为对每一行使用“直方图最大矩形覆盖”算法来求最大矩形。时间复杂度为O(m * n)。

     1 public class Solution {
     2     public int maximalRectangle(char[][] matrix) {
     3         if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
     4             return 0;
     5         }
     6         
     7         int m = matrix.length;
     8         int n = matrix[0].length;
     9         int[][] HistogramMat = new int[m][n];
    10         for (int j = 0; j < n; j++) {
    11             HistogramMat[0][j] = matrix[0][j] - '0';
    12         }
    13         for (int i = 1; i < m; i++) {
    14             for (int j = 0; j < n; j++) {
    15                 HistogramMat[i][j] = matrix[i][j] == '1' ? HistogramMat[i - 1][j] + 1 : 0;
    16             }
    17         }
    18         
    19         int res = Integer.MIN_VALUE;
    20         Stack<Integer> s = new Stack<Integer>();
    21         for (int i = 0; i < m; i++) {
    22             s.clear();
    23             for (int j = 0; j <= n; j++) {
    24                 int num = j == n ? -1 : HistogramMat[i][j];
    25                 while (!s.isEmpty() && HistogramMat[i][s.peek()] >= num) {
    26                     int h = HistogramMat[i][s.pop()];
    27                     int w = s.isEmpty() ? j : j - s.peek() - 1;
    28                     res = Math.max(res, h * w);
    29                 }
    30                 s.push(j);
    31             }
    32         }
    33         return res;
    34     }
    35 }
    View Code

    Max Tree

    最大树

    思路:(1)对于任意一个数组元素,它的左子树的范围一直到它左边第一个比它大的树,右子树的范围一直到它右边第一个比它大的树;

            (2)在结论(1)的基础上,对于情形[......b......a......c......],其中,b是a左边第一个比a大的数,c是a右边第一个比a大的数:

          若 b < c , 则a一定是b的右儿子;若b>c,则a一定是c的左儿子;

       (3)在涉及到求数组中每个数的左(右)两边第一个比它大(小)的数时,联想到单调栈。单调栈的性质是,每个栈元素a出栈时,下一个栈顶元素是a的左边界,使a出栈的元素b是a的右边界。

       (4)基于上,每个元素出栈时,都可以找到它的父亲节点并作连接,最后用压入一个无穷大,使得所有元素出栈。最终返回这个无穷大节点的左子树即为max tree。

    /**
     * Definition of TreeNode:
     * public class TreeNode {
     *     public int val;
     *     public TreeNode left, right;
     *     public TreeNode(int val) {
     *         this.val = val;
     *         this.left = this.right = null;
     *     }
     * }
     */
    public class Solution {
        /**
         * @param A: Given an integer array with no duplicates.
         * @return: The root of max tree.
         */
        public TreeNode maxTree(int[] A) {
            // write your code here
           Stack<TreeNode> s = new Stack<TreeNode>();
           for (int i = 0; i <= A.length; i++) {
               int cur = (i == A.length) ? Integer.MAX_VALUE : A[i];
               TreeNode right = new TreeNode(cur);
               while (!s.isEmpty() && s.peek().val < cur) {
                   TreeNode nowNode = s.pop();
                   if (!s.isEmpty()) {
                       TreeNode left = s.peek();
                       if (left.val < right.val) {
                           left.right = nowNode; 
                       } else {
                           right.left = nowNode;
                       }
                   } else {
                       right.left = nowNode;
                   }
               }
               s.push(right);
           }
           return s.peek().left;
        }
        
    }
    View Code

    Merge k Sorted Arrays

     合并k个数组

    思路:用最小堆。注意堆中存储的元素还要记录该元素所在的行和列,便于知道删除它后下一个进来的是谁。

    public class Solution {
        /**
         * @param arrays k sorted integer arrays
         * @return a sorted array
         */
        public List<Integer> mergekSortedArrays(int[][] arrays) {
            // Write your code here
            ArrayList<Integer> results = new ArrayList<Integer>();
            if (arrays == null || arrays.length == 0) {
                return results;
            }
            int m = arrays.length;
            int n = arrays[0].length;
            PriorityQueue<Element> minHeap = new PriorityQueue<Element>();
            for (int i = 0; i < m; i++) {
                if (arrays[i].length == 0) {
                    continue;
                }
                minHeap.offer(new Element(arrays[i][0], i, 0));
            }
            while (!minHeap.isEmpty()) {
                Element top = minHeap.poll();
                results.add(top.val);
                if (top.col + 1 < arrays[top.row].length) {
                    minHeap.add(new Element(arrays[top.row][top.col + 1], top.row, top.col + 1));
                }
            }
            return results;
        }
    }
    class Element implements Comparable<Element> {
        int val;
        int row;
        int col;
        public Element(int val, int row, int col) {
            this.val = val;
            this.row = row;
            this.col = col;
        }
        public int compareTo(Element eleB) {
            return val - eleB.val;
        }
    }
    View Code

    Number of Boomerangs

    Boomerangs的数目

    思路:两重循环。把每个点作为Boomerangs中的第一个点,建一个hash表,表示到该点距离为Key的点有value个。时间复杂度O(n^2)。

     1 public class Solution {
     2     public int numberOfBoomerangs(int[][] points) {
     3         int res = 0;
     4         for (int i = 0; i < points.length; i++) {
     5             Map<Long, Integer> map = new HashMap<Long, Integer>();
     6             for (int j = 0; j < points.length; j++) {
     7                 if (i == j) {
     8                     continue;
     9                 }
    10                 long diff1 = points[i][0] - points[j][0];
    11                 long diff2 = points[i][1] - points[j][1];
    12                 
    13                 long distance = diff1 * diff1 + diff2 * diff2;
    14                 if (!map.containsKey(distance)) {
    15                     map.put(distance, 0);
    16                 }
    17                 map.put(distance, map.get(distance) + 1);
    18             }
    19             for (long key : map.keySet()) {
    20                 int val = map.get(key);
    21                 res += val * (val - 1);
    22             }
    23         }
    24         return res;
    25     }
    26 }
    View Code

    Super Ugly Number

    超级丑数

    思路1:与丑数II这道题一样。如果用heap时间复杂度就是O(knlgn),过不了leetcode最后两个大的test case。

     1 public class Solution {
     2     public int nthSuperUglyNumber(int n, int[] primes) {
     3         PriorityQueue<Long> q = new PriorityQueue<Long>();
     4         Set<Long> set = new HashSet<Long>();
     5         q.offer(1L);
     6         int count = 0;
     7         while(!q.isEmpty()) {
     8             long cur = q.poll();
     9             if (++count == n) {
    10                 return (int)cur;
    11             }
    12             for (int prime : primes) {
    13                 if (!set.contains(cur * prime)) {
    14                     q.offer(cur * prime);
    15                     set.add(cur * prime);
    16                 }
    17             }
    18         }
    19         return 0;
    20     }
    21 }
    View Code

    思路2:与丑数II的思路2一样的方法,只不过p1,p2,p3用一个索引数组indexs[]来代替。时间复杂度O(nk)。

     1 public class Solution {
     2     public int nthSuperUglyNumber(int n, int[] primes) {
     3         int[] uglys = new int[n];
     4         uglys[0] = 1;
     5         int[] indexs = new int[primes.length];
     6         int last = 1;
     7         
     8         for (int i = 1; i < n; i++) {
     9             for (int j = 0; j < indexs.length; j++) {
    10                 while (primes[j] * uglys[indexs[j]] <= last) {
    11                     indexs[j]++;
    12                 }
    13             }
    14             last = Integer.MAX_VALUE;
    15             for (int j = 0; j < indexs.length; j++) {
    16                 last = Math.min(last, primes[j] * uglys[indexs[j]]);
    17             }
    18             uglys[i] = last;
    19         }
    20         return uglys[n - 1];
    21     }
    22 }
    View Code

      

    Stack Sorting

     栈排序

    思路:一个栈作为单调栈,另一个栈作为辅助栈。不断把辅助栈中的元素压入单调栈,单调栈弹出的元素存回辅助栈。直到所有元素都压入单调栈为止。

    public class Solution {
        /**
         * @param stack an integer stack
         * @return void
         */
        public void stackSorting(Stack<Integer> stack) {
            // Write your code here
            Stack<Integer> stack2 = new Stack<Integer>();
            while (!stack.isEmpty()) {
                int cur = stack.pop();
                while (!stack2.isEmpty() && stack2.peek() < cur) {
                    stack.push(stack2.pop());
                }
                stack2.push(cur);
            }
            while (!stack2.isEmpty()) {
                stack.push(stack2.pop());
            }
        }
    }
    View Code

    Top k Largest Numbers

     最大的k个数

    思路1:用最大堆,时间复杂度为

    思路2:用最小堆,时间复杂度为

    思路3:快速选择算法,时间复杂度为

      

    Top k Largest Numbers II

     最大的k个数II

    思路:流式数据版的topk,用一个最小堆存当前的topk。

    public class Solution {
        int k;
        PriorityQueue<Integer> minHeap;
    
        public Solution(int k) {
            // initialize your data structure here.
            this.k = k;
            minHeap = new PriorityQueue<Integer>();
        }
    
        public void add(int num) {
            // Write your code here
            if (minHeap.size() < k) {
                minHeap.add(num);
            } else {
                if (minHeap.peek() < num) {
                    minHeap.poll();
                    minHeap.add(num);
                } else {
                    return;
                }
            }
        }
    
        public List<Integer> topk() {
            // Write your code here
            List<Integer> res = new ArrayList<Integer>();
            for (Integer num : minHeap) {
                res.add(num);
            }
            Collections.sort(res, Collections.reverseOrder());
            return res;
        }
    }
    View Code

    Top K Frequent Words

     最高频k个单词

    思路:类似于TOP K ii(流式数据topk)这道题,即维护一个最小堆,这个最小堆里保存着当前的TOPk。

    public class Solution {
        /**
         * @param words an array of string
         * @param k an integer
         * @return an array of string
         */
        public String[] topKFrequentWords(String[] words, int k) {
            // Write your code here
            String[] res = new String[k];
            if (k == 0) {
                return res;
            }
            Map<String, Integer> map = new HashMap<String, Integer>();
            for (String word : words) {
                int value = map.get(word) == null ? 1 : map.get(word) + 1;
                map.put(word, value);
            }
            
            PriorityQueue<Element> q = new PriorityQueue<Element>();
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                Element e = new Element(entry.getKey(), entry.getValue());
                if (q.size() < k) {
                    q.offer(e);
                } else {
                    if (q.peek().compareTo(e) < 0) {
                        q.poll();
                        q.offer(e);
                    }
                }
            }
            
            for (int i = k-1; i >= 0; i--) {
                res[i] = q.poll().key;
            }
            return res;
        }
    }
    
    class Element implements Comparable<Element> {
        String key;
        int value;
        Element(String key, int value) {
            this.key = key;
            this.value = value;
        }
        public int compareTo(Element e) {
            if (value > e.value) {
                return 1;
            } else if (value < e.value) {
                return -1;
            } else {
                if (dictOrderCompare(key, e.key) > 0) {
                    return 1;
                } else {
                    return -1;
                }
            }
        }
        private int dictOrderCompare(String s1, String s2) {
            int p1 = 0;
            int p2 = 0;
            while (p1 <= s1.length() - 1 && p2 <= s2.length() - 1) {
                if (s1.charAt(p1) < s2.charAt(p2)) {
                    return 1;
                } else if (s1.charAt(p1) > s2.charAt(p2)) {
                    return -1;
                } else {
                    p1++;
                    p2++;
                }
            }
            
            if (p1 <= s1.length() - 1) {
                return -1;
            }
            
            if (p2 <= s2.length() - 1) {
                return 1;
            }
            return 0;
        }
    }
    View Code

    Ugly Number II

    丑数II

    思路1:使用heap。时间复杂度O(nlgn)。

     1 class Solution {
     2     /**
     3      * @param n an integer
     4      * @return the nth prime number as description.
     5      */
     6     public int nthUglyNumber(int n) {
     7         // Write your code here
     8         PriorityQueue<Long> minHeap = new PriorityQueue<Long>();
     9         Set<Long> set = new HashSet<Long>();
    10         minHeap.offer(1L);
    11         for (int i = 1; i <= n; i++){
    12             long cur = minHeap.poll();
    13             if (i == n) {
    14                 return (int)cur;
    15             }
    16             if (!set.contains(cur * 2)) {
    17                 minHeap.offer(cur * 2);
    18                 set.add(cur * 2);
    19             }
    20             if (!set.contains(cur * 3)) {
    21                 minHeap.offer(cur * 3);
    22                 set.add(cur * 3);
    23             }
    24             if (!set.contains(cur * 5)) {
    25                 minHeap.offer(cur * 5);
    26                 set.add(cur * 5);
    27             }
    28         }
    29         return -1;
    30     }
    31 };
    View Code

    思路2:使用一个list记录丑数,三个指针p1,p2,p3对应于已经输出的丑数list(p1),list(p2),list(p3),分别表示2,3,5的因子。三个乘积list(p1)*2,list(p2)*3,list(p3)*5应该大于上一个丑数,这三个乘积的最小值就是下一个丑数。时间复杂度O(n)。

     1 public class Solution {
     2     public int nthUglyNumber(int n) {
     3         List<Integer> uglys = new ArrayList<Integer>();
     4         uglys.add(1);
     5         int p1 = 0;
     6         int p2 = 0;
     7         int p3 = 0;
     8         
     9         int last = 1;
    10         
    11         for (int i = 1; i < n; i++) {
    12             while (uglys.get(p1) * 2 <= last) {
    13                 p1++;
    14             }
    15             while (uglys.get(p2) * 3 <= last) {
    16                 p2++;
    17             }
    18             while (uglys.get(p3) * 5 <= last) {
    19                 p3++;
    20             }
    21             last = Math.min(Math.min(uglys.get(p1) * 2, uglys.get(p2) * 3), uglys.get(p3) * 5);
    22             uglys.add(last);
    23         }
    24         return uglys.get(n - 1);
    25     }
    26 }
    View Code

      

    Valid Anagram

    有效的Anagram

    思路1:排序然后一一比较。时间复杂度O(nlgn),空间复杂度O(1)。

    思路2:hash。这里用数组,因为只有26个字母。时间复杂度O(n),空间复杂度O(1)。

     1 public class Solution {
     2     public boolean isAnagram(String s, String t) {
     3         if (s == null || t == null || s.length() != t.length()) {
     4             return false;
     5         }
     6         int[] count = new int[26];
     7         for (int i = 0; i < s.length(); i++) {
     8             char c = s.charAt(i);
     9             count[c - 'a']++;
    10         }
    11         for (int i = 0; i < t.length(); i++) {
    12             char c = t.charAt(i);
    13             if (count[c - 'a'] == 0) {
    14                 return false;
    15             }
    16             count[c - 'a']--;
    17         }
    18         return true;
    19         
    20     }
    21 }
    View Code
  • 相关阅读:
    面试题33:把数组排成最小的数
    面试题32:从1到n整数中1出现的次数
    面试题31:连续子数组的最大和
    HTTPS 及加密信息全解析
    面试题30:最小的k个数
    linux退出vi
    linux清除当前屏幕
    java web开发环境配置
    jQuery积累
    html5离线应用详摘
  • 原文地址:https://www.cnblogs.com/coldyan/p/6000311.html
Copyright © 2011-2022 走看看