[英]Determining the Big O time complexity for this program?
我正在分析我寫給 Leetcode 問題的解決方案的時間復雜度,二叉樹的垂直順序遍歷
該問題提供了一個二叉樹並要求返回一個列表列表,其中每個列表對應於樹中的垂直列,並且每個列表從上到下排序,同一行和列中的值按數字排序。
我的代碼如下所示,但總結一下,我使用兩個隊列逐級執行廣度優先搜索。 我同樣使用兩張地圖。 一個 map 保存整個表的列和該列中的值之間的關系,其他映射具有類似的目的,但它一次只保存一個級別的值。 這使我們能夠對每個級別內的潛在聯系進行排序,然后將其值添加到上述 map。 在程序結束時,包含所有級別信息的 map 將轉換為 Leetcode 分級器的列表列表。
我無法分析排序邏輯的時間復雜度。 最壞的情況是在完全二叉樹中排序最多的情況。 到目前為止,這是我一直在想的:
對於這個完整的二叉樹,節點已經用它們各自的列進行了標記,在頂部,我顯示了每一列在每個級別的頻率。 我在圖像上有這些數據,因為 Stack Overflow 在提交這篇文章后沒有正確呈現表格。
列值的大小並不是特別重要,但出現的頻率模式是帕斯卡三角/二項式系數展開的模式。 事后看來,從右/左子節點的父節點列中添加/減去 1 是有道理的。 列號的頻率表示必須排序的該級別的列表大小(瓶頸)。
鑒於此,我可以重復
遞歸中的 L 指的是二叉樹中的層級。 每個級別的總工作量是之前級別完成的工作量,加上在該級別對具有可變大小的列表進行排序的工作(大小相對於二項式展開系數是可變的)。 從這里開始,我不確定 go 在哪里。 L,或 logN,表示樹中的 N 個元素。
直覺使我相信這種重復產生以下表達式
但這仍然不是很清楚,我也不確定它是否正確(就像我說的,直覺---不是證明---導致我這樣做)。 這看起來對嗎? 有沒有辦法簡化我還沒有看到的?
我在下面附上了實現此功能的 Java 代碼。 謝謝,如果您有任何問題,請告訴我。
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*
*/
class Solution {
/*
* Map<TreeNode, Integer> to keep track of column for each treenode
* Map<Integer, List<Integer>> to keep track of columns and the values in the column
* Map<Integer, List<Integer>> to keep track of columns and the values in the column
FOR EACH ROW
* Breadth first search
* - as adding to queue, add to column map
* - use a 2 queue approach for each level in breadth first search
* - when each level of BFS is finished, sort third map, then add all to second map
and clear it
*/
public List<List<Integer>> verticalTraversal(TreeNode root) {
Map<TreeNode, Integer> columns = new HashMap<>();
final Map<Integer, List<Integer>> table = new HashMap<>();
Map<Integer, List<Integer>> levelTable;
Queue<TreeNode> bfsQueue;
Queue<TreeNode> nextLevel = new ArrayDeque<>();
int minColOverall = Integer.MAX_VALUE;
nextLevel.offer(root);
columns.put(root, 0);
while(!nextLevel.isEmpty()){
bfsQueue = nextLevel;
nextLevel = new ArrayDeque<>();
levelTable = new HashMap<>();
int minCol = Integer.MAX_VALUE;
while(!bfsQueue.isEmpty()){ // Evaluate each level individually
TreeNode node = bfsQueue.poll();
int column = columns.remove(node);
minCol = Math.min(minCol,column);
if(!levelTable.containsKey(column))
levelTable.put(column, new ArrayList<Integer>());
levelTable.get(column).add(node.val);
if(node.left != null){
nextLevel.offer(node.left);
columns.put(node.left, column-1);
}
if(node.right != null){
nextLevel.offer(node.right);
columns.put(node.right, column+1);
}
}
minColOverall = Math.min(minCol, minColOverall);
for(int m = minCol; !levelTable.isEmpty(); m+=2){ // After level, add sorted col vals to final table
if(!levelTable.containsKey(m))
continue;
List<Integer> level = levelTable.remove(m);
Collections.sort(level);
if(!table.containsKey(m))
table.put(m, new ArrayList<Integer>());
table.get(m).addAll(level);
}
}
// Transform map to list
final List<List<Integer>> answer = new ArrayList<>();
while(!table.isEmpty())
answer.add(table.remove(minColOverall++));
return answer;
}
}```
我相信我已經想出了一個上限的解釋。
時間復雜度是遍歷的時間復雜度加上排序操作的時間復雜度。 排序的時間復雜度將支配遍歷的時間復雜度,所以這就是我要關注的內容。
這種解釋的關鍵是,盡管每個級別的列大小的大小相對於二項式系數展開是可變的,但如果使用平均輸入大小代替,則可以忽略這一點。
設時間復雜度為:
O(總排序數 * 平均排序復雜度)
我們對每一行中的每一列進行排序,因此排序的總數是該行中所有行的列數的總和。 在由 N 個節點組成的二叉樹中有 O(log N) 行。 如上圖所示,每行中唯一列的數量以 1 線性增長。第一行有 1 個唯一列,第二行有 2 個唯一列,第三行有 3 個唯一列……最后一行,行編號 logN,將有 logN 唯一列。 這是從 1 到 logN 的整數的簡單線性和,等於 logN(1+logN)/2,即 O((logN)^2)
要排序的輸入的總大小等於 N,因為每個節點只屬於一列。 這意味着平均排序大小為 O(N / (logN)^2)。 令 M 的值等於 N / (logN)^2。
這意味着總時間復雜度
O(總排序數 * 平均排序復雜度) =
O((logN)^2 * M log M) =
O((logN)^2 * (N / (logN)^2) log [N / (logN)^2]) =
O(N log [N / (logN)^2])
這個界限比 O(N log N) 更嚴格,但 O(N log N) 在技術上仍然是正確的。
這是非常好的。 這是一個有趣的問題。
我只想指出,您可以使用已排序的數據結構進行更嚴格的編碼。 當然,樹圖要慢一些。
class Solution {
private final TreeMap<Integer, TreeMap<Integer, List<Integer>>> accumulator =
new TreeMap<>();
private void add(int val, int i, int j) {
accumulator.computeIfAbsent(j, k -> new TreeMap<>())
.computeIfAbsent(i, k -> new ArrayList<>())
.add(val);
}
private void search(TreeNode node, int i, int j) {
if (node == null) return;
add(node.val, i, j);
search(node.left, i + 1, j - 1);
search(node.right, i + 1, j + 1);
}
public List<List<Integer>> verticalTraversal(TreeNode root) {
search(root, 0, 0);
return accumulator.values().stream().map(colMap -> {
colMap.values().forEach(Collections::sort);
return colMap.values().stream().flatMap(List::stream).collect(toList());
}).collect(toList());
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.