12. Advanced Data Structure(with example problems)

0. Overview

This time we will discuss some helpful data structures in algorithm. In many times, a good data structure could save us lots of energy, thus, besides the basic data structure vector, stack and queue, we will discuss below data structure:

  1. Hash
  2. Heap/ Priority Queue
  3. Union Find
  4. Trie Tree
  5. Stack (monotonous stack, reverse stack)

1. Data Type:

1.1 Hash

Operation: O(1) Insert / O(1) Find / O(1) Delete

Saturation of the hash table
Saturation = number of actual storage elements / total open space

Generally speaking, when it exceeds 1/10 (experience value), it needs rehash

1.1.1 Examples:

129. Rehashing

Using vector<Listnode* > to store the head of each index, hash table is to store the last node of the list of each index.

Notice:
C++/Java: if you directly calculate -4 % 3 you will get -1. You can use function: a % b = (a % b + b) % b to make it is a non negative integer.
Python: you can directly use -1 % 3, you will get 2 automatically.

    vector<ListNode*> rehashing(vector<ListNode*> hashTable) {
        // write your code here
        if(hashTable.empty() )
            return hashTable;
        int n = hashTable.size() *2;
        vector<ListNode*> result(n, nullptr);
        unordered_map<int, ListNode*> hash;
        
        for(auto node : hashTable) {
            while(node) {
                int index = (node->val % n + n) % n;
                auto tmp = node ->next;
                node ->next = nullptr;
                if(hash.count(index)) {
                    hash[index] ->next = node;
                    hash[index] = hash[index] ->next;
                } else {
                    result[index] = node;
                    hash[index] = node;
                }
                node = tmp;
            }
        }
        return result;
    }

134. LRU Cache

List in C++ is doubly linked list, thus, it would be much easier than we thought.

#include<list>
class LRUCache {
private:
    int limit;
    list<int> cache;
    unordered_map<int, int> hash;

public:
    /*
    * @param capacity: An integer
    */LRUCache(int capacity) {
        // do intialization if necessary
        limit = capacity;
    }

    /*
     * @param key: An integer
     * @return: An integer
     */
    int get(int key) {
        // write your code here
        
        auto it = find(cache.begin(), cache.end(), key);
        if(it == cache.end())
            return -1;
            
        cache.erase(it);
        cache.push_back(key);
        
        return hash[key];        
    }

    /*
     * @param key: An integer
     * @param value: An integer
     * @return: nothing
     */
    void set(int key, int value) {
        // write your code here
        cache.remove(key);
        
        if(cache.size() == limit) 
            cache.pop_front();
        
        cache.push_back(key);
        hash[key] = value;
    }
};

171. Anagrams

    vector<string> anagrams(vector<string> &strs) {
        // write your code here
        unordered_map<string, vector<string>> hash;
        vector<string> result;
        
        for(auto word : strs) {
            string tmp = word;
            sort(tmp.begin(), tmp.end());
            
            if(hash.count(tmp)) {
                hash[tmp].push_back(word);
            } else {
                hash[tmp] = vector<string> ();
                hash[tmp].push_back(word);
            }
        }
        
        for(auto now : hash) {
            if(now.second.size() >= 2) {
                result.insert(result.end(), now.second.begin(), now.second.end() );
            }
        }
        
        return result;
    }

124. Longest Consecutive Sequence
Using hash set for quick look-up of pre-number/ post-number.

To improve the efficiency, in hash set, while each element being visited, remove it from the hash set to save the space and time.

    int longestConsecutive(vector<int> &nums) {
        // write your code here
        unordered_set<int> set(nums.begin(), nums.end() );
        
        int result = -1;
        
        for(auto num : nums) {
            if(set.count(num)) {
                set.erase(num);
                int prev = num -1;
                int post = num +1;
                while(set.count(prev) )
                    set.erase(prev--);
                    
                while(set.count(post) )
                    set.erase(post++);
                    
                result = max(result, post - prev -1);
            }
        }
        return result;
    }
};

1.2 Heap/ Priority Queue

Operation: O(log N) Add / O(log N) Remove / O(1) Min or Max

1.2.1 Examples:

4. Ugly Number II

    int nthUglyNumber(int n) {
        int *uglys = new int[n];
        uglys[0] = 1;
        int next = 1;
        int *p2 = uglys;
        int *p3 = uglys;
        int *p5 = uglys;
        while (next < n){
            int m = min(min(*p2 * 2, *p3 * 3), *p5 * 5);
            uglys[next] = m;
            while (*p2 * 2 <= uglys[next])
                *p2++;
            while (*p3 * 3 <= uglys[next])
                *p3++;
            while (*p5 * 5 <= uglys[next])
                *p5++;
            next++;
        }
        int uglyNum = uglys[n - 1];
        delete[] uglys;
        return uglyNum;
    }

545. Top k Largest Numbers II
Simple implementation of priority_queue

class Solution {
private:
    priority_queue<int> pq;
    int limit;

public:
    /*
    * @param k: An integer
    */Solution(int k) {
        // do intialization if necessary
        limit = k;
    }

    /*
     * @param num: Number to be added
     * @return: nothing
     */
    void add(int num) {
        // write your code here
        pq.push(num);
    }

    /*
     * @return: Top k element
     */
    vector<int> topk() {
        // write your code here
        vector<int> result;
        int cnt = limit;
        while(pq.size() > 0 && cnt) {
            result.push_back(pq.top());
            pq.pop();
            cnt--;
        }
        
        for(auto num : result) 
            pq.push(num);
            
        return result;
    }
};

104. Merge K Sorted Lists
Practice writing the compare of priority_queue

class my_comparison {
public:
    bool operator() (ListNode *n1, ListNode *n2) {
        return n1 ->val > n2 ->val;
    }
} ;

class Solution {
public:
    /**
     * @param lists: a list of ListNode
     * @return: The head of one sorted list.
     */
    ListNode *mergeKLists(vector<ListNode *> &lists) {
        // write your code here
        priority_queue<ListNode*, vector<ListNode*>, my_comparison> pq;
        for(auto now : lists) {
            while(now) {
                ListNode *tmp = now ->next;
                now ->next = nullptr;
                
                pq.push(now);
                now = tmp;
            }
        }
        
        ListNode *dummy = new ListNode(0);
        ListNode *ptr = dummy;
        while( !pq.empty() ) {
            ListNode *tmp = pq.top(); pq.pop();
            ptr ->next = tmp;
            ptr = ptr ->next;
        }
        return dummy ->next;
        
    }
};

613. High Five

class cmp {
public:
    bool operator() (Record a, Record b) {
        return (a.score > b.score);
    }
};

class Solution {
public:
    /**
     * @param results a list of <student_id, score>
     * @return find the average of 5 highest scores for each person
     * map<int, double> (student_id, average_score)
     */
    map<int, double> highFive(vector<Record>& results) {
        // Write your code here
        // if (results.empty()) ??
        map<int, double> ans;
        unordered_map<int, int> id_to_row;

        int n = results.size(), m = 0;
        for (int i = 0; i < n; i++) {
            if (id_to_row.count(results[i].id) == 0){   //没出现过
                id_to_row[results[i].id]=m++;
            }
        }
        
        vector<priority_queue<Record, vector<Record>, cmp>> pq(m);
        for (int i = 0; i < n; i++) {
            int row = id_to_row[results[i].id];
            pq[row].push(results[i]);
            if (pq[row].size() >5) {
                pq[row].pop();
            }
        }
        
        for (int i = 0; i < m; i++) {
            int count = pq[i].size(),  id = pq[i].top().id;
            float sum = 0;
            while (!pq[i].empty()) {
                sum+=(pq[i].top().score);
                pq[i].pop();
            }
            
            ans[id] = sum/count;
        }
        
        return ans;
    }
};

612. K Closest Points

class my_compare{
public:
    bool operator() (pair<int, Point> &p1, pair<int, Point> &p2) {

        int diff = p1.first - p2.first;
        
        if(diff == 0)
            diff = p1.second.x - p2.second.x;
            
        if(diff == 0)
            diff = p1.second.y - p2.second.y;
            
        return diff > 0;
    }
};

class Solution {
public:
    /**
     * @param points: a list of points
     * @param origin: a point
     * @param k: An integer
     * @return: the k closest points
     */
    vector<Point> kClosest(vector<Point> &points, Point &origin, int k) {
        // write your code here
        
        priority_queue<pair<int, Point>, vector<pair<int, Point>>, my_compare> pq;
        for(auto point : points) {
            int distance = pow((point.x - origin.x), 2) + pow((point.y - origin.y), 2);
            pq.push(make_pair(distance, point));
        }
        vector<Point> result;
        while(!pq.empty() && k) {\
            auto tmp = pq.top(); pq.pop();
            k--;
            result.push_back(tmp.second);
        }
        return result;
    }
};

486. Merge K Sorted Arrays

    vector<int> mergekSortedArrays(vector<vector<int>> &arrays) {
        // write your code here
        priority_queue<int,vector<int>, greater<int>> pq;
        for(auto arr : arrays ) 
            for(auto num : arr) 
                pq.push(num);
                
        vector<int> result;
        
        while(!pq.empty() ) {
            result.push_back(pq.top());
            pq.pop();
        }
        return result;
    }

401. Kth Smallest Number in Sorted Matrix
Surprisingly, priority_queue has good performance.

    int kthSmallest(vector<vector<int>> &matrix, int k) {
        // write your code here
        priority_queue<int, vector<int>, less<int>> pq;
        
        for(auto arr : matrix) 
            for(auto num : arr) {
                if(pq.size() < k) {
                    pq.push(num);
                } else if(pq.top() > num) {
                    pq.pop();
                    pq.push(num);
                } 
            }
            
        return pq.top();
    }

544. Top k Largest Numbers

    vector<int> topk(vector<int> &nums, int k) {
        // write your code here
        priority_queue<int> pq;
        for(auto num : nums)
            pq.push(num);
         
        vector<int> result;
        for(int i = 0; i < k; i++)
        {
            result.push_back(pq.top());
            pq.pop();
        }        
        return result;
    }

81. Find Median from Data Stream

class Solution {
private:
    vector<int> result;
    int size = 0;
    priority_queue<int> maxHeap;
    priority_queue<int, vector<int>, greater<int>> minHeap;
    
public:
    /**
     * @param nums: A list of integers
     * @return: the median of numbers
     */
    vector<int> medianII(vector<int> &nums) {
        // write your code here
        for(auto num : nums) {
            addNum(num);
            result.push_back(getMedian());
        }
        return result;
    }
    
    void addNum(int num) {
        maxHeap.push(num);
        if(size % 2 == 0) {
            if(minHeap.empty()) {
                size++;
                return;
            } else if (maxHeap.top() > minHeap.top() ) {
            // switch the roots of two priority_queue
                int maxHeapRoot = maxHeap.top(); maxHeap.pop();
                int minHeapRoot = minHeap.top(); minHeap.pop();
                maxHeap.push(minHeapRoot);
                minHeap.push(maxHeapRoot);
            }
        } else {
            minHeap.push(maxHeap.top());
            maxHeap.pop();
        }        
        size++;
    }
    
    int getMedian() {
        return maxHeap.top();
    }
};

363. Trapping Rain Water
This problem is twp-pointer one, which is pre-heat of the next problem.

    int trapRainWater(vector<int> &heights) {
        // write your code here
        int maxHeight = -1, maxIndex = -1;
        int sum = 0;
        for(int i = 0; i < heights.size(); i++){
            if(heights[i] > maxHeight){
                maxHeight = heights[i];
                maxIndex = i;
            }
        }
        
        // left part
        maxHeight = -1;
        for(int i = 0; i < maxIndex; i++){
            if(maxHeight > heights[i]) {
                sum += maxHeight - heights[i];
            }
            maxHeight = max(maxHeight, heights[i]);
        }
        
        // right part
        maxHeight = -1;
        for(int i = heights.size()-1; i > maxIndex; i--){
            if(maxHeight > heights[i]) {
                sum += maxHeight - heights[i];
            }
            maxHeight = max(maxHeight, heights[i]);
        }
        
        return sum;
    }

364. Trapping Rain Water II

    int trapRainWater(vector<vector<int>> &heights) {
        // write your code here
        int m = heights.size();
        int n = (m == 0)? 0 : heights[0].size();
        
        priority_queue<pair<int, pair<int ,int>>,
                            vector<pair<int, pair<int ,int>>>,
                            greater<pair<int, pair<int ,int>>>>     pq;
        
        vector<vector<int>> visited(m, vector<int>(n, 0));
        
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if( !(i == 0 || i == m-1 || j == 0 || j == n-1) ){
                    continue;
                }
                visited[i][j] = 1;
                pq.push(make_pair(heights[i][j], make_pair(i, j)));
            }
        }
        
        vector<int> dir = {0,1,0,-1,0};
        int res = 0, H = INT_MIN;
        
        while(!pq.empty()){
            auto p = pq.top(); pq.pop();
            int h = p.first, x = p.second.first, y = p.second.second;
            H = max(H, h);
            for(int i = 0; i < 4; i++) {
                int nx = x + dir[i];
                int ny = y + dir[i+1];
                if(nx < 0 || nx > m-1 || ny < 0 || ny > n-1 || visited[nx][ny]){
                    continue;
                }
                visited[nx][ny] =1;
                int diff = H - heights[nx][ny];
                if(diff > 0)
                    res += diff;
                pq.push(make_pair(heights[nx][ny], make_pair(nx, ny)));
            }
        }
        
        return res;
        
    }

360. Sliding Window Median
// notice here, the syntax one
// set.erase(const Iterator it), erase() need the const iterator, set.begin() is constant whereas set.rbegin() is not. Thus, we need *set.lower_bound(set.rbegin()) as const iterator of rbegin() element.

// windows problem:
    // a. add an element
    // b. remove an element
    vector<int> medianSlidingWindow(vector<int> &nums, int k) {
        // write your code here
        vector<int> res;
        multiset<int> min, max;
        int n = nums.size();
        if(n == 0)
            return nums;
        
        for(int i = 0; i < k; i++){
            max.insert(nums[i]);
        }
        
        for(int i = 0; i < k/2; i++){
            min.insert(*max.rbegin());
            max.erase(max.lower_bound(*max.rbegin()));
        }
        
        for(int i = k; i < n; i++){
            res.push_back(*max.rbegin());
            if(max.find(nums[i-k]) != max.end()){
                max.erase(max.find(nums[i-k]));
                max.insert(nums[i]);
            }
            else{
                min.erase(min.find(nums[i-k]));
                min.insert(nums[i]);
            }
            
            if(max.size() > 0 && min.size() > 0 && *max.rbegin() > *min.begin()){
                int tmp = *max.rbegin();
                max.erase(max.lower_bound(*max.rbegin()));
                max.insert(*min.begin());
                min.erase(min.begin());
                min.insert(tmp);
            }
        }
        res.push_back(*max.rbegin());
        
        return res;
    }

1.3 Union Find

What’s Union Find?
A data structure for solving set query merge

Operations:
O(1)find/O(1)union

What’s Union Find for?

  1. Check whether in a set: find()
  2. Merge set: union()
  3. Query the number of one’s set
  4. Query the number of sets

Union Find Template

class UnionFind{
	vector<int> father;
public:
	int find(int x) {
		if(father[x] == x ) 
			return x;

		return find(father[x]);
	}

	void union(int a, int b) {
		int root_a = find(a);
		int root_b = find(b);
		if(root_a != root_b)
			father[root_a] = root_b;
	}

};
1.3.1 Examples:

589. Connecting Graph
Union Find is a perfect data structure to find the whether we elements were in the same component.

class ConnectingGraph {
    vector<int> father;
public:
    /*
    * @param n: An integer
    */ConnectingGraph(int n) {
        // do intialization if necessary
        father.resize(n +1);
        for(int i = 1; i <= n; i++ ) {
            father[i] = i;
        }
    }
    
    int find(int x) {
        if(father[x] == x)
            return x;
        
        // it will update the father
        return father[x] = find(father[x]);
    }

    /*
     * @param a: An integer
     * @param b: An integer
     * @return: nothing
     */
    void connect(int a, int b) {
        // write your code here
        int root_a = find(a);
        int root_b = find(b);
        if(root_a != root_b) {
            father[root_a] = root_b;
        }
    }

    /*
     * @param a: An integer
     * @param b: An integer
     * @return: A boolean
     */
    bool query(int a, int b) {
        // write your code here
        int root_a = find(a);
        int root_b = find(b);
        return root_a == root_b;
    }
};

590. Connecting Graph II
In this one, we need to get the size of community, which means we need to record the size at the index of root node. After comparison, using vector instead of using unordered_map has a better performance as hash map.

class ConnectingGraph2 {
    vector<int> hash;
    vector<int> father;
public:
    /*
    * @param n: An integer
    */ConnectingGraph2(int n) {
        // do intialization if necessary
        father.resize(n +1);
        hash.resize(n +1);
        for(int i = 1; i <= n; i++) {
            hash[i] = 1;
            father[i] = i;
        }
    }
    
    int find(int x) {
        if(father[x] == x) 
            return x;
            
        return father[x] = find(father[x]);
    }

    /*
     * @param a: An integer
     * @param b: An integer
     * @return: nothing
     */
    void connect(int a, int b) {
        // write your code here
        int root_a = find(a);
        int root_b = find(b);
        if(root_a != root_b) {
            father[root_a] = root_b;
            hash[root_b] += hash[root_a];
        }
    }

    /*
     * @param a: An integer
     * @return: An integer
     */
    int query(int a) {
        // write your code here
        int root_a = find(a);
        return hash[root_a];
    }
};

591. Connecting Graph III

class ConnectingGraph3 {
    int cnt = 0;
    vector<int> father;
public:
    /**
     * @param a: An integer
     * @param b: An integer
     * @return: nothing
     */
    ConnectingGraph3(int n) {
        // initialize your data structure here.
        father.resize(n +1);
        cnt = n;
        for(int i = 1; i < n +1; i++) {
            father[i] = i;
        }
    }
    
    int find(int x) {
        if(father[x] == x) 
            return x;
            
        return father[x] = find(father[x]);
    }
    
    void connect(int a, int b) {
        // write your code here
        int root_a = find(a);
        int root_b = find(b);
        
        if(root_a != root_b) {
            cnt--;
            father[root_a] = root_b;
        }
    }

    /**
     * @return: An integer
     */
    int query() {
        // write your code here
        return cnt;
    }
};

434. Number of Islands II

In this matrix problem, we transfer the 2D location into linear index. It not only saves us from complicate operations of 2D points, but also do the minimum modification on Find-Union data structure.

/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */

class Solution {
public:
    vector<int> father;
    int find(int x) {
        if(father[x] == x)
            return x;
            
        return father[x] = find(father[x]);
    }
    
    bool merge(int a, int b) {
        if(father[a] == -1 || father[b] == -1)
            return false;
        int root_a = find(a);
        int root_b = find(b);
        if(root_a != root_b) {
            father[root_a] = root_b;
            return true;
        }
        return false;
    }
    
    /**
     * @param n: An integer
     * @param m: An integer
     * @param operators: an array of point
     * @return: an integer array
     */
    vector<int> numIslands2(int n, int m, vector<Point> &operators) {
        // write your code here
        father.resize(n * m, -1);
        int cnt = 0;
        vector<int> result;
        
        vector<int> dir {0,1,0,-1,0};
        for(auto op : operators) {
            int point = op.x * m + op.y;
            if(father[point] == -1) {
                cnt++;
                father[point] = point;
            }
            
            for(int i = 0; i < 4; i++) {
                int nx = op.x + dir[i];
                int ny = op.y + dir[i +1];
                if(0 <= nx && nx < n && 0 <= ny && ny < m) {
                    if(merge(nx * m + ny, point))
                        cnt--;
                }
            }
            result.push_back(cnt);
        }
        return result;
    }
};

178. Graph Valid Tree
To valid whether a graph is a tree, we could use union-find to store each node’s root node. In this case, every time we connect two node, all we need to do is to check whether their root nodes are the same, if so, then there exists a circle which cannot be a tree.

class Solution {
    vector<int> root;
public:
    /**
     * @param n: An integer
     * @param edges: a list of undirected edges
     * @return: true if it's a valid tree, or false
     */
    bool validTree(int n, vector<vector<int>> &edges) {
        // write your code here
        root.resize(n, -1);
        for(auto edge : edges) {
            int root1 = find(edge[0]);
            int root2 = find(edge[1]);
            if(root1 == root2) {
                return false;
            }
            
            root[root1] = root2;
        }
        return n -1 == edges.size();
    }
    
    int find(int e) {
        if(root[e] == -1) 
            return e;
        else 
            return root[e] = find(root[e]);
    }
};

477. Surrounded Regions

BFS Version:

class Solution {
public:
    /*
     * @param board: board a 2D board containing 'X' and 'O'
     * @return: nothing
     */
    void surroundedRegions(vector<vector<char>> &board) {
        // write your code here
        
        int n = board.size();
        if(n == 0) 
            return;
        int m = board[0].size();
        if(m == 0)
            return;
        
        vector<int> dirs {0, 1, 0, -1, 0};
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(i == 0 || i == n -1 || j == 0 || j == m -1) {
                    if(board[i][j] == 'O') {
                        queue<int> Q;
                        Q.push(i*m + j);
                        
                        while(!Q.empty()) {
                            int tmp = Q.front(); Q.pop();
                            int x = tmp / m;
                            int y = tmp % m;
                            board[x][y] = '#';
                            
                            for(int i = 0; i < 4; i++) {
                                int nx = x + dirs[i];
                                int ny = y + dirs[i +1];
                                if(0 <= nx && nx < n && 0 <= ny && ny < m && board[nx][ny] == 'O') {
                                    Q.push(nx * m + ny);
                                }
                            }
                        }
                    }
                }
            }
        }
        
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < m; j++) {
                if(board[i][j] == 'O')
                    board[i][j] = 'X';
                    
                if(board[i][j] == '#')
                    board[i][j] = 'O';
            }
        }
        
    }
    
};

1.4 Trie Tree

What kinds of problem could related to Trie Tree?

  1. Implementation of Trie Tree
  2. Solve the problem according to the prefix of the tree
  3. Executing the DFS in a character matrix: traversal both DFS tree and Trie Tree
1.4.1 Examples:

442. Implement Trie (Prefix Tree)

class TrieNode {
public:
    bool isString;
    TrieNode* next[26];
    TrieNode() {
        isString = false;
        for(int i = 0; i < 26; i++) {
            next[i] = nullptr;
        }
    }
};

class Trie {
    TrieNode* root;
public:
    Trie() {
        // do intialization if necessary
        root = new TrieNode();
    }

    /*
     * @param word: a word
     * @return: nothing
     */
    void insert(string &word) {
        // write your code here
        TrieNode* p = root;
        for(char ch : word) {
            if(p->next[ch - 'a'] == nullptr) {
               p->next[ch - 'a'] = new TrieNode(); 
            }
            p = p->next[ch - 'a'];
        }
        p->isString = true;
    }

    /*
     * @param word: A string
     * @return: if the word is in the trie.
     */
    bool search(string &word) {
        // write your code here
        TrieNode* p = root;
        for(auto ch : word) {
            if(p->next[ch - 'a'] == nullptr) 
                return false;
            p = p->next[ch - 'a'];
        }
        
        return p->isString;
    }

    /*
     * @param prefix: A string
     * @return: if there is any word in the trie that starts with the given prefix.
     */
    bool startsWith(string &prefix) {
        // write your code here
        TrieNode* p = root;
        for(auto ch : prefix) {
            if(p->next[ch - 'a'] == nullptr)
                return false;
            p = p->next[ch - 'a'];
        }
        return true;
    }
};

473. Add and Search Word - Data structure design

class TrieNode {
public:
    TrieNode* next[26];
    bool isEnd;
    TrieNode() {
        isEnd = false;
        for(int i = 0; i < 26; i++) {
            next[i] = nullptr;
        }
    }
};

class WordDictionary {
    TrieNode* root;
public:
    WordDictionary() {
        root = new TrieNode();
    }
    /*
     * @param word: Adds a word into the data structure.
     * @return: nothing
     */
    void addWord(string &word) {
        // write your code here
        TrieNode* p = root;
        for(auto ch : word) {
            if(p->next[ch - 'a'] == nullptr) {
                p->next[ch - 'a'] = new TrieNode();
            }
            p = p->next[ch - 'a'];
        }
        p->isEnd = true;
    }

    /*
     * @param word: A word could contain the dot character '.' to represent any one letter.
     * @return: if the word is in the data structure.
     */
    bool search(string &word) {
        // write your code here
        return search(word, word.length(), 0, root);
    }
    
    bool search(string &word, int len, int pos, TrieNode* curr) {
        if(!curr)   return false;
        if(pos == len)  return curr->isEnd;
        if(word[pos] == '.') {
            for(int i = 0; i < 26; i++) {
                if(curr->next[i] != nullptr) {
                    if(search(word, len, pos+1, curr->next[i]))
                        return true;
                }
            }
        } else {
            int index = word[pos] - 'a';
            return search(word, len, pos +1, curr->next[index]);
        }
        return false;
    }
};

132. Word Search II
This problem tagged as hard level, whereas the solution is the combination of Trie Tree and DFS. Thus, we need to write out the trie tree implement in minimum time and to figure out how to implement DFS.

In dfs part, firstly, what’s the end condition of dfs recursion? Secondly, with the help of ‘mask’ matrix to record whether current element is visited, we could avoid duplicate visiting of the same element.

struct Node {
  Node* next[26];
  string str;
  Node(): str("") {
      for(int i = 0; i < 26; i++) {
          next[i] = nullptr;
      }
  }
};

class Solution {
    Node* root;
    vector<string> result;
public:
    void insert(Node *p, string s) {
        for(auto ch : s) {
            if(!p->next[ch - 'a'])
                p->next[ch - 'a'] = new Node();
                
            p =  p->next[ch - 'a'];
        }
        p->str = s;
    }

    /**
     * @param board: A list of lists of character
     * @param words: A list of string
     * @return: A list of string
     */
    vector<string> wordSearchII(vector<vector<char>> &board, vector<string> &words) {
        // write your code here
        root = new Node();
        for(auto word : words) 
            insert(root, word);
        
        if(board.empty())
            return words;
        
        vector<vector<bool>> mask(board.size(), vector<bool>(board[0].size(), true));
        for(int i = 0; i < board.size(); i++) {
            for(int j = 0; j < board[0].size(); j++) {
                int index = board[i][j] - 'a';
                if(root->next[index] ) 
                    // dfs search
                    search(board, mask, root->next[index], i, j);
            }
        }
        return result;
    }
    
    void search(vector<vector<char>> &board, vector<vector<bool>> &mask, Node* p, int x, int y) {
    	// end condition for dfs
        if(p->str != "") {
            result.push_back(p->str);
            p->str = "";
        }
        
        mask[x][y] = false;
        vector<int> dirs{0,1,0,-1,0};
        for(int i = 0; i < 4; i++) {
            int nx = x + dirs[i];
            int ny = y + dirs[i +1];
            
            if(0 <= nx && nx < board.size() && 0 <= ny && ny < board[0].size() ) {
                int index = board[nx][ny] - 'a';
                //Don't forget to check the mask[nx][ny], or else it gonna back toward the former element
                if(p->next[index] && mask[nx][ny])
                    search(board, mask, p->next[index], nx, ny);
            }
        }
        mask[x][y] = true;
    }
    
};

634. Word Squares

class Solution {
public:
    struct TrieNode {
        vector<int> indexs;
        vector<TrieNode*> children;
        TrieNode(): children(26, nullptr) {}
    };
    TrieNode* buildTrie(vector<string>& words) {
        TrieNode *root = new TrieNode();
        for (int i = 0; i < words.size(); ++i) {
            TrieNode *t = root;
            for (int j = 0; j < words[i].size(); ++j) {
                if (!t->children[words[i][j] - 'a']) {
                    t->children[words[i][j] - 'a'] = new TrieNode();
                }
                t = t->children[words[i][j] - 'a'];
                t->indexs.push_back(i);
            }
        }
        return root;
    }
    vector<vector<string> > wordSquares(vector<string>& words) {
        TrieNode *root = buildTrie(words);
        vector<vector<string>> res;
        if(words.size() == 0){
            return res;
        }
        vector<string> out(words[0].size());
        for (string word : words) {
            out[0] = word;
            helper(words, 1, root, out, res);
        }
        return res;
    }
    void helper(vector<string>& words, int level, TrieNode* root, vector<string>& out, vector<vector<string>>& res) {
        if (level >= words[0].size()) {
            res.push_back(out);
            return;
        }
        string str = "";
        for (int i = 0; i < level; ++i) {
            str += out[i][level];
        }
        TrieNode *t = root;
        for (int i = 0; i < str.size(); ++i) {
            if (!t->children[str[i] - 'a']) return;
            t = t->children[str[i] - 'a'];
        }
        for (int idx : t->indexs) {
            out[level] = words[idx];
            helper(words, level + 1, root, out, res);
        }
    }
};

1.5 Stack (monotonous stack, reverse stack)

Operation:
O(1) Push / O(1) Pop / O(1) Top

Monotonous stack

  1. Find the left or right of each element
  2. The first element smaller / larger than itself
  3. Use monotonic stack to maintain

Easily understanding the monotonous stack, it is a stack with ascending/descending order. When a new element is not obey this rule, we do some operations(pop out the stack one by one until it obeys) to satisfy the problem.

1.4.1 Examples:

12. Min Stack
Using two stack. One is the regular stack involves push, pop, and so on. The other stack is used to keep the record of min element. That means, each time a new element was pushed, we need to store the current minimum element.

class MinStack {
public:
    stack<int> stk, minStk;
    MinStack() {
        // do intialization if necessary
    }

    /*
     * @param number: An integer
     * @return: nothing
     */
    void push(int number) {
        // write your code here
        if(minStk.empty()) {
            stk.push(number);
            minStk.push(number);
        } else {
            int tmp = minStk.top();
            if(tmp > number) {
                stk.push(number);
                minStk.push(number);
            } else {
                stk.push(number);
                minStk.push(tmp);
            }
        }
    }

    /*
     * @return: An integer
     */
    int pop() {
        // write your code here
        int tmp = stk.top();
        stk.pop();
        minStk.pop();
        return tmp;
    }

    /*
     * @return: An integer
     */
    int min() {
        // write your code here
        return minStk.top();
    }
};

40. Implement Queue by Two Stacks

class MyQueue {
public:
    stack<int> stk, stkQ;
    MyQueue() {
        // do intialization if necessary
    }

    /*
     * @param element: An integer
     * @return: nothing
     */
    void push(int element) {
        // write your code here
        stk.push(element);
    }

    /*
     * @return: An integer
     */
    int pop() {
        // write your code here
        if( stkQ.empty() ) {
            while(! stk.empty() ) {
                stkQ.push(stk.top());
                stk.pop();
            }
        }
        int tmp = stkQ.top();
        stkQ.pop();
        return tmp;
    }

    /*
     * @return: An integer
     */
    int top() {
        // write your code here
        if( stkQ.empty() ) {
            while(! stk.empty() ) {
                stkQ.push(stk.top());
                stk.pop();
            }
        }
        return stkQ.top();
    }
};

575. Decode String
Two stacks to record the information of the string. One stack is to push the int, which would the repetition number; The other is to record the content/string needed to be repeated.

class Solution {
public:
    /**
     * @param s: an expression includes numbers, letters and brackets
     * @return: a string
     */
    string expressionExpand(string &s) {
        // write your code here
        stack<string> stk_str;
        stack<int> stk_cnt;
        
        int count = 0;
        string str = "";
        
        string tmp;
        
        for(auto ch : s) {
            // 1. ch is a number
            if('0' <= ch && ch <= '9') {
                count = count * 10 + (ch - '0');
            }
            // 2. ch is '['
            else if (ch == '[') {
                stk_cnt.push(count);
                cout << count << " ";
                count = 0;
                stk_str.push(str);
                str = "";
            } 
            // 3. ch is ']'
            else if (ch == ']') {
                int cnt = stk_cnt.top(); stk_cnt.pop();
                string tmp = stk_str.top(); stk_str.pop();
                for(int i = 0; i < cnt; i++) 
                    tmp += str;
                cout << tmp << " ";
                str = tmp;
                count = 0;

            }
            // 4. ch is character fropm 'a' to 'z' 
            else {
                str += ch;
            }
        }
        return str;
    }
};

122. Largest Rectangle in Histogram

    int largestRectangleArea(vector<int> &height) {
        // write your code here
        stack<int> stk;
        if(height.empty())  return 0;
        height.push_back(0);
        
        int result = 0;
        for(int i = 0; i < height.size(); i++) {
            // if the the height[i] is bigger than the top element in stack
            if(stk.empty() || height[i] > height[stk.top()]) {
                stk.push(i);
            } 
            // if the height[i] is smaller than top element in stack
            // we pop out the stack to compare the result.
            else {
                while(!stk.empty() && height[i] < height[stk.top()]) { 
                    int idx = stk.top(); stk.pop();
                    int width = stk.empty()? i : (i - stk.top() -1);
                    result = max(result, height[idx] * width);
                }
                stk.push(i);
            }
        }
        return result;
    }

510. Maximal Rectangle
Translate the matrix into histogram, then iterate each row as a collection of height.

class Solution {
public:
    /**
     * @param matrix: a boolean 2D matrix
     * @return: an integer
     */
    int maximalRectangle(vector<vector<bool>> &matrix) {
        // write your code here
        if(matrix.empty() || matrix[0].empty()) 
            return 0;
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<int>> heights(m, vector<int>(n, 0));
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(matrix[i][j]) {
                    heights[i][j] = (i == 0)? 1 : heights[i -1][j] +1;
                }
            }
        }
        
        int result = 0;
        for(int i = 0; i < m; i++) {
            result = max(result, getMaxArea(heights[i]));
        }
        
        return result;
    }
    
    int getMaxArea(vector<int> &heights) {
        stack<int> stk;
        int res = 0;
        // used to do the final check out
        heights.push_back(0);
        for(int i = 0; i < heights.size(); i++) {
            if(stk.empty() || heights[i] > heights[stk.top()] ) {
                stk.push(i);
            }
            else {
                while(!stk.empty() && heights[i] < heights[stk.top()]) {
                    int idx = stk.top(); stk.pop();
                    int width = stk.empty()? i : (i - stk.top() -1);
                    res = max(res, heights[idx] * width);
                }
                stk.push(i);
            }
        }
        return res;
    }
};

126. Max Tree

class Solution {
public:
    vector<TreeNode*> stk;
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    TreeNode * maxTree(vector<int> &A) {
        // write your code here
        for(auto num : A) {
            TreeNode* tmp = new TreeNode(num);
            if(!stk.empty() && num > stk[stk.size() -1] ->val) {
                while(!stk.empty() && num > stk[stk.size() -1] ->val){
                    tmp->left = stk[stk.size() -1];
                    stk.pop_back();
                }
                
            }
            
            if(!stk.empty() && num < stk[stk.size() -1] ->val) {
                stk[stk.size() -1] ->right = tmp;
            }
            stk.push_back(tmp);
        }
        return stk[0];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Keras.layers.advanced_activations是Keras深度学习框架中的高级激活函数模块。它包含了一些常用的激活函数,如ReLU、LeakyReLU、PReLU、ELU、ThresholdedReLU等,这些激活函数可以用于神经网络的隐藏层和输出层,帮助提高模型的性能和准确率。此外,该模块还提供了一些高级激活函数,如SReLU、Swish等,这些激活函数可以进一步提高模型的性能和泛化能力。 ### 回答2: Keras是一个高层次的神经网络API,它提供了许多构建神经网络所需的函数和对象,并且使得构建和训练深度神经网络更加容易。在Keras中,有一部分是用于激活函数的高级激活层,称为keras.layers.advanced_activations。 keras.layers.advanced_activations包含了多种高级激活函数,比如PReLU,LeakyReLU和ELU等。这些激活函数相较于传统的sigmoid和tanh激活函数来说,具有更好的非线性表达能力,可以使得神经网络更有效地学习特征。PReLU是一种带有参数的修正线性单元,它在某些情况下可以提高模型的性能并减少过拟合。LeakyReLU是一种有助于避免梯度消失问题的激活函数,而ELU则可以使得神经网络的输出具有更好的稳定性和鲁棒性。此外,这些高级激活函数还可以帮助加速训练过程,提高模型的泛化能力和预测精度。 总之,keras.layers.advanced_activations提供了多种高级激活函数,可以在深度神经网络中发挥重要作用。通过选择适当的激活函数,可以提高模型的表现,加快训练速度并减少过拟合现象的发生。因此,深度学习爱好者和从事深度学习相关工作的研究人员都应该掌握这些高级激活函数的用法和特性。 ### 回答3: Keras.layers.advanced_activations是Keras中的一个高级激活函数模块,它提供了一系列非线性激活函数,这些激活函数有助于提高深度学习模型的性能,使其更快地收敛。具体包括: 1. LeakyReLU LeakyReLU是ReLU的变体,在ReLU函数的基础上添加了一个小的负值斜率。这样做的目的是解决ReLU函数在负数区域的不足,LeakyReLU能够在负数区域有一定输出,从而解决了负区间出现的“神经元死亡”问题。 2. PReLU PReLU是ParametricReLU的缩写,是一个具有可学习参数的变体,与LeakyReLU结合起来使用可以在一定程度上提高模型的性能。 3. ELU ELU是Exponential Linear Units(指数线性单元),是在ReLU函数基础上设计出的,应用在卷积神经网络(CNN)之中,使得生成的特征图(feature map)的稀疏比率较低,提高了低级模型的训练速度,而且模型的测试泛化误差也比ReLU方法输出更低。 4. ThresholdedReLU ThresholdedReLU是基于ReLU的剪辑变体,将小于阈值的值设为0,而大于该阈值的值则保持不变。通过这种方式,ThresholdedReLU可用于减少模型中的线性输出,使其更加非线性。 总的来说,Keras.layers.advanced_activations中包含的高级激活函数可以增强神经网络的非线性表示能力,达到优化模型效果的目的。使用这些激活函数,可以在不增加神经元的情况下增加模型的复杂度,提高难度,在保证了高精度的前提下更加高效的解决问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值