2011-09-22 5 views
4

빠른 삽입/삭제 및 균일 무작위 선택을 허용하기 위해 C#에서 사용할 수있는 데이터 구조는 무엇입니까? 목록은 요소별로 느린 삭제 (매번 요소의 색인을 찾아야하기 때문에)이지만 HashSet은 요소를 임의로 선택할 수 없습니다 (목록에 복사하지 않고).C#에서 빠른 삽입/삭제 및 무작위 선택을 허용하는 설정

데이터 구조 지속적으로 업데이트되므로 삽입 및 삭제는 온라인 절차 여야합니다. 삽입, 삭제, 랜덤 선택을 모두 O (log n)로 만드는 방법이있는 것처럼 보입니다.

개체에 할당 된 임의의 정수 키를 가진 이진 검색 트리에서 이러한 모든 문제를 해결할 수 있지만 C# 표준 라이브러리에서 적절한 클래스를 찾을 수 없습니다. 사용자 정의 이진 검색 트리를 작성하지 않고이를 해결할 표준 방법이 있습니까?

+0

균일 한 임의 선택 기준은 무엇입니까? 키/값 쌍을 사용할 수 있다면 사전이 목적에 부합 할 수 있습니다. – EtherDragon

+0

기본적으로 모든 객체를 모자에 넣고 무작위로 꺼내야합니다 (단, 모자에서 유지해야합니다). 여기서는 객체의 구조가 없습니다. 필요한 경우 각 개체에 고유 한 ID를 연결할 수 있지만 의미가없는 선택입니다. –

답변

2

이미 C# BCL에 BST가 있고 SortedDictionary<TKey, TValue>이라고합니다. 키 값 쌍이 필요하지 않고 단일 항목을 원할 경우 SortedSet<T> (SortedSet은 .NET 4.0에 있음)을 사용할 수 있습니다.

예를 들어 SortedDictionary<int, WhateverValueType>을 원합니다. 비록 당신이 "균일 무작위 선택"이라고 말할 때 당신이 정확히 무엇인지 확신 할 수는 없지만.

물론, Dictionary<TKey, TValue>은 O (1)이며 훨씬 빠릅니다. 그래서 당신이 정렬 된 키 순서가 필요하다면, 나는 그것을 사용할 것입니다.

업데이트 : 필요에 따라 효율적으로 조정할 수 있습니다. 데이터 구조에서 임의의 인접 인덱스로 점프 할 수있게하려면 얼마나 자주 삽입/삭제할 것입니까? 자주 (O (n log n)) 후에 배열을 사용하고 Sort()를 사용하거나 항상 순서대로 삽입/삭제 (O (n)) 할 수 있습니다.

또는 당신이 Dictionary<int, YourType> 포장 및 병렬 List<int>을 유지하고 이후 모든 추가/삭제 업데이트 할 수 있습니다 :

_dictionary.Add(newIndex, newValue); 
_indexes.Add(newIndex); 

을 그리고 단지 조회의 목록에서 임의의 인덱스를 액세스 할 수 있습니다. 좋은 점은이 방법에서 실제로 Add()가 ~ O (1) (목록 크기가 조정되지 않지만 일부는 피할 수있는 초기 용량을 설정할 수 있음)이지만 O (n) .

조회가 불편하거나 삭제/삽입에 시간이 많이 걸리는 경우가 있습니다. 문제는 모든 액세스 시간 컨테이너가 연속적이지 않다는 것입니다. 듀얼 List<int>/Dictionary<int, YourValue> 콤보를 사용하면 꽤 좋은 믹스를 얻을 수 있습니다.

업데이트 2 : 우리의 계속 된 토론 에서처럼 절대 성능이 요구 사항이라면 자신 만의 행운을 빕니다. 생각 해보니 재미 있었고, 내가 다른 것을 생각하면 업데이트 할 것입니다.

+0

정렬 된 순서가 필요하지 않습니다. 요소에는 자연스럽게 "키"가 없으며 단지 데이터 객체입니다. 일률적 인 무작위 선택에 의해 컨테이너에 N 개의 객체가 있다면 임의의 주어진 객체를 얻을 확률 1/N으로 임의로 선택하고 싶습니다. 사전에서 무작위로 선택하려면 KeyCollection()을 목록으로 바꾸고 [random.Next (0, keys.Count())]와 같은 작업을 수행해야한다고 생각합니다. 하지만 KeyCollection을 목록으로 돌리거나 첫 번째 위치에 KeyCollection을 만들면 O (n)이됩니다. 그렇지 않습니까? –

+0

@Corey : Dictionary 을 사용하고 Random 클래스를 사용하여 임의의 숫자를 선택하고 값을 검색 할 수 있습니다. 주된 문제는 항목을 제거하면 해당 번호가 더 이상 임의 선택을 할 수 없게되는 것입니까? 인덱스가 일정하게 유지되지만 내용이 변경되는지 확인하려고합니다. –

+0

@Corey : 신경 쓰지 마라, 나는 네가하려는 것을 본다. –

2

이진 검색 나무와 SortedDictionary 또는 SortedSet 같은 파생 구조, 키를 비교 에 의해 작동한다.

개체는 그 자체로는 비교할 수 없지만 개체 ID와 해시 값을 제공합니다. 따라서 올바른 데이터 구조는 HashSet입니다. 참고 : Dictionary<int,YourType>은 제거가 선형 검색 (O (n))이되고 제거한 후 임의의 문제를 해결하지 않기 때문에 적절하지 않습니다.

  • 삽입 (1)
  • 제거 (1)
  • RandomElement은 O (N) 인 O이다 O이다. 그것은 쉽게 구현 될 수 있습니다.

    set.ElementAt(random.Next(set.Count)) 
    

    중간 목록에 복사 할 필요가 없습니다.

+0

감사합니다 set.ElementAt 예제; 유용하고 시간을 절약 할 수 있습니다. 그러나 각 객체와 관련된 임의의 고유 ID를 기반으로하는 사용자 정의 이진 검색 트리를 사용하면 O (log n)에서 세 가지 작업을 모두 수행 할 수 있다는 것을 알고 있습니다. 성능은이 응용 프로그램의 큰 문제이며, 아래의 내 의견에 따르면 세 가지 작업이 모두 거의 같은 비율로 사용되므로 O (n)이 우선 순위를 차지합니다. 거기에는 이런 유형의 기능을위한 표준 .NET 구성 요소가없는 것처럼 보입니다. –

+0

당신 말이 맞아요. 개체에 임의의 고유 ID를 추가하면 BST를 사용하면 모든 작업이 O (로그 n)이됩니다. 불행하게도 BCL에는 유용한 공용 구현체가 포함되어 있지 않습니다. 즉, SortedDictionary는 BST이지만 유용한 API는 제공하지 않습니다. – Mark

0

나는이 질문은 3 세 이상 실현, 그러나 다만이 페이지 건너 사람들을 위해 :

당신이 정렬 된 데이터 세트의 항목을 보관할 필요가없는 경우, 당신이 할 수있는 List<ItemType>을 사용해주세요.

삽입 및 임의 선택은 O (1)입니다. 마지막 항목을 삭제하려는 항목의 위치로 옮기고 끝에서 제거하여 O (1)에서 삭제할 수 있습니다.

코드 :

using System; // For the Random 
using System.Collections.Generic; // The List 

// List: 
List<ItemType> list = new List<ItemType>(); 

// Add x: 
ItemType x = ...; // The item to insert into the list 
list.Add(x); 

// Random selection 
Random r = ...; // Probably get this from somewhere else 
int index = r.Next(list.Count); 
ItemType y = list[index]; 

// Remove item at index 
list[index] = list[list.Count - 1]; // Copy last item to index 
list.RemoveAt(list.Count - 1); // Remove from end of list 

편집 : 물론List<ItemType> 당신이 알아야 인덱스에서 요소를 제거합니다. 무작위 요소를 제거하려면 위의 예제에서와 같이 임의의 인덱스를 사용할 수 있습니다. 특정 항목을 제거하려는 경우 항목을 해당 색인에 매핑하는 Dictionary<ItemType,int>을 유지할 수 있습니다. 이 색인을 추가, 제거 및 업데이트하는 작업은 모두 O (1) (상각)에서 수행 할 수 있습니다.

이로 인해 모든 작업에서 O (1) (상각 된 금액)의 복잡성이 발생합니다.

+0

확실한 삽입 (가운데는?)은 O (1)입니까? –

+0

@ GáborBakos'Add' 메소드를 사용하는'List'의 끝 부분에 있습니다. 물론 어레이를 성장시켜야한다면 더 오래 걸릴 것입니다. 그러나 여전히 O (1)이 상각되며, 적절한 초기 용량을 사용하면 어레이를 매우 자주 성장시킬 필요가 없습니다 (전혀없는 경우). – RdJ

관련 문제