2014-09-20 5 views
1

정렬 된 대기열을 유지하려는 경우 가장 낮은 값 (비트가 std::priority_queue 인 항목)을 팝업 할 수 있기를 원합니다. 항목 값은 연속적이며 계속 증가합니다. 그러나 항목의 비교가 정의 된 것보다 작지 않으며 is-previousis-next 술어 (A is-previous B ==) 만 있고 ++ 또는 --도 지원합니다.소계가 아닌 술어가없는 정렬 된 순서 유지

이러한 술어를 기반으로 (부분적으로) 값을 정렬하는 알고리즘이 있는지 궁금합니다. 또는 대기열을 추적하는 더 좋은 방법이 있습니까?

이 문제는 모듈로 산술로 작업 할 때 발생합니다. 즉, 항목이 정수로 지정되었지만 모듈로 인해 의미가 작아지고 더 이상 중요하므로 더 이상 정렬 할 수 없습니다.

선택 언어는 C++이지만 대부분의 합리적인 언어를 포팅 할 수 있습니다.

편집 예제 코드를 작성하면서 답변을 위해 노력하는 모든 사람들에게

Appologies, 나는 원래의 질문이 잘못 형성 깨달았다. 정말로 embarasing하고 있습니다 만, 나는 당신의 시간이 낭비되지 않았 음을 강조하고 싶습니다. 제가 한 시간 동안 땀을 흘렸을 때, 그리고 나서 10 분이 지나면 어떻게 잘못되었는지 깨달았습니다. 어쨌든,이 행동이 내가 원하는입니다 :

template <class T> 
class OrderedQueue { 
    std::vector<T> storage; 
    T next; 

public: 
    OrderedQueue(T lowest_value = T()) 
     :next(lowest_value) 
    {} 

    void Push(T t) 
    { 
     storage.push_back(t); 
    } 

    T Pop() 
    { 
     std::vector<T>::iterator it = std::find(storage.begin(), storage.end(), next); 
     if(it == storage.end()) 
      throw std::runtime_error("no such element"); 
     T value = *it; 
     storage.erase(it); 
     ++ next; 
     return value; 
    } 
}; 

내 문제는 Pop()O(N)를 취한다는 것입니다. is-prev와 is-next 술어를 사용하여 더 빨리 만들 수있는 방법이 있습니까?

+0

왜 정렬해야합니까? 중간에 아무 것도 추가 할 수 없습니다! – Basilevs

+0

@Basilevs 정렬 된 배열을 유지하는 것이 일반적으로 정렬되지 않은 배열보다 훨씬 빠르기 때문에 정렬하고 싶습니다. 어쨌든'std :: priority_queue'를 사용할 수 없을 것입니다. 왜냐하면 그것은 술어보다 적은 수를 필요로하기 때문입니다. 값을 저장하기 위해'std :: vector'를 사용할 것이고, 거기에서'vector.insert (where, 1, what)'을 쉽게 할 수 있습니다. –

+0

deque를 감싸고 시작과 끝을 제외한 모든 삽입을 허용하지 않습니다. – Basilevs

답변

1

제 문제는 Pop()이 O (N)을 사용한다는 것입니다. is-prev와 is-next 술어를 사용하여 더 빨리 만들 수있는 방법이 있습니까?

키의 실제 모듈화 정수 표현 (예 : Integer(x))을 얻는 방법을 제공해야합니다. 그런 다음 PushPop 둘 다 일정 시간 내에 수행 할 수 있습니다.

당신은 정수와 계수 M이있는 경우, 큰

size_t i = Integer(next); 
// check for existence, raise if necessary 
T r = queues[i].front(); 
queues[i].pop_front(); 
return r; 

M 경우로 queues[Integer(x)].push_back(x)으로 Push(x)Pop을 구현, 이중 연결리스트 (std::list<T> queues[M])의 배열을 사용하고 대부분의 기대 대기열이 대부분 비어 있으면 std::unordered_map<size_t, std::list<T>>을 사용하여 동일한 복잡성을 얻지 만 공간을 절약 할 수 있습니다.

std::deque도 사용될 수있다. 두 작업에 대해 상각 된 일정 시간 만 제공하지만 실제로는 훨씬 빨라질 수 있습니다.

+0

큐마다 최대 하나의 정수를 항상 저장하지는 않습니다 (반복하지 않지만 꾸준히 증가한다고 가정하면 구현에 최악의 시퀀스가 ​​적용됩니까?). 나는 이것이 효과가있을 것이라고 동의하지만, 그것은 비효율적 일 것입니다! 만약'M = 2^32'이라면? 정수 표현을 얻을 수 있다면'std :: set' 또는'std :: map'을 사용하여'Push()'와'Pop()'을 대수 시간으로 만들 수 있습니다. –

+0

@theswine M이 큰 경우 'unordered_map >'을 사용하여 일정 시간 누름 및 팝을 얻습니다. 업데이트 된 답변보기 –

+0

큰 마진이 아님에도 불구하고 이것은 실제로 가장 빠른 해결책입니다. 질문에 설명 된'OrderedQueue'에 100k 요소를 삽입하는 것은'std :: unordered_map'을 사용하는 것보다 약 100msec 더 느립니다. 그것은 더 나은 컴파일러 또는 더 나은 STL 구현으로 향상 될 수 있습니다. –

0

C++에서 형식을 정렬하거나 연관 컨테이너로 사용하기 위해 operator<을 정의 할 필요가 없습니다. A 테이너와 알고리즘은 비교를 수행하는 술어를 사용할 수 있습니다.

std::sort(v.begin(), v.end(), is_pred); 

하지 않으면, 당신은 당신의 자신의 펑 어댑터 또는 람다를 작성할 수 있습니다 : 예를 들어, (이 함수 객체 또는 일반 함수가 이미 가정) 술어로 is_pred를 사용하여 벡터를 정렬하려면

std::sort(v.begin(), v.end(), [](T const& lhs, T const& rhs) { 
      return apply_is_pred_predicate(lhs,rhs) 
      }); 

여기의 요구 사항은 이 -prev 인 관계가 엄격한 약한 순서 여야한다는 것입니다.

+0

설명 된 술어가 '엄격한 약한 순서 지정'이 아니기 때문에 가능합니다. 하지만 고마워. –

+0

예, is_previous가 명확하게 약한 명령을 정의하지 않습니다. –