並查集是這樣一種數據機構:
A, B, C三我的在Microsoft工做, 給他們三我的每一個人都安個指針指向Microsoft. 這個指針相對於你工做單位的腰牌,表明你所屬的陣營, 說明你是這個陣營的小弟.
D, E, F, J四我的在Linkedin工做, 給這四我的每一個人都安個腰牌指針指向大哥Linkedin.
java
這個指針腰牌有神馬用呢? 就是假設A在飯桌上遇到了F, A一看對放腰牌是L公司, F一看對方腰牌是M公司, 就都知道對方是否是本身一家公司的. 又來我的D, D的腰牌是L公司, 這下F和D也當即就知道他倆是同事.
node
這時候, 發生了一件事兒, 就是M公司Microsoft把L公司Linkedin收購了. 可是Linkedin也不小, 底下一撮子的小弟, 一個個改腰牌好麻煩. 而後公司想了個辦法, D, E, F, J的大哥不變, 依然由Linkedin擔當, 可是Linkedin要百Microsoft當大哥. 這樣飯桌上即便聊起來, A能夠看下E的腰牌, 噢, 你大哥是L啊, 是我大哥M手下L管的小弟, 咱倆仍是一個陣營噠~
c++
這裏, 咱們能夠不用M和L這個公司名字, 直接在公司的全部員工裏, 選個權利最大的作表明, 好比M公司的A, B, C我就選了B (Bill Gates), L公司呢選J, 你們的大哥就分別是這兩我的:
面試
這時候呢, 若是公司之間合併收購, 就讓J拜B當大哥, 你們就是一夥兒的啦
express
初始化後每個元素的父親節點是它自己,即本身是本身的大哥(也能夠根據狀況而變), 舉個栗子:數組
private int[] father = null; public Initialization(int n) { // initialize your data structure here. father = new int[n + 1]; for (int i = 1; i <= n; ++i) father[i] = i; }
查找一個元素所在的集合,其精髓是找到這個元素的大哥!這個纔是並查集判斷和合並的最終依據。
判斷兩個元素是否屬於同一集合,只要看他們是否是最終都歸一個大哥管, 模板以下:app
private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); }
合併兩個不相交集合就是讓一夥的老大哥拜另外一夥兒的老大哥爲大哥, 模板爲:ide
public 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; }
無路徑壓縮是這樣的:wordpress
private int find(int x) { if (father[x] == x) { return x; } return find(father[x]); }
有路徑壓縮是函數
private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); }
其中路徑壓縮是這樣的原理:
假設A->B->Z->Y->W這樣的路徑, 咱們想查詢A的老大哥, 須要從A走過B, Z, Y到W, 途中通過5個點, B查詢通過4個點… 每次這樣好麻煩:
因而咱們在遞歸回溯的過程當中, 把每層的father大哥都變成最大的大哥:
而後咱們就由鏈條型多步找大哥變爲衆星捧月型一步找大哥:
其中:
private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); }
的時間複雜度第一次是O(n), 可是屢次下來是log*n (見https://en.wikipedia.org/wiki/Iterated_logarithm), 證實略
由於1-5都很小, 因此find基本上是O(1)的操做.
若是find是O(1), 那麼union也是O(1)時間複雜度的操做.
Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning.
You need to support the following method:
connect(a, b), add an edge to connect node a and node b`.
query(a, b), check if two nodes are connected.
這道題很容易直接想到bfs, bfs的遍歷, 時間複雜度是O(kn), 其中k是操做次數.
若是用並查集的方法, 時間複雜度每次操做都是O(1), 因此總共是O(k).
public class ConnectingGraph { private int[] father = null; private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); } public ConnectingGraph(int n) { // initialize your data structure here. father = new int[n + 1]; for (int i = 1; i <= n; ++i) father[i] = i; } public 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; } public boolean query(int a, int b) { // Write your code here int root_a = find(a); int root_b = find(b); return root_a == root_b; } }
Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning.
You need to support the following method:
connect(a, b), an edge to connect node a and node b
query(a), Returns the number of connected component nodes which include node a.
public class ConnectingGraph2 { private int[] father = null; private int[] size = null; private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); } public ConnectingGraph2(int n) { // initialize your data structure here. father = new int[n + 1]; size = new int[n + 1]; for (int i = 1; i <= n; ++i) { father[i] = i; size[i] = 1; } } public 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; size[root_b] += size[root_a]; } } public int query(int a) { // Write your code here int root_a = find(a); return size[root_a]; } }
Given a boolean 2D matrix, 0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent.
Find the number of islands.
這道題, 用bfs或者dfs的時候, k個操做, 時間複雜度是O(kmn), 其中m是行數, n是列數.
可是其實每次操做, 咱們並不須要遍歷整張圖, 只須要在每一個點附近的局部作處理就行, 這種bfs/dfs下的局部處理, 能夠想到並查集. 須要作的的事是: 把其餘的操做,轉化成查找和合並這兩件事情.
值得注意的是, 二位矩陣轉一維有個公式:
二位矩陣中的(x, y)某點若是想變爲一維矩陣中的某個位置, id轉化公式以下, 其中m爲二維矩陣的列數:
(x, y) ---> ID : ID = x * m + y ID ----> (x, y): x = ID / m y = ID % m
這道題的題解:
class UnionFind { private int[] father = null; private int count; private int find(int x) { if (father[x] == x) { return x; } return father[x] = find(father[x]); } public UnionFind(int n) { // initialize your data structure here. father = new int[n]; for (int i = 0; i < n; ++i) { father[i] = i; } } public 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; count --; } } public int query() { // Write your code here return count; } public void set_count(int total) { count = total; } } public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ public int numIslands(boolean[][] grid) { int count = 0; int n = grid.length; if (n == 0) return 0; int m = grid[0].length; if (m == 0) return 0; UnionFind union_find = new UnionFind(n * m); int total = 0; for(int i = 0;i < grid.length; ++i) for(int j = 0;j < grid[0].length; ++j) if (grid[i][j]) total ++; union_find.set_count(total); for(int i = 0;i < grid.length; ++i) for(int j = 0;j < grid[0].length; ++j) { if (grid[i][j]) { if (i > 0 && grid[i - 1][j]) { union_find.connect(i * m + j, (i - 1) * m + j); } if (i 0 && grid[i][j - 1]) { union_find.connect(i * m + j, i * m + j - 1); } if (j < m - 1 && grid[i][j + 1]) { union_find.connect(i * m + j, i * m + j + 1); } } return union_find.query(); } }
此道題相似於把大象裝冰箱, 一共分三步:
C++的implementation:
class TrieNode { // Initialize your data structure here. char c; HashMap children = new HashMap(); boolean hasWord; public TrieNode(){ } public TrieNode(char c){ this.c = c; } } public class Trie { private TrieNode root; public Trie() { root = new TrieNode(); } // Inserts a word into the trie. public void insert(String word) { TrieNode cur = root; HashMap curChildren = root.children; char[] wordArray = word.toCharArray(); for(int i = 0; i < wordArray.length; i++){ char wc = wordArray[i]; if(curChildren.containsKey(wc)){ cur = curChildren.get(wc); } else { TrieNode newNode = new TrieNode(wc); curChildren.put(wc, newNode); cur = newNode; } curChildren = cur.children; if(i == wordArray.length - 1){ cur.hasWord= true; } } } // Returns if the word is in the trie. public boolean search(String word) { if(searchWordNodePos(word) == null){ return false; } else if(searchWordNodePos(word).hasWord) return true; else return false; } // Returns if there is any word in the trie // that starts with the given prefix. public boolean startsWith(String prefix) { if(searchWordNodePos(prefix) == null){ return false; } else return true; } public TrieNode searchWordNodePos(String s){ HashMap children = root.children; TrieNode cur = null; char[] sArray = s.toCharArray(); for(int i = 0; i < sArray.length; i++){ char c = sArray[i]; if(children.containsKey(c)){ cur = children.get(c); children = cur.children; } else{ return null; } } return cur; } }
O(1)不是表明一個字符, O(1)表明整個字符串
Hash Trie 構建 O(n) O(n) 查詢 O(1) O(1)
對於 a, aa, aaa, aaaa的狀況
Hash Trie 存儲 10個a 5個a節點 可用操做 有/無查詢 有/無/前綴查詢 代碼量 1行 75~100行
因此選擇hash緣由是代碼量小, 可是涉及到前綴查詢的時候, 考慮trie樹
Design a data structure that supports the following two operations: addWord(word) and search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z or …
A . means it can represent any one letter.
若是用dfs在矩陣裏面一個一個字符遍歷, 時間複雜度至少爲O(mn * mn).
dfs+trie樹就只須要遍歷在trie樹裏面有前綴的, 因此會快
注意不能用.做爲trie樹的第27叉.
class TrieNode { public TrieNode[] children; public boolean hasWord; public TrieNode() { children = new TrieNode[26]; for (int i = 0; i < 26; ++i) children[i] = null; hasWord = false; } } public class WordDictionary { private TrieNode root; public WordDictionary(){ root = new TrieNode(); } // Adds a word into the data structure. public void addWord(String word) { // Write your code here TrieNode now = root; for(int i = 0; i < word.length(); i++) { Character c = word.charAt(i); if (now.children[c - 'a'] == null) { now.children[c - 'a'] = new TrieNode(); } now = now.children[c - 'a']; } now.hasWord = true; } boolean find(String word, int index, TrieNode now) { if(index == word.length()) { return now.hasWord; } Character c = word.charAt(index); if (c == '.') { for(int i = 0; i < 26; ++i) if (now.children[i] != null) { if (find(word, index+1, now.children[i])) return true; } return false; } else if (now.children[c - 'a'] != null) { return find(word, index+1, now.children[c - 'a']); } else { return false; } } // Returns if the word is in the data structure. A word could // contain the dot character '.' to represent any one letter. public boolean search(String word) { // Write your code here return find(word, 0, root); } }
c++的實現:
Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position.
三步走:
注意:
a-z
.能夠問面試官的clarify的問題:
public class Solution { /** * @param board: A list of lists of character * @param words: A list of string * @return: A list of string */ class TrieNode { String s; boolean isString; HashMap subtree; public TrieNode() { // TODO Auto-generated constructor stub isString = false; subtree = new HashMap(); s = ""; } }; class TrieTree{ TrieNode root ; public TrieTree(TrieNode TrieNode) { root = TrieNode; } public void insert(String s) { TrieNode now = root; for (int i = 0; i < s.length(); i++) { if (!now.subtree.containsKey(s.charAt(i))) { now.subtree.put(s.charAt(i), new TrieNode()); } now = now.subtree.get(s.charAt(i)); } now.s = s; now.isString = true; } public boolean find(String s){ TrieNode now = root; for (int i = 0; i < s.length(); i++) { if (!now.subtree.containsKey(s.charAt(i))) { return false; } now = now.subtree.get(s.charAt(i)); } return now.isString ; } }; public int []dx = {1, 0, -1, 0}; public int []dy = {0, 1, 0, -1}; public void search(char[][] board, int x, int y, TrieNode root, List ans) { if(root.isString == true) { // -------這行很重要 由於每一個結束都是靠它下面一層遞歸加string進結果 // 而它下面的遞歸可能進去不少次,而且dad這種palindrome原本也會重複----------------------- if(!ans.contains(root.s)){ ans.add(root.s); } } if(x = board.length || y = board[0].length || board[x][y]==0 || root == null) return ; if(root.subtree.containsKey(board[x][y])){ for(int i = 0; i < 4; i++){ char now = board[x][y]; board[x][y] = 0; search(board, x+dx[i], y+dy[i], root.subtree.get(now), ans); board[x][y] = now; } } } public List wordSearchII(char[][] board, List words) { List ans = new ArrayList(); TrieTree tree = new TrieTree(new TrieNode()); for(String word : words){ tree.insert(word); } for(int i = 0; i < board.length; i++){ for(int j = 0; j < board[i].length; j++){ search(board, i, j, tree.root, ans); } } return ans; // write your code here } }
Given a set of words without duplicates, find all word squares you can build from them.
A sequence of words forms a valid word square if the kth row and column read the exact same string, where 0 ≤ k < max(numRows, numColumns).
For example, the word sequence [「ball」,「area」,「lead」,「lady」] forms a word square because each word reads the same both horizontally and vertically.
b a l l a r e a l e a d l a d y
假設單詞平均長度爲m, 單詞個數爲n, 用簡單的dfs咱們須要的時間複雜度是A(m*n), A表明排列.可是不要氣餒, 寫程序不是一步登天, 咱們先想蠢辦法, 而後一步一步優化. 楊過的路子很差走,我們學郭靖, 一點點來.
由於有前綴相關的, 因此想到trie樹, 總共主要有兩步驟:
public class Solution { class TrieNode { List startWith; TrieNode[] children; TrieNode() { startWith = new ArrayList(); children = new TrieNode[26]; } } class Trie { TrieNode root; Trie(String[] words) { root = new TrieNode(); for (String w : words) { TrieNode cur = root; for (char ch : w.toCharArray()) { int idx = ch - 'a'; if (cur.children[idx] == null) cur.children[idx] = new TrieNode(); cur.children[idx].startWith.add(w); cur = cur.children[idx]; } } } List findByPrefix(String prefix) { List ans = new ArrayList(); TrieNode cur = root; for (char ch : prefix.toCharArray()) { int idx = ch - 'a'; if (cur.children[idx] == null) return ans; cur = cur.children[idx]; } ans.addAll(cur.startWith); return ans; } } public List wordSquares(String[] words) { List ans = new ArrayList(); if (words == null || words.length == 0) return ans; int len = words[0].length(); Trie trie = new Trie(words); List ansBuilder = new ArrayList(); for (String w : words) { ansBuilder.add(w); search(len, trie, ans, ansBuilder); ansBuilder.remove(ansBuilder.size() - 1); } return ans; } private void search(int len, Trie tr, List ans, List ansBuilder) { if (ansBuilder.size() == len) { ans.add(new ArrayList(ansBuilder)); return; } int idx = ansBuilder.size(); StringBuilder prefixBuilder = new StringBuilder(); for (String s : ansBuilder) prefixBuilder.append(s.charAt(idx)); List startWith = tr.findByPrefix(prefixBuilder.toString()); for (String sw : startWith) { ansBuilder.add(sw); search(len, tr, ans, ansBuilder); ansBuilder.remove(ansBuilder.size() - 1); } } }
這個在搜索type ahead上面常用到.
線段樹是一個二叉樹結構, 它能作全部heap能作的操做,而且能夠logn時間查找到某個區間的最大和最小值
Heap Segment Tree push O(logn) O(logn) pop O(logn) O(logn) top O(1) O(1) modify X O(logn)
1. 二叉樹 2. Parent 區間被平分 Leftson = 左區間 Rightson = 右區間 葉子節點不可分 3. 每一個節點表示區間(葉子節點表明一個元素)
For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end).
Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree.
Notice
It is much easier to understand this problem if you finished Segment Tree Build first.
Example
For array [1, 4, 2, 3], the corresponding Segment Tree is:
[0, 3, max=4] / \ [0,1,max=4] [2,3,max=3] / \ / \ [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3]
query(root, 1, 1), return 4
query(root, 1, 2), return 4
query(root, 2, 3), return 3
query(root, 0, 2), return 4
節點區間和要查找區間的關係分四種狀況: 1.節點區間包含查找區間—>查找區間遞歸向下 2.節點區間不相交於查找區間 ->查找區間中止搜索 3.節點區間相交不包含於查找區間->查找區間分裂成兩段區間,一段於被節點區間包含,另外一段不相交 4. 節點區間相等於查找區間—> 返回值查找的結果
public class Solution { /** *@param root, start, end: The root of segment tree and * an segment / interval *@return: The maximum number in the interval [start, end] */ public int query(SegmentTreeNode root, int start, int end) { // write your code here if(start == root.start && root.end == end) { // 相等 return root.max; } int mid = (root.start + root.end)/2; int leftmax = Integer.MIN_VALUE, rightmax = Integer.MIN_VALUE; // 左子區 if(start <= mid) { if( mid < end) { // 分裂 leftmax = query(root.left, start, mid); } else { // 包含 leftmax = query(root.left, start, end); } // leftmax = query(root.left, start, Math.min(mid,end)); } // 右子區 if(mid < end) { // 分裂 3 if(start <= mid) { rightmax = query(root.right, mid+1, end); } else { // 包含 rightmax = query(root.right, start, end); } //rightmax = query(root.right, Math.max(mid+1,start), end); } // else 就是不相交 return Math.max(leftmax, rightmax); } }
The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval.
start and end are both integers, they should be assigned in following rules:
The root’s start and end is given by build method.
The left child of node A has start=A.left, end=(A.left + A.right) / 2.
The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right.
if start equals to end, there will be no children for this node.
Implement a build method with two parameters start and end, so that we can create a corresponding segment tree with every node has the correct start and end value, return the root of this segment tree.
Clarification
Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like:
which of these intervals contain a given point
which of these points are in a given interval
See wiki:
Segment Tree
Interval Tree
Example
Given start=0, end=3. The segment tree will be:
[0, 3] / \ [0, 1] [2, 3] / \ / \ [0, 0] [1, 1] [2, 2] [3, 3]
Given start=1, end=6. The segment tree will be:
[1, 6] / \ [1, 3] [4, 6] / \ / \ [1, 2] [3,3] [4, 5] [6,6] / \ / \ [1,1] [2,2] [4,4] [5,5]
自上而下遞歸分裂
自下而上回溯更新
一共logn層, 每層的元素個數是分別是
[1, 2, 4, 8…n]
O = 1+2+4+...+n = (2^(logn+1) -1)/ (1 - 2) = 2n = n
public class Solution { /** *@param start, end: Denote an segment / interval *@return: The root of Segment Tree */ public SegmentTreeNode build(int start, int end) { // write your code here if(start > end) { // check core case return null; } SegmentTreeNode root = new SegmentTreeNode(start, end); if(start != end) { int mid = (start + end) / 2; root.left = build(start, mid); root.right = build(mid+1, end); // root.max = Math.max(root.left.max, root.right.max); } return root; } }
The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval.
start and end are both integers, they should be assigned in following rules:
The root’s start and end is given by build method.
The left child of node A has start=A.left, end=(A.left + A.right) / 2.
The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right.
if start equals to end, there will be no children for this node.
Implement a build method with a given array, so that we can create a corresponding segment tree with every node value represent the corresponding interval max value in the array, return the root of this segment tree.
Clarification
Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like:
which of these intervals contain a given point
which of these points are in a given interval
See wiki:
Segment Tree
Interval Tree
Example
Given [3,2,1,4]. The segment tree will be:
[0, 3] (max = 4) / \ [0, 1] (max = 3) [2, 3] (max = 4) / \ / \ [0, 0](max = 3) [1, 1](max = 2) [2, 2](max = 1) [3, 3] (max = 4)
public class Solution { /** *@param A: a list of integer *@return: The root of Segment Tree */ public SegmentTreeNode build(int[] A) { // write your code here return buildTree(0, A.length - 1, A); } public SegmentTreeNode buildTree(int start, int end, int[] A) { if (start > end) return null; if (start == end) { return new SegmentTreeNode(start, end, A[start]); } SegmentTreeNode node = new SegmentTreeNode(start, end, A[start]); int mid = (start + end) / 2; node.left = this.buildTree(start, mid, A); node.right = this.buildTree(mid + 1, end, A); if (node.left != null && node.left.max > node.max) node.max = node.left.max; if (node.right != null && node.right.max > node.max) node.max = node.right.max; return node; } }
c++ implementation:
/** * Definition of SegmentTreeNode: * class SegmentTreeNode { * public: * int start, end, max; * SegmentTreeNode *left, *right; * SegmentTreeNode(int start, int end, int max) { * this->start = start; * this->end = end; * this->max = max; * this->left = this->right = NULL; * } * } */ class Solution { public: /** *@param A: a list of integer *@return: The root of Segment Tree */ SegmentTreeNode * build(vector<int>& A) { // write your code here return buildTree(0, A.size()-1, A); } SegmentTreeNode * buildTree(int start, int end, vector<int>& A) { if (start > end) return NULL; SegmentTreeNode * node = new SegmentTreeNode(start, end, A[start]); if (start == end) return node; int mid = (start + end) / 2; node->left = buildTree(start, mid, A); node->right = buildTree(mid+1, end, A); if (node->left && node->left->max > node->max) node->max = node->left->max; if (node->right && node->right->max > node->max) node->max = node->right->max; return node; } };
For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in this node’s interval.
Implement a modify function with three parameter root, index and value to change the node’s value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value.
Notice
We suggest you finish problem Segment Tree Build and Segment Tree Query first.
Have you met this question in a real interview? Yes
Example
For segment tree:
[1, 4, max=3] / \ [1, 2, max=2] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3]
if call modify(root, 2, 4), we can get:
[1, 4, max=4] / \ [1, 2, max=4] [3, 4, max=3] / \ / \ [1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3]
or call modify(root, 4, 0), we can get:
[1, 4, max=2] / \ [1, 2, max=2] [3, 4, max=0] / \ / \ [1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0]
1. heap沒法對元素進行修改;hash heap和平衡二叉樹能夠 2. modify最多查找logn層,時間複雜度是logn 3. 口訣:自上而下遞歸查找 自下而上回溯更新 4. 以數組下標來創建線段樹
public class Solution { /** *@param root, index, value: The root of segment tree and *@ change the node's value with [index, index] to the new given value *@return: void */ public void modify(SegmentTreeNode root, int index, int value) { // write your code here if(root.start == index && root.end == index) { // 查找到 root.max = value; return; } // 查詢 int mid = (root.start + root.end) / 2; if(root.start <= index && index <=mid) { modify(root.left, index, value); } if(mid < index && index <= root.end) { modify(root.right, index, value); } //更新 root.max = Math.max(root.left.max, root.right.max); } }
Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the minimum number between index start and end in the given array, return the result list.
Notice
We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first.
Have you met this question in a real interview? Yes
Example
For array [1,2,7,8,5], and queries [(1,2),(0,4),(2,4)], return [2,1,5]
class SegmentTreeNode { public int start, end, min; public SegmentTreeNode left, right; public SegmentTreeNode(int start, int end, int min) { this.start = start; this.end = end; this.min = min; this.left = this.right = null; } } public class Solution { /** *@param A, queries: Given an integer array and an query list *@return: The result list */ public SegmentTreeNode build(int start, int end, int[] A) { // write your code here if(start > end) { // check core case return null; } SegmentTreeNode root = new SegmentTreeNode(start, end, Integer.MAX_VALUE); if(start != end) { int mid = (start + end) / 2; root.left = build(start, mid, A); root.right = build(mid+1, end, A); root.min = Math.min(root.left.min, root.right.min); } else { root.min = A[start]; } return root; } public int query(SegmentTreeNode root, int start, int end) { // write your code here if(start == root.start && root.end == end) { // 相等 return root.min; } int mid = (root.start + root.end)/2; int leftmin = Integer.MAX_VALUE, rightmin = Integer.MAX_VALUE; // 左子區 if(start <= mid) { if( mid < end) { // 分裂 leftmin = query(root.left, start, mid); } else { // 包含 leftmin = query(root.left, start, end); } } // 右子區 if(mid < end) { // 分裂 3 if(start <= mid) { rightmin = query(root.right, mid+1, end); } else { // 包含 rightmin = query(root.right, start, end); } } // else 就是不相交 return Math.min(leftmin, rightmin); } public ArrayList<Integer> intervalMinNumber(int[] A, ArrayList<Interval> queries) { // write your code here SegmentTreeNode root = build(0, A.length - 1, A); ArrayList ans = new ArrayList<Integer>(); for(Interval in : queries) { ans.add(query(root, in.start, in.end)); } return ans; } }
1.Query O(log(n)) 2.Build O(n) 3.Modify O(log(n))
1. Sum 2. Maximum/ Minimum 3. Count
1. 下標做爲創建區間 2. 值做爲創建區間