Sıralı Tam Sayılar Listesinden Dengeli BST Oluşturma


15

Benzersiz, sıralı bir tamsayı listesi verildiğinde, özyineleme kullanmadan dizi olarak temsil edilen dengeli bir ikili arama ağacı oluşturun.

Örneğin:

func( [1,2,3,5,8,13,21] ) => [5,2,13,1,3,8,21]

Başlamadan önce, bir ipucu: bu sorunu bir ton basitleştirebiliriz, böylece giriş tam sayılarını (veya bu konu için karşılaştırılabilir herhangi bir nesneyi!) Düşünmek zorunda kalmazız.

Giriş listesinin zaten sıralandığını biliyorsanız, içeriği önemsizdir. Sadece orijinal diziye endeksler açısından düşünebiliriz.

Girdi dizisinin dahili bir temsili şu şekilde olur:

func( [0,1,2,3,4,5,6] ) => [3,1,5,0,2,4,6]

Bu, karşılaştırılabilir nesnelerle uğraşmak zorunda olan bir şey yazmaktan ziyade, sadece [0, n) aralığından sonuçtaki diziye eşlenen bir işlev yazmamız gerekir. Yeni siparişi aldıktan sonra, dönüş dizisini oluşturmak için eşlemeyi girişteki değerlere geri uygulayabiliriz.

Geçerli çözümler:

  • Sıfır elemanlı bir diziyi kabul edin ve boş bir dizi döndürün.
  • N uzunluğunda bir tamsayı dizisini kabul edin ve bir tamsayı dizisi döndürün
    • N ile 2 eksi 1 arasındaki bir sonraki en yüksek güç arasındaki uzunluk .
    • Kök düğümün 0 konumunda olduğu ve yüksekliğin log (n) 'e eşit olduğu bir BST'yi temsil eden dizi, burada 0 eksik bir düğümü (veya nulldiliniz izin veriyorsa benzer bir değeri ) temsil eder . Boş düğümler, varsa, yalnızca ağacın sonunda olmalıdır (ör. [2,1,0])

Giriş tamsayı dizisi aşağıdaki garantilere sahiptir:

  • Değerler, sıfırdan büyük 32 bit işaretli tamsayılardır.
  • Değerler benzersizdir.
  • Değerler sıfır konumundan artan sıradadır.
  • Değerler seyrek olabilir (yani, birbirine bitişik olmayabilir).

Ascii karakter sayısına göre en kısa kod kazanır, ancak aynı zamanda herhangi bir dil için zarif çözümler görmekle de ilgileniyorum.

Test senaryoları

İhtiva eden basit diziler için çıkışlar 1için nçeşitli için n. Yukarıda tarif edildiği gibi, takip eden 0kısımlar isteğe bağlıdır.

[]
[1]
[2,1,0]
[2,1,3]
[3,2,4,1,0,0,0]
[4,2,5,1,3,0,0]
[4,2,6,1,3,5,0]
[4,2,6,1,3,5,7]
[5,3,7,2,4,6,8,1,0,0,0,0,0,0,0]
[6,4,8,2,5,7,9,1,3,0,0,0,0,0,0]
[7,4,9,2,6,8,10,1,3,5,0,0,0,0,0]
[8,4,10,2,6,9,11,1,3,5,7,0,0,0,0]
[8,4,11,2,6,10,12,1,3,5,7,9,0,0,0]
[8,4,12,2,6,10,13,1,3,5,7,9,11,0,0]
[8,4,12,2,6,10,14,1,3,5,7,9,11,13,0]
[8,4,12,2,6,10,14,1,3,5,7,9,11,13,15]

İster bir programlama bulmacası ister bir kod golfü olsun, bu sitedeki tüm sorular objektif bir birincil kazanma ölçütüne sahip olmalıdır, böylece hangi girişin kazanacağına tartışmasız karar vermek mümkündür.
Howard

@ Teşekkürler. Kazanan için kesin ölçütlerle güncellendi.
Jake Wharton

1
En kolay olanı (şu anda olduğu gibi) yerine, zor vakaları kapsayan bazı test senaryolarına sahip olmak çok yararlı olacaktır.
Peter Taylor

Özyinelemeyi reddetmek için bir neden var mı? Yinelemeli bir çözüme baktığımdan değil, bu hem yapay hem de gereksiz görünüyor.
dmckee --- eski moderatör kedi yavrusu

1
Birisi listenin bir BST'yi nasıl temsil ettiğini açıklayabilir mi?
justinpc

Yanıtlar:


4

Yakut , 143

s=ARGV.size;r,q=[],[[0,s]];s.times{b,e=q.shift;k=Math::log2(e-b).to_i-1;m=(e-b+2)>(3<<k)?b+(2<<k)-1:e-(1<<k);r<<ARGV[m];q<<[b,m]<<[m+1,e]};p r

Temel olarak ağaç üzerinde bir BFS yapan aşağıdaki kodun (gevşek) sıkıştırılmış bir sürümüdür.

def l(n)
    k = Math::log2(n).to_i-1
    if n+2 > (3<<k) then
        (2<<k)-1
    else
        n-(1<<k) 
    end
end

def bfs(tab)
  result = []
  queue = [[0,tab.size]]
  until queue.empty? do
    b,e = queue.shift
    m = b+l(e-b)
    result << tab[m]
    queue << [b,m] if b < m
    queue << [m+1,e] if m+1 < e
  end
  result
end

p bfs(ARGV)

Ayrıca, DFS değil, BFS olduğundan, özyinelemeli çözüm gereksinimi önemli değildir ve bazı dilleri dezavantajlı hale getirir.

Düzenleme: Yorumu için @PeterTaylor sayesinde Sabit çözüm!


@PeterTaylor Niyet 4'ü 4'ün soluna koymaktı, ancak boşluk yok, bu yüzden yanlış. Bunu işaret ettiğiniz için teşekkürler!
dtldarek

@PeterTaylor Öğle yemeğinde düzeltildi, şimdi çalışmalı.
dtldarek

4

Java , 252

Tamam, işte benim girişimim. Ben bit işlemleri ile oynuyordum ve ben orijinal dizinin dizininden BST bir öğenin dizin hesaplamak için bu doğrudan yolu ile geldi.

Sıkıştırılmış versiyon

public int[]b(int[]a){int i,n=1,t;long x,I,s=a.length,p=s;int[]r=new int[(int)s];while((p>>=1)>0)n++;p=2*s-(1l<<n)+1;for(i=0;i<s;i++){x=(i<p)?(i+1):(p+2*(i-p)+1);t=1;while((x&1<<(t-1))==0)t++;I=(1<<(n-t));I|=((I-1)<<t&x)>>t;r[(int)I-1]=a[i];}return r;}

Uzun versiyon aşağıdadır.

public static int[] makeBst(int[] array) {
  long size = array.length;
  int[] bst = new int[array.length];

  int nbits = 0;
  for (int i=0; i<32; i++) 
    if ((size & 1<<i)!=0) nbits=i+1;

  long padding = 2*size - (1l<<nbits) + 1;

  for (int i=0; i<size; i++) {
    long index2n = (i<padding)?(i+1):(padding + 2*(i-padding) + 1);

    int tail=1;
    while ((index2n & 1<<(tail-1))==0) tail++;
    long bstIndex = (1<<(nbits-tail));
    bstIndex = bstIndex | ((bstIndex-1)<<tail & index2n)>>tail;

    bst[(int)(bstIndex-1)] = array[i];
  }
 return bst;
}

Bir karakter sayısına ihtiyacınız var ve bu şu anda golf değil.
dmckee --- eski moderatör kedi yavrusu

@dmckee Yazıyı, sıkıştırılmış bir sürümü ve bir karakter sayısını içerecek şekilde düzenledim
mikail sheikh

İyi gösteri. Bahse girerim ki bu alanlardan bazıları gereksizdir. C olarak, int[] b(int[] a)olduğu gibi ifade edilir int[]b(int[]a).
dmckee --- eski moderatör yavru kedi

Sen sahip a.lengthdizi tahsisinde. Olarak değiştirin s. for (Birden çok defa boşluktan kurtulun . Her for döngüsü bir int i=0ve de oluşturur int t=0. n( int n=0,i,t;) İle oluşturun ve sonra sadece i=0döngülerde ve t=1içeride. İç bildirmek long xve long Iile s(döngüde sadece başlat ve long s=a.length,I,x;ve x=../ I=..). İkili AND çevresinde boşluklara ihtiyacınız olmamalıdır &.
Jake Wharton

Ayrıca, I=I|..yazılabilirI|=..
Jake Wharton

3
def fn(input):
    import math
    n = len(input)
    if n == 0:
        return []
    h = int(math.floor(math.log(n, 2)))
    out = []
    last = (2**h) - 2**(h+1) + n

    def num_children(level, sibling, lr):
        if level == 0:
            return 0
        half = 2**(level-1)
        ll_base = sibling * 2**level + lr * (half)
        ll_children = max(0, min(last, ll_base + half - 1) - ll_base + 1)
        return 2**(level-1) - 1 + ll_children

    for level in range(h, -1, -1):
        for sibling in range(0, 2**(h-level)):
            if level == 0 and sibling > last:
                break
            if sibling == 0:
                last_sibling_val = num_children(level, sibling, 0)
            else:
                last_sibling_val += 2 + num_children(level, sibling - 1, 1) \
                    + num_children(level, sibling, 0)
            out.append(input[last_sibling_val])
    return out

2

Bu, ağacın sonunda boş düğümlerin gereksiniminize tam olarak uyup uymadığından emin değil ve kesinlikle kısalık için herhangi bir ödül kazanmayacak, ancak doğru olduğunu ve test senaryoları olduğunu düşünüyorum :)

public class BstArray {
    public static final int[] EMPTY = new int[] { };
    public static final int[] L1 = new int[] { 1 };
    public static final int[] L2 = new int[] { 1, 2 };
    public static final int[] L3 = new int[] { 1, 2, 3 };
    public static final int[] L4 = new int[] { 1, 2, 3, 5 };
    public static final int[] L5 = new int[] { 1, 2, 3, 5, 8 };
    public static final int[] L6 = new int[] { 1, 2, 3, 5, 8, 13 };
    public static final int[] L7 = new int[] { 1, 2, 3, 5, 8, 13, 21 };
    public static final int[] L8 = new int[] { 1, 2, 3, 5, 8, 13, 21, 35 };
    public static final int[] L9 = new int[] { 1, 2, 3, 5, 8, 13, 21, 35, 56 };
    public static final int[] L10 = new int[] { 1, 2, 3, 5, 8, 13, 21, 35, 56, 91 };

    public static void main(String[] args) {
        for (int[] list : Arrays.asList(EMPTY, L1, L2, L3, L4, L5, L6, L7, L8, L9, L10)) {
            System.out.println(Arrays.toString(list) + " => " + Arrays.toString(bstListFromList(list)));
        }
    }

    private static int[] bstListFromList(int[] orig) {
        int[] bst = new int[nextHighestPowerOfTwo(orig.length + 1) - 1];

        if (orig.length == 0) {
            return bst;
        }

        LinkedList<int[]> queue = new LinkedList<int[]>();
        queue.push(orig);

        int counter = 0;
        while (!queue.isEmpty()) {
            int[] list = queue.pop();
            int len = list.length;

            if (len == 1) {
                bst[counter] = list[0];
            } else if (len == 2) {
                bst[counter] = list[1];
                queue.add(getSubArray(list, 0, 1));
                queue.add(new int[] { 0 });
            } else if (len == 3) {
                bst[counter] = list[1];
                queue.add(getSubArray(list, 0, 1));
                queue.add(getSubArray(list, 2, 1));
            } else {
                int divide = len / 2;
                bst[counter] = list[divide];
                queue.add(getSubArray(list, 0, divide));
                queue.add(getSubArray(list, divide + 1, len - (divide + 1)));
            }
            counter++;
        }

        return bst;
    }

    private static int nextHighestPowerOfTwo(int n) {
        n--;
        n |= n >> 1;
        n |= n >> 2;
        n |= n >> 4;
        n |= n >> 8;
        n |= n >> 16;
        n++;

        return n;
    }

    private static int[] getSubArray(int[] orig, int origStart, int length) {
        int[] list = new int[length];
        System.arraycopy(orig, origStart, list, 0, length);
        return list;
    }
}

2

Golf Yazıları ( 99 89)

~]:b[]:^;{b}{{:|.,.2base,(2\?:&[-)&2/]{}$0=&(2/+:o[=]^\+:^;|o<.!{;}*|o)>.!{;}*}%:b}while^p

Temelde Python çözümümün düz bir bağlantı noktası, hemen hemen aynı şekilde çalışıyor.

Muhtemelen @ petertaylor'un girdisi ile 10 karakterle daha fazla "golfizm" ile biraz geliştirilebilir :)


GolfScript cevabımı hala bitirmememe rağmen 70'ten fazla olmamak mümkün olmalı. Yine de, sizin için bazı kolay iyileştirmeler var. !{;}{}ifSadece olabilir !{;}*çünkü !garanti dönmek 0ya 1. Kullandığınız eğer öyleyse, değişkenler için alfabetik olmayan belirteçleri kullanabilirsiniz ^yerine r, |yerine x, &yerine ytüm bu boşluk ortadan kaldırabilir.
Peter Taylor

@PeterTaylor Teşekkürler, alfasayısal olmayan değişkenler hakkında bilmiyordum, hala golfscript için çok yeni :)
Joachim Isaksson

2

Java 192

Girdi içindeki dizin, çıktıdaki dizin

int[]b(int[]o){int s=o.length,p=0,u=s,i=0,y,r[]=new int[s],c[]=new int[s];while((u>>=1)>0)p++;for(int x:o){y=p;u=i;while(u%2>0){y--;u/=2;}r[(1<<y)-1+c[y]++]=x;i+=i>2*s-(1<<p+1)?2:1;}return r;}

Uzun versiyon:

static int[] bfs(int[] o) {
  int rowCount = 32 - Integer.numberOfLeadingZeros(o.length); // log2
  int slotCount = (1<<rowCount) - 1; // pow(2,rowCount) - 1

  // number of empty slots at the end
  int emptySlots = slotCount - o.length;
  // where we start to be affected by these empty slots
  int startSkippingAbove = slotCount - 2 * emptySlots; // = 2 * o.length - slotCount

  int[] result = new int[o.length];
  int[] rowCounters = new int[rowCount]; // for each row, how many slots in that row are taken
  int i = 0; // index of where we would be if this was a complete tree (no trailing empty slots)
  for (int x : o) {
    // the row (depth) a slot is in is determined by the number of trailing 1s
    int rowIndex = rowCount - Integer.numberOfTrailingZeros(~i) - 1;
    int colIndex = rowCounters[rowIndex]++; // count where we are
    int rowStartIndex = (1 << rowIndex) - 1; // where this row starts in the result array

    result[rowStartIndex + colIndex] = x;

    i++;
    // next one has to jump into a slot that came available by not having slotCount values
    if (i > startSkippingAbove) i++;
  }

  return result;
}

2

Wolfram Mathematica 11, 175 Bayt

g[l_]:=(x[a_]:=Floor@Min[i-#/2,#]&@(i=Length[a]+1;2^Ceiling@Log2[i]/2);Join@@Table[Cases[l//.{{}->{},b__List:>(n[Take[b,#-1],b[[#]],Drop[b,#]]&@x[b])},_Integer,{m}],{m,x[l]}])

Fonksiyon g[l]a girişi List(örneğin l={1,2,3,4,...}) olarak alır Listve istenen formdan a değerini döndürür . Aşağıdaki gibi çalışır:

  • x[a_]:=Floor@Min[i-#/2,#]&@(i=Length[a]+1;2^Ceiling@Log2[i]/2) bir liste alır ve ilgili BST'nin kökünü bulur.
    • i=Length[a]+1 listenin uzunluğu için kısayol
    • 2^Ceiling@Log2[i]/2 kök değerinin üst sınırı
    • Min[i-#/2,#]&@(...)#İçinde ne olduğunu gösteren iki argümanın en azı(...)
  • l//.{...} Aşağıdaki değiştirme kurallarını tekrar tekrar uygulayın l
  • {}->{} Yapacak bir şey yok (bu sonsuz bir döngüden kaçınmak için son durumdur)
  • b__List:>(n[Take[b,#-1],b[[#]],Drop[b,#]]&@x[b])Bir Bölünmüş ListINTO{{lesser}, root, {greater}}
  • Cases[...,_Integer,{m}] Tüm tam sayıları düzey (derinlik) al m
  • Table[...,{m,1,x[l]}]Hepsi için miçin yukarı x[l](birden fazla aslında gereklidir).

Çalıştırarak test edilebilir

Table[g[Range[a]], {a, 0, 15}]//MatrixForm

Bu uygulama sondaki sıfırları içermez.


1

Python ( 175 171)

Oldukça yoğun, hala okunabilir;

def f(a):
 b=[a]
 while b:
  c,t=((s,2**(len(bin(len(s)))-3))for s in b if s),[]
  for x,y in c:
   o=min(len(x)-y+1,y/2)+(y-1)/2
   yield x[o]
   t+=[x[:o],x[o+1:]]
  b=t

Sonucu geri verir, böylece üzerinde döngü yapabilir veya (görüntüleme amacıyla) bir liste olarak yazdırabilirsiniz;

>>> for i in range(1,17): print i-1,list(f(range(1,i)))
 0 []
 1 [1]
 2 [2, 1]
 3 [2, 1, 3]
 4 [3, 2, 4, 1]
 5 [4, 2, 5, 1, 3]
 6 [4, 2, 6, 1, 3, 5]
 7 [4, 2, 6, 1, 3, 5, 7]
 8 [5, 3, 7, 2, 4, 6, 8, 1]
 9 [6, 4, 8, 2, 5, 7, 9, 1, 3]
10 [7, 4, 9, 2, 6, 8, 10, 1, 3, 5]
11 [8, 4, 10, 2, 6, 9, 11, 1, 3, 5, 7]
12 [8, 4, 11, 2, 6, 10, 12, 1, 3, 5, 7, 9]
13 [8, 4, 12, 2, 6, 10, 13, 1, 3, 5, 7, 9, 11]
14 [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13]
15 [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15]

@dtldarek Onun yorumu kaldırılmış gibi görünüyor, ama şimdi test örneklerini geçiyor gibi görünüyor.
Joachim Isaksson

Ben insanlar bu adam olduğunu söyleyen bir yorum nedeniyle @ dtldarek cevap upvoting kaçınmak yorum sildi.
Peter Taylor

@PeterTaylor Peki, ilginiz için teşekkür ederim ;-)
dtldarek

1

Java

Bu doğrudan bir hesaplama çözümüdür. Bence işe yarıyor, ama pragmatik olarak zararsız bir yan etkisi var. Ürettiği dizi bozuk olabilir ancak aramaları etkileyecek hiçbir şekilde olmayabilir. 0 (boş) düğüm üretmek yerine, erişilemeyen düğümler üretecektir, yani düğümler arama sırasında ağaçta daha önce bulunmuş olacaktır. 2 boyutlu ikili arama ağacı dizisinin normal gücünün indeks dizisini düzensiz boyutlu bir ikili arama ağacı dizisine eşleyerek çalışır. En azından işe yaradığını düşünüyorum.

import java.util.Arrays;

public class SortedArrayToBalanceBinarySearchTreeArray
{
    public static void main(String... args)
    {
        System.out.println(Arrays.toString(binarySearchTree(19)));
    }

    public static int[] binarySearchTree(int m)
    {
        int n = powerOf2Ceiling(m + 1);
        int[] array = new int[n - 1];

        for (int k = 1, index = 1; k < n; k *= 2)
        {
            for (int i = 0; i < k; ++i)
            {
                array[index - 1] = (int) (.5 + ((float) (m)) / (n - 1)
                        * (n / (2 * k) * (1 + 2 * index) - n));
                ++index;
            }
        }

        return array;
    }

    public static int powerOf2Ceiling(int n)
    {
        n--;
        n |= n >> 1;
        n |= n >> 2;
        n |= n >> 4;
        n |= n >> 8;
        n |= n >> 16;
        n++;

        return n;
    }

}

İşte daha yoğun bir sürüm (sadece işlev ve adlar eşleştirildi). Hala beyaz alan var, ama kazanmak konusunda endişelenmiyorum. Ayrıca bu sürüm aslında bir dizi alır. Diğeri ise dizideki en yüksek dizin için int aldı.

public static int[] b(int m[])
{
    int n = m.length;
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8;
    n |= n >> 16;
    n++;

    int[] a = new int[n - 1];

    for (int k = 1, j = 1, i; k < n; k *= 2)
    {
        for (i = 0; i < k; ++i)
        {
            a[j - 1] = m[(int) (.5 + ((float) m.length) / (n - 1)
                    * (n / (2 * k) * (1 + 2 * j) - n)) - 1];
            ++j;
        }
    }

    return a;
}

Bu kod golf olduğundan , yöntemlerinizi / adlarınızı / vb. Mümkün olduğunca kısaltın; tüm boşlukları (ve gereksiz yöntemleri / materyali) kaldırın ve karakter sayısını ekleyin. Aksi halde, harika yapıyorsun.
Justin

@Jake Wharton. Doğrudan haritalama çözümünüzü görmek istiyorum. Madenin çok büyük diziler için çalıştığından% 100 emin değilim çünkü değerleri yuvarlanan sürekli bir matematiksel haritalamaya dayanıyor. Kesinlikle işe yarıyor gibi görünüyor, ama nasıl kanıtlayacağından emin değilim.
14:50

1

GolfScript ( 79 77 70 karakter)

Sorudaki örnek bir işlev kullandığından, bunu bir işlev haline getirdim. Yığına {}:f;girdi alan ve BST'yi yığın üzerinde bırakan bir ifadeyi bırakmak için 5 karakter kaydedilir.

{[.;][{{.!!{[.,.)[1]*{(\(@++}@(*1=/()\@~]}*}%.{0=}%\{1>~}%.}do][]*}:f;

Çevrimiçi demo (not: uygulama ısınmaya biraz zaman alabilir: 3 saniye içinde çalıştırmadan önce iki kez zaman aşımına uğradı).

Yapıyı göstermek için boşluk ile:

{
    # Input is an array: wrap it in an array for the working set
    [.;]
    [{
        # Stack: emitted-values working-set
        # where the working-set is essentially an array of subtrees
        # For each subtree in working-set...
        {
            # ...if it's not the empty array...
            .!!{
                # ...gather into an array...
                [
                    # Get the size of the subtree
                    .,
                    # OEIS A006165, offset by 1
                    .)[1]*{(\(@++}@(*1=
                    # Split into [left-subtree-plus-root right-subtree]
                    /
                    # Rearrange to root left-subtree right-subtree
                    # where left-subtree might be [] and right-subtree might not exist at all
                    ()\@~
                ]
            }*
        }%
        # Extract the leading element of each processed subtree: these will join the emitted-values
        .{0=}%
        # Create a new working-set of the 1, or 2 subtrees of each processed subtree
        \{1>~}%
        # Loop while the working-set is non-empty
        .
    }do]
    # Stack: [[emitted values at level 0][emitted values at level 1]...]
    # Flatten by joining with the empty array
    []*
}:f;

1

J , 52 bayt

t=:/:(#/:@{.(+:,>:@+:@i.@>:@#)^:(<.@(2&^.)@>:@#`1:))

işlevi sıralı bir liste alır ve ikili ağaç sırasına göre döner

ağaçların aynı şekle sahip olduğuna, ancak alt seviyenin kısaldığına dikkat edin

  • `1: 1 ile başla
  • <.@(2&^.)@>:@# log2 tabanına göre yineleme (uzunluk + 1)
  • +: , >:@+:@i.@>:@# döngü: tek sayılarla son vektörün iki katını ekler 1,3 .. 2 * uzunluk + 1
  • # /:@{. sadece gerekli sayıda öğeyi al ve sıralama indekslerini al
  • /: bu sıralama indekslerini verilen girdiye uygular

TIO


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.