Liste başka bir listenin alt kümesidir olup olmadığını belirlemek nasıl?

13 Cevap php

Liste başka bir liste bir alt kümesi olup olmadığını belirlemek için etkili yolu nedir?

Örnek:

is_subset(List(1,2,3,4),List(2,3))    //Returns true
is_subset(List(1,2,3,4),List(3,4,5))  //Returns false

Ben çoğunlukla verimli algoritması arıyor ve çok liste saklanır nasıl endişe etmiyorum. Bu dizi, bağlantı liste ya da başka bir veri yapısında depolanır.

Teşekkürler

EDIT: liste sıralanır

13 Cevap

Burada yapabileceğiniz birkaç ticaret off vardır. Diyelim ki elemanları iki takım var olduğunu varsayalım, S ve T, biz belirlemek istiyoruz bir evren U'dan çizilmiş S ≥ T. Verilen örneklerden birinde,
var

S={1,2,3,4}
T={3,4,5}
U={1,2,3,4,5}

1. Sorted Lists (or balanced search tree)
The method suggested by most posters. If you already have sorted lists, or don't care about the length of time it takes to create them (say, you're not doing that often), then this algorithm is basically linear time and space. This is usually the best option.

(Uygun yerlerde, ancak bu genellikle relivant değil "| | U Log" Burada diğer seçimler adil olmak için, zaman ve mekan sınırları aslında faktörleri içermelidir)

Data structures: S ve T. her Yoksa sürekli uzayda iterated dengeli bir arama ağacı (örn. AVL ağacı, kırmızı-siyah ağaç, B +-tree) için sıralama listesi.

Algorithm: T her eleman için, sırayla, arama, o öğenin doğrusal S. Her arama bıraktığınız yerden hatırlıyorum ve orada bir sonraki arama başlatın. Her arama başarılı olursa, o zaman S ≥ T.

Time complexity: yaklaşık O( | S | Giriş | S | + | T | Giriş | T | ) sıralı listeleri oluşturmak için, {[(1)] } max (| S |, | T |) ) karşılaştırmak.

Space complexity, yaklaşık O( | S | + | T | )

Example (C++)

#include <set>
#include <algorithm>

std::set<int> create_S()
{
    std::set<int> S;
    // note: std::set will put these in order internally
    S.insert(3);
    S.insert(2);
    S.insert(4);
    S.insert(1);
    return S;
}

std::set<int> create_T()
{
    std::set<int> T;
    // note std::set will put these in order internally
    T.insert(4);
    T.insert(3);
    T.insert(5);
    return T;
}

int main()
{
    std::set<int> S=create_S();
    std::set<int> T=create_T();
    return std::includes(S.begin(),S.end(), T.begin(), T.end());
}

2. Hash tables
Better average time complexity than with a sorted list can be obtained using hash tables. The improved behavior for large sets comes at the cost of generally poorer performance for small sets.

Sıralanan listeleri gibi, ben evrenin büyüklüğüne göre katkıda karmaşıklığını görmezden geliyorum.

Data structure: S için Hash tablo, T. çabuk iterable şey

Algorithm: kendi hashtable içine S her elemanını takın. Daha sonra, T her öğe için, bu karma tablo olmadığını görmek için kontrol edin.

Time complexity: O( | S | + | T | ) kurmak, O( | T | ) karşılaştırmak için.

Space complexity, O( | S | + | T | )

Example (C++)

#include <tr1/unordered_set>

std::tr1::unordered_set<int> create_S()
{
    std::tr1::unordered_set<int> S;
    S.insert(3);
    S.insert(2);
    S.insert(4);
    S.insert(1);
    return S;
}

std::tr1::unordered_set<int> create_T()
{
    std::tr1::unordered_set<int> T;
    T.insert(4);
    T.insert(3);
    T.insert(5);
    return T;
}

bool includes(const std::tr1::unordered_set<int>& S, 
              const std::tr1::unordered_set<int>& T)
{
    for (std::tr1::unordered_set<int>::const_iterator iter=T.begin();
         iter!=T.end();
         ++iter)
    {
        if (S.find(*iter)==S.end())
        {
            return false;
        }
    }
    return true;
}

int main()
{
    std::tr1::unordered_set<int> S=create_S();
    std::tr1::unordered_set<int> T=create_T();
    return includes(S,T);
}

3. Bit sets
If your universe is particularly small (let's say you can only have elements 0-32), then a bitset is a reasonable solution. The running time (again, assuming you don't care about setup time) is essentially constant. In the case you do care about setup, it's still faster than creating a sorted list.

Ne yazık ki, bit kümeleri hatta orta büyüklükteki bir evren için çok hızlı bir şekilde hantal hale gelir.

Data structure, bit S her biri için vektör (genellikle bir makine tam sayı) ve T. Biz = 11110 S kodlamak ve T = 00111, verilen örnekte olabilir.

Algorithm: sonuç T, daha sonra S ≥ T. eşitse T. gelen bit ile S her bit bitsel 've' bilgisayar tarafından, kavşak hesaplayın

Time complexity: O( | U | + | S | + | T | ) kurulumu, O( | U | {[(2 )]} karşılaştırmak.

Space complexity, O( | U | )

Example: (C++)

#include <bitset>

// bitset universe always starts at 0, so create size 6 bitsets for demonstration.
// U={0,1,2,3,4,5}

std::bitset<6> create_S()
{
    std::bitset<6> S;
    // Note: bitsets don't care about order
    S.set(3);
    S.set(2);
    S.set(4);
    S.set(1);
    return S;
}

std::bitset<6> create_T()
{
    std::bitset<6> T;
    // Note: bitsets don't care about order
    T.set(4);
    T.set(3);
    T.set(5);
    return T;
}

int main()
{
    std::bitset<6> S=create_S();
    std::bitset<6> T=create_T();

    return S & T == T;
}

4. Bloom filters
All the speed benefits of bitsets, without the pesky limitation on universe size the bitsets have. Only one down side: they sometimes (often, if you're not careful) give the wrong answer: If the algorithm says "no", then you definitely don't have inclusion. If the algorithm says "yes", you might or might not. Better accuracy is attained by choosing a large filter size, and good hash functions.

Onlar ve yanlış cevaplar verecek, Bloom filtreleri korkunç bir fikir gibi gelebilir göz önüne alındığında. Ancak, belirli bir kullanım alanı vardır. Genellikle bir hızla birçok dahil denetimlerini yapmak Bloom filtreleri kullanın ve daha sonra gerektiğinde doğruluğunu garanti için daha yavaş bir deterministik yöntemini kullanırsınız. Bağlantılı Wikipedia makale Bloom filtreleri kullanarak bazı uygulamalar bahseder.

Data structure: A Bloom filter süslü bitset olduğunu. Önceden bir filtre boyutu ve hash fonksiyonları seçmelisiniz.

Algorithm (kroki): 0 Bit kümesiyle başlatma, bir çiçek filtreye bir öğe eklemek, her hash fonksiyonu ile karma ve bitset uygun biti ayarlamak için.. Içermenin belirlenmesi sadece bit kümeleri gibi çalışıyor.

Time complexity, O( filter size )

Space complexity, O( filter size )

Probability of correctness: o "S T içermez" için cevap verirse her zaman doğru. (| S | x | T | / (filter size)) Bu cevap) ise "S T içerir" Something ^ 0,6185 gibi. Doğruluk makul bir olasılık vermek | T | S | | Özel olarak, filtre boyutu ürününe orantılı seçilmelidir ve.

C + + için en iyi yolu, std::includes algoritma kullanmaktır:

#include <algorithm>

std::list<int> l1, l2;
...
// Test whether l2 is a subset of l1
bool is_subset = std::includes(l1.begin(), l1.end(), l2.begin(), l2.end());

Bu soru belirtildiği gibi, sıralanması için iki liste gerektirir. Karmaşıklık doğrusal.

Sadece Python bunun için bir yöntem olduğunu belirtmek istedim:

return set(list2).issubset(list1)

Veya:

return set(list2) <= set(list1)

Her iki listeleri sipariş varsa, basit bir çözüm aynı anda (her iki listede de iki yumru işaretçileri ile) her iki listede üzerine gitmek olacak ve tüm elemanlar bulununcaya kadar ikinci listede tüm öğeleri (ilk listede görünür doğrulayacaktır veya ilk listede daha büyük bir sayı ulaşana kadar).

C + + ile bir pseudo-kod şöyle olacaktır:

List l1, l2;
iterator i1 = l1.start();
iterator i2 = l2.start();
while(i1 != l1.end() && i2 != l2.end()) {
  if (*i1 == *i2) {
    i1++;
    i2++;
  } else if (*i1 > *i2) {
    return false;
  } else {
    i1++;
  }
}
return true;

(Açıkçası olduğu gibi çalışmaz, ancak fikir açık olmalıdır).

Listeleri sipariş değilseniz, bir hashtable kullanabilirsiniz - ilk listesinde tüm öğeleri eklemek, ve sonra ikinci listede tüm öğeleri hashtable görünür olmadığını kontrol edin.

Bu algoritmik cevaplar. Farklı dillerde, yerleşik yöntemlerin bu kontrol varsayılan vardır.

If you're concerned about ordering or continuity, you may need to use the Boyer-Moore or the Horspool algorithm.

Sorusu, bir alt kümesi olarak [1, 2] dikkate istiyorsun [1, 2, 3]? Eğer istiyorsun [1, 3] bir alt kümesi olarak kabul edilecek [1, 2, 3]? Cevabı hem de bu hayır ise, yukarıda bağlantılı algoritmaları birini düşünebilirsiniz. Aksi takdirde, bir karma set düşünebilirsiniz.

Scala, sen alt kümesi tarafından dizilerine demek varsayarak:

def is_subset[A,B](l1: List[A], l2: List[B]): Boolean =
  (l1 indexOfSeq l2) > 0

Her neyse, bir alt dizi sadece bir alt sorundur. Optimal algoritmaları Knuth-Morris-Pratt ve Boyer-Moore, ve daha bir kaç karmaşık olanları içerir.

Eğer gerçekten, ama alt kümesi anlamına geliyordu ve böylece Takımları değil Listelerinin konuşma, sadece Scala subsetOf yöntemini kullanabilirsiniz. Algoritmalar kümesi saklanır nasıl bağlıdır. Aşağıdaki algoritma çok bırakıyorsa biri bir liste depolama için çalışıyor.

def is_subset[A,B](l1: List[A], l2: List[B]): Boolean = (l1, l2) match {
  case (_, Nil) => true
  case (Nil, _) => false
  case (h1 :: t1, h2 :: t2) if h1 == h2 => is_subset(t1, t2)
  case (_ :: tail, list) => is_subset(tail, list)
}

IndexOfSeq için scala bagajına ben inceleyebilirsiniz KMP uygulanmaktadır: SequenceTemplate

Bir HashSet içinde veri depolama ok iseniz sadece list1 list2 her x için x içerip içermediğini kontrol edebilirsiniz. List2 büyüklüğünde O (n) yakın olacak. (Tabii ki de diğer datastructures ile aynı şeyi yapabilirsiniz, ama bu farklı çalıştırmalar yol açacaktır).

Bu, dil / araç, hem de listelerinin boyutuna ve depolama son derece bağlıdır.

Listeleri sıralanır eğer, tek bir döngü bu belirleyebilirsiniz. Sadece bir sonraki geçmek, sonra (eğer değer geçmek eğer kırmak), ve bulunduğunuz yerden devam küçük listenin ilk elemanını bulmak için çalışırken daha büyük bir liste yürümeye başlayabilirsiniz. Bu bir döngü / tek geçiş algoritması beri bu hızlı.

Ayrımı yapılmamış diğer listeleri için, bu karma kapalı ikinci listedeki her öğe daha sonra arama, ilk listenin elemanlarından karma tablo çeşit oluşturmak için genellikle hızlı bulunuyor. Bu (onlar oldukça büyük bir geçici bellek gereksinimleri olmasına rağmen). NET LINQ uzantıları birçok öğeyi bir liste içinde arama için dahili kullanımı ve oldukça iyi ölçeklediğinizi yaklaşımdır.

func isSubset ( @list, @possibleSubsetList ) {
    if ( size ( @possibleSubsetList ) > size ( @list ) ) {
        return false;
    }
    for ( @list : $a ) {
        if ( $a != @possibleSubsetList[0] ) {
            next;
        } else {
            pop ( @possibleSubsetList );
        }
    }
    if ( size ( @possibleSubsetList ) == 0 ) {
        return true;
    } else {
        return false;
    }
}

O (n) viyola. Tabii, isSubset ((1,2,3,4,5), (2,4)) true dönecektir

STL yöntemi arama uygulaması bakmak olmalıdır. Ben bu yapılabilir düşünüyorum C + + yoludur.

http://www.sgi.com/tech/stl/search.html

Açıklama:

Arama aralığı içinde bir alt dizi [first1, last1) özdeş olduğu tespit [first2, last2) öğesi-by-eleman karşılaştırıldığında.

Sen bir alt bir dizeye aitse aynı sorun doğrulamak için bir liste başka bir liste bir alt kümesi olup olmadığını sorun kontrol görebilirsiniz. Bunun için en iyi bilinen algoritması KMP (Knuth-Morris-Pratt) olduğunu. Bir sözde-kodu için wikipedia bakmak ya da sadece tercih dilde bazı String.contains yöntemini kullanın. =)

Verimli algoritma (Python) bellekte kabul durumları tutmak devlet makinesinin çeşit kullanır:

def is_subset(l1, l2):
    matches = []
    for e in l1:
        # increment
        to_check = [0] + [i+1 for i in matches]
        matches = [] # nothing matches
        for i in to_check:
            if l2[i] = e:
                if i == len(l2)-1:
                    return True
                matches.append(i)
    return False

EDIT: liste sıralanır eğer tabii, o algoritma gerek yok, sadece bunu:

def is_subset(l1, l2):
    index = 0
    for e in l1:
        if e > l2[index]:
            return False
        elif e == l2[index]:
            index += 1
        else:
            index == 0
        if index == len(l2):
            return True
    return False