2015-12-14 7 views
5

ツリートラバーサルスパイラル:考えるが内側に私が興味深い問題に出くわした

がバイナリツリーは、最初の印刷レベル1すなわち内側にらせん状のためにそれを印刷し、それから、それから、nは1からn-ので、レベル2レベルに。 Iが溶液考えた

For Ex: 
1 
2 3 
4 5 6 7 
8 9 10 11 12 13 14 15 

Should Output: 
1 15 14 13 12 11 10 9 8 2 3 7 6 5 4 

  • ストアリスト内の各レベル要素
    リスト[0] = [1]
    リスト[1] = [2,1 3]
    リスト[2] = [4、5、6、7]
    リスト[3] = [8、9、10、11、12、13、14、15]

  • 必要な順番(0、n-1、1、n-2、...)でリストのリストをループして印刷します。ここではnは上記の場合のレベル数です。

その空間の複雑さはO(n)です。私はそこにはより良い空間の複雑さとより良い解決策があるかもしれないと確信していますが、私はそれを考えることはできません。誰もがポインタを持っていますか?

+0

アイテムを保存する場合は、O(1)ルックアップのパフォーマンスを与えるhashMapを使用することもできます。 –

+0

@MichaelQueueこの場合、HashMapは違いはありません。リストの配列でさえ、各リストのO(1)ルックアップを得るでしょう。 – Atri

+0

こんにちはアトリ、時間があれば、受け入れられる答えがどのように空間O(1)であるか説明してください。 「プリントノード」は、解決策をプリント出力に適合させるためにO(n)スペースが必要であることを意味しないか?ありがとうございました。 –

答えて

1

はここnは、ノードの数であるとhは、木の高さであるO(1)スペースとO(n * h)時間、とシンプルなソリューションです。 2つの変数を現在のレベルを指し示したままにしておき、適切な木の深さに対応する長さのバイナリ文字列に従ってツリーを走査した後に各ノードを出力する。 "0"の場合は右に、 "1"の場合は右に移動します(たとえば、一番左のノードには "000"、その隣には "001")。 1つのノードを出力するためには、最大でhの反復を行う必要があります。時間の複雑さはO(n * h)です。ここで

はもう少し詳細な言葉でアルゴリズムです:

down = 0 
up = tree height 

while: 
    if down > up: 
    break 
    else: 
    set exp to down then up (or only one if up == down): 
     for i = 0 to 2^exp - 1: 
     s = binary string i, padded to exp digits 
     traverse tree according to s 
     if a node exists by the end of s: 
      print node 
    down = down + 1 
    up = up - 1 

私はそれが速く大きさの順になるかどうかわからないんだけど、あなたはツリー内の同じ原理歩行を使用することができ、よりもむしろ"0"になるまでバックアップし、 "1"に反転してから目標レベルまで下げたままにし、文字列がすべて "1"になるまで再帰的に繰り返します。例えば、目標レベル4:

0000 => (000)1 => (00)10 => (001)1 => (0)100...etc. 
+0

あなたの答えをありがとう。これは非常に明確で理解しやすい説明です。 – Atri

+0

@Atriありがとうございます!それが助けてくれてうれしい。 –

+0

マイナーなポイントが、H = O(Nログ())ので、この答えは、オリジナルの答えよりもnが漸近的に悪化しているO(nは(ログ(n))は、時間の複雑さ、である。 –

3

レベルごとにノードを保存する必要はありません。すべてのノードをdeque structureに保存し、ノードのレベルをアレイ内に維持することで解決できます。

Deque <Integer> deque = new LinkedList(); 
Queue <Integer> q = new LinkedList(); 
int[]level = new int[n];//we need to store the level of each node, n is number of node 
q.add(0);//Adding the root node 
while(!q.isEmpty()){ 
    int node = q.deque(); 
    for(int child : getNodeChildren(node)){ 
     level[child] = level[node] + 1;//Keep track the level of child node 
     q.add(child); 
     deque.add(child);//Store the child node into the queue. 
    } 
} 
// Print output 
boolean printHead = true; 
while(!deque.isEmpty()){ 
    if(printHead){ 
     int node = deque.pollFirst(); 
     print node; 
     //Check if we already printed every node in this current level, 
     //if yes, we need to switch to print from the end of the queue 
     if(!deque.isEmpty() && level[deque.getFirst()] != level[node]){ 
      printHead = false; 
     } 
    }else{ 
     int node = deque.pollLast(); 
     print node; 
     if(!deque.isEmpty() && level[deque.getLast()] != level[node]){ 
      printHead = true; 
     } 
    } 
} 

コードのロジックは次のとおりです。次のノードはまた、我々はただ意味最後に印刷されたノードと同じレベルにない場合

我々は、dequeの先頭から印刷を続けます現在のレベルのすべての要素を印刷した場合は、dequeの最後から印刷するように切り替える必要があります。逆も同様です。すべてのノードが印刷されるまでこのプロセスを続行します。

空間複雑度はO(n)、時間複雑度はO(n)です。

+0

良い実装。入力がありがとう、スペースの複雑さがO(n)未満になるアルゴリズム(存在する場合)を探していますが、私は第1レベルと最後のレベルだけを何とかして保存し、そして第2レベルとn-1レベルなどを再帰的に繰り返すことを考えていました。アルゴリズムは見つかりましたが、時間の複雑さはO(n)以上です。 – Atri

+0

@ashutoshはい、時間と空間の間にいつも決定が必要ですが、ツリーを格納するためにO(n)の空間の複雑さが常に必要なので、あなたはO(n) )。また、Javaの言語では、ノードリストを格納するための配列リストを保持するためにIMOを使用すると、両端キューを使用するだけではなく、オーバーヘッドが大きくなります。 –

+2

スペースの複雑さには入力が含まれていません。コンピューティングの際に必要な余分なスペース(入力以外)。さもなければ、O(1)の空間複雑さ解決策が決して存在しない可能性がある。例:配列内の数値を検索する。 – Atri

1

私は彼の質問のポスターに感謝し、時間の複雑さと空間の複雑さが互いにどのように反対しているかの実例を提示したいと思います。大量のスペースがあれば、アルゴリズムは非常に高速になります(「状態」を作成できるため)。逆に、スペースが非常に限られている場合は、状態の不足を補うために余分な繰り返しを実行する必要があります。

この問題は、空間の複雑さO(n)で解くのが容易であることが分かります。結果(O(n))のためのスペースを作成することが許可されている場合、時間複雑度O(n)で解を作成できるいくつかのアルゴリズムがあります。 1つの解決策が掲載されています(私が好きです)。また、O(n)と時間複雑度O(n)を解くために、異なる、おそらくエレガントなアプローチを提供します。メソッドsolveOrderN()を参照してください。

難問は、O(n)未満の空間複雑度の解を見つける方法です。これを行うには、結果スペースを作成することを許可されてはならず、結果として、その質問に示されたツリースペース内で操作することが強制されます。要素をスワップする必要があります。

解決策 - solveSubOrderN() - は結果空間を作成しません。答えは質問と同じメモリ空間に返されます。

私は非常に楽観的でした。私はO(log base 2(n))とO(n)に近い時間的複雑さでもこの問題を解決できました。しかし、多くの分析の後、私はこれを積極的にすることはできません。

要素の入れ替えを開始すると、処理不能な要素に戻るときに、最終的に「停止状態」になります。この停止が存在しない場合は、O(ログベース2 n)を達成できます。しかし、あなたはそれを避けることはできません。そこで、これを補うために、処理中の要素の状態を表すスペース(ブール値の配列)を作成することを余儀なくされました。

このブール値の状態が、結果/解決策のためのスペースを作成することとどう違うかについて、お話したいと思います。解(n個の要素、サイズs)のために作成するときは、スペース= n * sを作成しています。この質問では、sは整数です。一般的には非常に大きく、「高価な」プロセス(そしてポスターがこの問題を引き起こした理由)になります。ブール値の配列の空間はかなり小さく、sのサイズが大きくなるにつれ無視できるほどです(つまり、n/sは、sが大きくなるにつれて0に近づきます)。

また、空間の複雑さがO(n)より小さい場合、時間の複雑さO(n)を達​​成できないことが分かります。停止状態になると、再度反復する必要があります。しかし、バイナリツリーの場合、余分な反復の量は少ない(各反復は単に次の開始点を見つけることである)ことが判明した。

要約すると、以下の2つの解決策を見つけてください。 1つは、空間複雑度O(n)および時間複雑度O(n)を有するが、より重要なことは、O(n)に非常に近い時間複雑さを有する空間複雑度がO

public class BinaryTree { 

public static void main(String[] args) { 

    int treeN2[] = {1, 2, 3}; 
    int treeN3[] = {1, 2, 3, 4, 5, 6, 7}; 
    int treeN4[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 
    int treeN5[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; 

    int answer[] = solveOrderN(treeN5); 
    System.out.println(Arrays.toString(answer)); 
    solveSubOrderN(treeN5); 
    System.out.println(Arrays.toString(treeN5)); 

} 

/** 
* Given a binary tree, Perform inward spiral tree traversal.<br> 
* With this approach, there is no space created for the result. 
* All manipulation is done within the original tree<br> 
* Some space is created (boolean[n]) to add necessary state of processing. 
* Space Complexity: Less than O(n), greater than log(base2)(n) 
* Time Complexity: Slightly greater than O(n) 
* @param tree Input tree 
*/ 
public static void solveSubOrderN(int tree[]) { 

    boolean complete[] = new boolean[tree.length]; 
    Arrays.fill(complete, false); 

    System.out.println("Solving Sub O(n); tree size="+tree.length); 
    int n = log2Round(tree.length+1); 

    if (n == 1) 
     return; 

    int o[] = getLevelOffsets(n); 
    System.out.println("Number of levels="+n); 

    int pos = 0; 
    complete[0] = true; 
    int currentValue = 0; 
    int moves=1; 

    while (moves < tree.length) { 
     pos = getStartingPos(complete); 
     currentValue = tree[pos]; 
     tree[pos] = 0; 
     while (true) { 
      int nextPos = getTargetPosition(pos, o, n); 
      int nextValue = tree[nextPos]; 
      tree[nextPos] = currentValue; 
      complete[nextPos] = true; 
      currentValue = nextValue; 
      pos = nextPos; 
      moves++; 
      if (currentValue == 0) 
       break; 
     } 
    } 
} 

/** 
* Given a binary tree, Perform inward spiral tree traversal. 
* Space Complexity: O(n) 
* Time Complexity: O(n) 
* @param tree Input tree 
* @return The solution 
*/ 
public static int[] solveOrderN(int tree[]) { 
    int answer[] = new int[tree.length]; 
    int n = log2Round(tree.length+1); 
    int o[] = getLevelOffsets(n); 
    System.out.println("Solving O(n); tree size="+tree.length); 
    System.out.println("Number of levels="+n); 

    for (int i = 0; i < tree.length; i++) { 
     answer[getTargetPosition(i, o, n)] = tree[i]; 
    } 
    return answer; 
} 

/** 
* Search for the first unprocessed element 
* @param complete An array of boolean (true = processed) 
* @return 
*/ 
public static int getStartingPos(boolean[] complete) { 
    for (int i=0; i<complete.length; i++) { 
     if (!complete[i]) 
      return i; 
    } 
    return 1; 
} 

public static int getTargetPosition(int pos, int o[], int n) { 
    int row = getRow(pos); 
    int rowOrder = getRowOrder(row, n); 
    boolean isReversed = isBottom(row, n); 
    int posInRow = getPosInRow(pos, n); 
    int rowOffset = getRowOffset(rowSize(row), posInRow, isReversed); 
    return o[rowOrder]+rowOffset; 
} 

public static int getRowOffset(int rowSize, int posInRow, boolean isReversed) { 
    if (!isReversed) 
     return posInRow; 
    else 
     return rowSize-posInRow-1; 
} 

public static int rowSize(int row) { 
    return exp(row, 2); 
} 

public static int getPosInRow(int pos, int n) { 
    int row = getRow(pos); 
    return pos-(exp(row,2)-1); 
} 

/** 
* The top n/2 rows print forward, the bottom n/2 rows print reversed 
* @param row Zero based row [0 to n-1] 
* @param n Number of levels to the tree 
* @return true if line should be printed forward, false if reversed 
*/ 
public static boolean isBottom(int row, int n) { 
    int halfRounded = n/2; 
    return (row <= n-halfRounded-1) ? false : true; 
} 

public static int exp(int n, int pow) { 
    return (int)Math.pow(pow, n); 
} 

public static double log2(int n) { 
    return (Math.log(n)/Math.log(2)); 
} 

public static int log2Round(int n) { 
    return (int)log2(n); 
} 

/** 
* For a given position [0 to N-1], find the level on the binary tree [0 to n-1] 
* @param pos Zero based position in the tree (0 to N-1) 
* @return Zero based level (0 to n-1) 
*/ 
public static int getRow(int pos) { 
    return log2Round(pos+1); 
} 

/** 
* For a given row [0 to n-1], find the order in which that line would be processed [1 to log base 2 n] 
* @param row The row in the tree [0 to n-1] 
* @return The order that row would be processed [0 to n-1] 
*/ 
public static int getRowOrder(int row, int n) { 
    return isBottom(row, n) ? (n-row-1)*2+1 : row*2; 
} 

public static int getRowForOffset(int row, int n) { 
    boolean isOdd = row%2 == 1; 
    return isOdd ? n-(row-1)/2 - 1 : row/2; 
} 

/** 
* Compute the offset for a given ordered row 
* @param n The number of levels in the tree 
* @return Generated offsets for each row (to keep time complexity at O(n)) 
*/ 
public static int[] getLevelOffsets(int n) { 
    int o[] = new int[n]; 
    Arrays.fill(o, 0); 
    o[0] = 0; 
    int offset = 0; 
    for (int i=0; i<n; i++) { 
     int nextRow = getRowForOffset(i, n); 
     o[i] = offset; 
     offset += exp(nextRow, 2); 
    } 
    return o; 
} 

} 
1

あなたはsignificative余分なスペースせずに、それを解決することができますが、それはO(n)を必要があります。時間の複雑さは、2分木の表現に依存する。 あなたは配列を持っている場合は、時間計算量はO(n)は、たとえば次のようになります。

public static void main(String[] args) { 
    int[] binaryTree = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 
    int nodes = binaryTree.length; 
    int levels = (int) Math.ceil(Math.log(nodes)/Math.log(2)); 
    int[] increment = {1, -1}; 
    int index = 0; 
    for (int i = 0; i < levels; i++) { 
     int level = (index) * levels + increment[index] * (i/2) - index; 
     int begin = (int) Math.round(Math.pow(2, level)); 
     int[] range = {begin, 2 * begin - 1}; 
     for (int j = range[index]; j != range[1 - index]; j += increment[index]) { 
      System.out.print(" " + binaryTree[j-1]); 
     } 
     System.out.print(" " + binaryTree[range[1 - index]-1]); 
     index = 1 - index; 
    } 
    System.out.println(); 
} 

あなたのように定義されたノードとのバイナリツリーを持っている場合:(余分なスペースなし)

publi class Node { 
    Node[] children; 
    int value; 
} 

時間の複雑さO(n * log(n))となる。 例:あなたは、nと時間を計算するにはどうすればよい

public class BinaryNode { 
    BinaryNode[] children = {null, null}; 
    int value; 

    public BinaryNode(int value) { 
     this.value = value; 
    } 

    private int getRecursive(int reference) { 
     int result = value; 
     if (reference > 1) { 
      result = children[reference%2].getRecursive(reference/2); 
     } 
     return result; 
    } 

    public int get(int p) { 
     int reference = 1; 
     int p2 = p; 
     while (p2 > 1){ 
      reference = (reference << 1) + (p2 & 1); 
      p2 >>= 1; 
     } 
     return getRecursive(reference); 
    } 

    private void generateLevels(int levels){ 
     if (levels > 0){ 
      children[0] = new BinaryNode(value*2); 
      children[0].generateLevel(levels -1); 
      children[1] = new BinaryNode(value*2+1); 
      children[1].generateLevel(levels -1); 
     } 
    } 

    public static BinaryNode generate(int levels){ 
     BinaryNode result = null; 
     if (levels > 0){ 
      result = new BinaryNode(1); 
      result.generateLevels(levels - 1); 
     } 
     return result; 
    } 

    public static void main(String[] args) { 

     int levels = 5; 
     BinaryNode btreeRoot = BinaryNode.generate(levels); 
     int[] increment = {1, -1}; 
     int index = 0; 
     for (int i = 0; i < levels; i++) { 
      int level = (index) * levels + increment[index] * (i/2) - index; 
      int begin = (int) Math.round(Math.pow(2, level)); 
      int[] range = {begin, 2 * begin - 1}; 
      for (int j = range[index]; j != range[1 - index]; j += increment[index]) { 
       System.out.print(" " + btreeRoot.get(j)); 
      } 
      System.out.print(" " + btreeRoot.get(range[1 - index])); 
      index = 1 - index; 
     } 
     System.out.println(); 
    } 
} 

EDIT

? 簡単:

h = log2(n) 

ため、時間複雑度:O(N *ログn)== O(N * hで)。

アルゴリズムはO(log(n))の要素を取得し、すべての(n要素)を取得する必要があります。結果の複雑さはO(n * log(n))です。 そしてアルゴリズムは余分なスペースなしでそれを行います。

BtreeRootは元のバイナリツリーです。アルゴリズムはすべてのノードを正しい順序で取得し、次のレベルをlevelで計算します。 levelは、O(1)の値(1, levels -1, 2, levels - 2)になります(log2(n)回) すべてのレベルで、アルゴリズムはmノードを取得します。2^levelからまでです。すべてのレベルの合計はn(ノードの数)、 レベル(奇数または偶数)に依存して、現在のノードの位置を最初から最後まで、または逆から計算します。次に、現在のノードの位置を知っていれば、完璧なバイナリツリーで値を取る必要があり、Log(n)のコストがかかります。あなたは木が表現される方法についての仮定を作っているし、次にあなたが任意の追加のデータおよび/または構造中でその表現を拡張することはできませんのようなあなたのスペースの複雑さを言って

+0

あなたはO(1)スペースを意味する場合と、 O(n * log n)時間、あなたはそのアルゴリズムを言葉で説明できますか? –

+0

@גלעדברקן編集、私は十分なのか分かりません。 –

1

は< O(n)が、それはそうですあなたのソリューション(つまりO(n))。

私の答えは、ツリーの高さがO(logN)であることを意味するバランスがとれていることを前提にします。

この仮定の下では、ツリーをヒープデータ構造に格納できます。この構成では、ノードデータがレベルが配置されて、配列に格納されている:

[0][1][1][2][2][2][2][3][3][3][3][3][3][3][3]... 

そのノードがなかった場合、アレイ内の所与の位置は、ノードデータまたはNULLを指すポインタへのポインタを持っているでしょう木には存在しません。

この場合のスペース量はO(2^L)です。ここで、Lはツリー内のレベル数です。バランスの取れた木では、L = O(log(N))なので、この表現のサイズはO(N)です。

また、この配列のサイズを知っていると仮定すると、ツリーのレベル数はceiling(log_2(size))です。

レベルを想定すると、0...K-1から、配列内の任意のレベルの開始位置は、2^L - 1になります。

ここにアルゴリズムの擬似コードがあります。

PrintLevel(Array<Node> heap, level) { 
    int levelStart = power(2, level) - 1; 
    int nextLevelStart = (levelStart + 1) * 2 - 1; // same as power(2, level+1) - 1 
    for(int i = levelStart; i < nextLevelStart; i++) { 
     if(heap[i] != NULL) { 
      print(heap[i]); 
     } 
    } 
} 

SpiralPrint(Array<Node> heap) { 
     int lowLevel = Ceil(Log(heap.size())) - 1 
     int highLevel = 0 
     while(lowLevel >= highLevel){ 
      PrintLevel(heap, highLevel); 
      if (lowLevel > highLevel) 
       PrintLevel(heap, lowLevel); 
      highLevel += 1; 
      lowLevel -= 1; 
     } 
    } 
関連する問題