Алгоритм нахождения максимальной разницы в массиве чисел

У меня есть массив из нескольких миллионов чисел.

double* const data = new double (3600000);

Мне нужно перебрать массив и найти диапазон (наибольшее значение в массиве минус наименьшее значение). Однако есть одна загвоздка. Я только хочу найти диапазон, в котором наименьшее и наибольшее значения находятся в пределах 1000 отсчетов друг от друга.

Поэтому мне нужно найти максимум: диапазон (данные + 0, данные + 1000), диапазон (данные + 1, данные + 1001), диапазон (данные + 2, данные + 1002), ...., диапазон (данные + 3599000, данные + 3600000).

Я надеюсь, что в этом есть смысл. В основном я мог бы сделать это, как указано выше, но я ищу более эффективный алгоритм, если он существует. Я думаю, что приведенный выше алгоритм - O (n), но я считаю, что его можно оптимизировать. Идея, с которой я играю, состоит в том, чтобы отслеживать самые последние максимальные и минимальные значения и их дальность, а затем возвращаться только при необходимости.

Я буду кодировать это на C++, но хороший алгоритм в псевдокоде подойдет. Кроме того, если у этого числа, которое я пытаюсь найти, есть имя, я хотел бы знать, что это такое.

Спасибо.

Более уместно, ваш алгоритм - O (O * m), где m - размер диапазона, на который вы смотрите.

David Nehme 04.10.2008 02:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
1
6 050
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

Алгоритм, который вы описываете, действительно O (N), но я думаю, что константа слишком высока. Другое решение, которое выглядит разумным, - использовать алгоритм O (N * log (N)) следующим образом:

* create sorted container (std::multiset) of first 1000 numbers
* in loop (j=1, j<(3600000-1000); ++j)
   - calculate range
   - remove from the set number which is now irrelevant (i.e. in index *j - 1* of the array)
   - add to set new relevant number  (i.e. in index *j+1000-1* of the array)

Я считаю, что это должно быть быстрее, потому что константа намного ниже.

Я думаю, что на практике это будет не быстрее, чем тривиальный метод, поскольку вы переносите сложность в управление отсортированным набором. Если реализация набора имеет какое-либо выделение памяти, это будет значительными накладными расходами.

Skizz 29.09.2008 13:14

Как людям приходят в голову эти гениальные идеи? Я бы никогда не подумал использовать набор, чтобы найти макс-мин. В любом случае это кажется простым, и ваше объяснение великолепно. Я собираюсь попробовать.

Imbue 29.09.2008 13:18

Этот алгоритм также O (N), так как поддержание набора должно занимать постоянное время, если в нем содержится 1000 элементов. Сегодня я собираюсь сравнить это с наивным решением.

Imbue 29.09.2008 22:15

Skizz - вместо выделения 1 кучи на узел с помощью std :: multiset вы можете использовать boost :: intrusive :: multiset и выделять пространство только для начальных 1000 элементов и повторно использовать пространство из удаленного элемента для вставленного элемента.

Greg Rogers 30.09.2008 00:18

Этот тип вопросов относится к ветви алгоритмов, называемых потоковыми алгоритмами. Это исследование проблем, которые требуют не только решения O (n), но также должны работать за один проход над данными. данные вводятся в алгоритм в виде потока, алгоритм не может сохранить все данные, а затем они теряются навсегда. алгоритму необходимо получить некоторый ответ о данных, такой как, например, минимум или медиана.

В частности, вы ищете максимум (или, чаще всего, в литературе - минимум) в окне над потоком.

Вот презентация на статья, который упоминает эту проблему как вспомогательную проблему того, к чему они стремятся. это может дать вам некоторые идеи.

Я думаю, что схема решения примерно такая - поддерживайте окно над потоком, где на каждом шаге один элемент вставляется в окно, а один удаляется с другой стороны (скользящее окно). Элементы, которые вы на самом деле храните в памяти, - это не все из 1000 элементов в окне, а избранные представители, которые будут хорошими кандидатами на роль минимума (или максимума).

читать статью. это немного сложно, но после 2-3 прочтений вы можете освоиться.

Это хорошее применение мин-очередь - очереди (First-In, First-Out = FIFO), которая может одновременно отслеживать минимальный элемент, который она содержит, с амортизированными обновлениями в постоянное время. Конечно, max-queue - это то же самое.

После того, как у вас есть эта структура данных, вы можете рассмотреть CurrentMax (из последних 1000 элементов) за вычетом CurrentMin, сохранить это как BestSoFar, а затем нажать новое значение, вставить старое значение и снова проверить. Таким образом, продолжайте обновлять BestSoFar, пока окончательное значение не станет решением вашего вопроса. Каждый отдельный шаг занимает амортизированное постоянное время, поэтому все является линейным, а реализация, о которой я знаю, имеет хорошую скалярную константу (она быстра).

Я не знаю какой-либо документации по min-queue - это структура данных, которую я придумал в сотрудничестве с коллегой. Вы можете реализовать это, внутренне отслеживая двоичное дерево наименьшего количества элементов в каждой непрерывной подпоследовательности ваших данных. Это упрощает проблему, заключающуюся в том, что вы будете извлекать данные только с одного конца структуры.

Если вас интересуют подробности, я могу попытаться их предоставить. Я думал написать эту структуру данных как документ для arxiv. Также обратите внимание, что Тарджан и другие ранее пришли к более мощной структуре min-deque, которая будет работать здесь, но реализация намного сложнее. Вы можете Google по запросу "mindeque", чтобы прочитать о работе Тарьяна и др.

Описываемая вами структура данных очень похожа на кучу: en.wikipedia.org/wiki/Heap_(data_structure%29

Greg Hewgill 29.09.2008 13:27

Это похоже, но не то же самое. Кучи не позволяют удалять элементы за амортизированное постоянное время.

Tyler 29.09.2008 13:41

«Каждый отдельный шаг занимает постоянное амортизированное время, поэтому все происходит линейно». Означает ли это, что, итеративно вытаскивая элемент min, вы можете сортировать элементы за линейное время?

Rafał Dowgird 29.09.2008 16:33

Нет, потому что вы можете вытолкнуть только с обратной стороны структуры, а найти - минимальный элемент (не удалять / выталкивать его). Извините, я нечетко изложил свое описание.

Tyler 30.09.2008 00:54

Идея алгоритма:

Возьмите первые 1000 значений данных и отсортируйте их. Последний в сортировке - первый диапазон (данные + 0, данные + 999) .
Затем удалите из стопки сортировки первый элемент со значением data [0]
. и добавьте данные элемента [1000]
Теперь последнее в сортировке - первое - это диапазон (данные + 1, данные + 1000) .
Повторяйте, пока не сделаете

// This should run in (DATA_LEN - RANGE_WIDTH)log(RANGE_WIDTH)
#include <set>
#include <algorithm>
using namespace std;

const int DATA_LEN = 3600000;
double* const data = new double (DATA_LEN);

....
....

const int RANGE_WIDTH = 1000;
double range = new double(DATA_LEN - RANGE_WIDTH);
multiset<double> data_set;
data_set.insert(data[i], data[RANGE_WIDTH]);

for (int i = 0 ; i < DATA_LEN - RANGE_WIDTH - 1 ; i++)
{
   range[i] = *data_set.end() - *data_set.begin();
   multiset<double>::iterator iter = data_set.find(data[i]);
   data_set.erase(iter);
   data_set.insert(data[i+1]);
}
range[i] = *data_set.end() - *data_set.begin();

// range now holds the values you seek

Вероятно, вам стоит проверить это на наличие ошибки 1, но идея есть.

std::multiset<double> range;
double currentmax = 0.0;
for (int i = 0;  i < 3600000;  ++i)
{
    if (i >= 1000)
        range.erase(range.find(data[i-1000]));
    range.insert(data[i]);
    if (i >= 999)
        currentmax = max(currentmax, *range.rbegin());
}

Примечание непроверенный код.

Редактировать: исправлена ​​одиночная ошибка.

  1. читайте в первых 1000 числах.
  2. создать связанный список из 1000 элементов, который отслеживает текущий номер 1000.
  3. создать массив из 1000 элементов указателей на узлы связанного списка, отображение 1-1.
  4. отсортировать массив указателей на основе значений узла связанного списка. Это изменит порядок массива, но сохранит связанный список без изменений.
  5. теперь вы можете вычислить диапазон для первых 1000 чисел, исследуя первый и последний элемент массива указателей.
  6. удалите первый вставленный элемент, который является либо головой, либо хвостом, в зависимости от того, как вы создали свой связанный список. Используя значение узла, выполните двоичный поиск в массиве указателей, чтобы найти указатель узла, который должен быть удален, и сдвиньте массив на единицу, чтобы удалить его.
  7. добавить 1001-й элемент в связанный список и вставить указатель на него в правильную позицию в массиве, выполнив один шаг сортировки вставкой. Это сохранит сортировку массива.
  8. теперь у вас есть мин. и макс. значение чисел от 1 до 1001 и может вычислить диапазон, используя первый и последний элемент массива указателей.
  9. Теперь должно быть очевидно, что вам нужно сделать для остальной части массива.

Алгоритм должен быть O (n), поскольку удаление и вставка ограничены log (1e3), а все остальное занимает постоянное время.

Сортировка вставкой и удаление элементов (смещение более высоких значений на одно место вниз) убьет здесь производительность - задействовано много копирования памяти, что будет основным узким местом.

Skizz 01.10.2008 15:28

Поскольку весь массив должен помещаться в кэш L2 современного процессора, я не понимаю, насколько это серьезная проблема.

freespace 02.10.2008 13:11

Я решил посмотреть, какой самый эффективный алгоритм, который я мог придумать для решения этой проблемы, заключался в использовании реального кода и фактических таймингов. Сначала я создал простое решение, которое отслеживает мин / макс для предыдущих n записей, используя кольцевой буфер, и тестовую систему для измерения скорости. В простом решении каждое значение данных сравнивается с набором минимальных / максимальных значений, так что это касается тестов window_size * count (где размер окна в исходном вопросе равен 1000, а count - 3600000).

Затем я подумал, как сделать это быстрее. Во-первых, я создал решение, в котором использовалась очередь fifo для хранения значений window_size и связанный список для хранения значений в порядке возрастания, где каждый узел в связанном списке также был узлом в очереди. Чтобы обработать значение данных, элемент в конце fifo был удален из связанного списка и очереди. Новое значение было добавлено в начало очереди, и был использован линейный поиск, чтобы найти позицию в связанном списке. Затем минимальные и максимальные значения можно было прочитать с начала и с конца связанного списка. Это было быстро, но плохо масштабировалось при увеличении window_size (т.е. линейно).

Поэтому я решил добавить в систему двоичное дерево, чтобы попытаться ускорить поисковую часть алгоритма. Окончательные тайминги для window_size = 1000 и count = 3600000 были:

Simple: 106875
Quite Complex: 1218
Complex: 1219

что было и ожидаемым, и неожиданным. Ожидалось, что использование отсортированного связного списка помогло, неожиданно в том, что накладные расходы, связанные с наличием самобалансирующегося дерева, не компенсировали преимущества более быстрого поиска. Я попробовал последние два с увеличенным размером окна и обнаружил, что они всегда были почти идентичны вплоть до window_size 100000.

Все это говорит о том, что теоретизирование алгоритмов - это одно, а их реализация - совсем другое.

В любом случае, для тех, кому интересно, вот код, который я написал (его совсем немного!):

Range.h:

#include <algorithm>
#include <iostream>
#include <ctime>

using namespace std;

//  Callback types.
typedef void (*OutputCallback) (int min, int max);
typedef int (*GeneratorCallback) ();

//  Declarations of the test functions.
clock_t Simple (int, int, GeneratorCallback, OutputCallback);
clock_t QuiteComplex (int, int, GeneratorCallback, OutputCallback);
clock_t Complex (int, int, GeneratorCallback, OutputCallback);

main.cpp:

#include "Range.h"

int
  checksum;

//  This callback is used to get data.
int CreateData ()
{
  return rand ();
}

//  This callback is used to output the results.
void OutputResults (int min, int max)
{
  //cout << min << " - " << max << endl;
  checksum += max - min;
}

//  The program entry point.
void main ()
{
  int
    count = 3600000,
    window = 1000;

  srand (0);
  checksum = 0;
  std::cout << "Simple: Ticks = " << Simple (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
  srand (0);
  checksum = 0;
  std::cout << "Quite Complex: Ticks = " << QuiteComplex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
  srand (0);
  checksum = 0;
  std::cout << "Complex: Ticks = " << Complex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
}

Simple.cpp:

#include "Range.h"

//  Function to actually process the data.
//  A circular buffer of min/max values for the current window is filled
//  and once full, the oldest min/max pair is sent to the output callback
//  and replaced with the newest input value. Each value inputted is 
//  compared against all min/max pairs.
void ProcessData
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output,
  int *min_buffer,
  int *max_buffer
)
{
  int
    i;

  for (i = 0 ; i < window ; ++i)
  {
    int
      value = input ();

    min_buffer [i] = max_buffer [i] = value;

    for (int j = 0 ; j < i ; ++j)
    {
      min_buffer [j] = min (min_buffer [j], value);
      max_buffer [j] = max (max_buffer [j], value);
    }
  }

  for ( ; i < count ; ++i)
  {
    int
      index = i % window;

    output (min_buffer [index], max_buffer [index]);

    int
      value = input ();

    min_buffer [index] = max_buffer [index] = value;

    for (int k = (i + 1) % window ; k != index ; k = (k + 1) % window)
    {
      min_buffer [k] = min (min_buffer [k], value);
      max_buffer [k] = max (max_buffer [k], value);
    }
  }

  output (min_buffer [count % window], max_buffer [count % window]);
}

//  A simple method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t Simple
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  int
    *min_buffer = new int [window],
    *max_buffer = new int [window];

  clock_t
    start = clock ();

  ProcessData (count, window, input, output, min_buffer, max_buffer);

  clock_t
    end = clock ();

  delete [] max_buffer;
  delete [] min_buffer;

  return end - start;
}

QuiteComplex.cpp:

#include "Range.h"

template <class T>
class Range
{
private:
  //  Class Types

  //  Node Data
  //  Stores a value and its position in various lists.
  struct Node
  {
    Node
      *m_queue_next,
      *m_list_greater,
      *m_list_lower;

    T
      m_value;
  };

public:
  //  Constructor
  //  Allocates memory for the node data and adds all the allocated
  //  nodes to the unused/free list of nodes.
  Range
  (
    int window_size
  ) :
    m_nodes (new Node [window_size]),
    m_queue_tail (m_nodes),
    m_queue_head (0),
    m_list_min (0),
    m_list_max (0),
    m_free_list (m_nodes)
  {
    for (int i = 0 ; i < window_size - 1 ; ++i)
    {
      m_nodes [i].m_list_lower = &m_nodes [i + 1];
    }

    m_nodes [window_size - 1].m_list_lower = 0;
  }

  //  Destructor
  //  Tidy up allocated data.
  ~Range ()
  {
    delete [] m_nodes;
  }

  //  Function to add a new value into the data structure.
  void AddValue
  (
    T value
  )
  {
    Node
      *node = GetNode ();

    //  clear links
    node->m_queue_next = 0;

    //  set value of node
    node->m_value = value;

    //  find place to add node into linked list
    Node
      *search;

    for (search = m_list_max ; search ; search = search->m_list_lower)
    {
      if (search->m_value < value)
      {
        if (search->m_list_greater)
        {
          node->m_list_greater = search->m_list_greater;
          search->m_list_greater->m_list_lower = node;
        }
        else
        {
          m_list_max = node;
        }

        node->m_list_lower = search;
        search->m_list_greater = node;
      }
    }

    if (!search)
    {
      m_list_min->m_list_lower = node;
      node->m_list_greater = m_list_min;
      m_list_min = node;
    }
  }

  //  Accessor to determine if the first output value is ready for use.
  bool RangeAvailable ()
  {
    return !m_free_list;
  }

  //  Accessor to get the minimum value of all values in the current window.
  T Min ()
  {
    return m_list_min->m_value;
  }

  //  Accessor to get the maximum value of all values in the current window.
  T Max ()
  {
    return m_list_max->m_value;
  }

private:
  //  Function to get a node to store a value into.
  //  This function gets nodes from one of two places:
  //    1. From the unused/free list
  //    2. From the end of the fifo queue, this requires removing the node from the list and tree
  Node *GetNode ()
  {
    Node
      *node;

    if (m_free_list)
    {
      //  get new node from unused/free list and place at head
      node = m_free_list;

      m_free_list = node->m_list_lower;

      if (m_queue_head)
      {
        m_queue_head->m_queue_next = node;
      }

      m_queue_head = node;
    }
    else
    {
      //  get node from tail of queue and place at head
      node = m_queue_tail;

      m_queue_tail = node->m_queue_next;
      m_queue_head->m_queue_next = node;
      m_queue_head = node;

      //  remove node from linked list
      if (node->m_list_lower)
      {
        node->m_list_lower->m_list_greater = node->m_list_greater;
      }
      else
      {
        m_list_min = node->m_list_greater;
      }

      if (node->m_list_greater)
      {
        node->m_list_greater->m_list_lower = node->m_list_lower;
      }
      else
      {
        m_list_max = node->m_list_lower;
      }
    }

    return node;
  }

  //  Member Data.
  Node
    *m_nodes,
    *m_queue_tail,
    *m_queue_head,
    *m_list_min,
    *m_list_max,
    *m_free_list;
};

//  A reasonable complex but more efficent method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t QuiteComplex
(
  int size,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  Range <int>
    range (window);

  clock_t
    start = clock ();

  for (int i = 0 ; i < size ; ++i)
  {   
    range.AddValue (input ());

    if (range.RangeAvailable ())
    {
      output (range.Min (), range.Max ());
    }
  }

  clock_t
    end = clock ();

  return end - start;
}

Complex.cpp:

#include "Range.h"

template <class T>
class Range
{
private:
  //  Class Types

  //  Red/Black tree node colours.
  enum NodeColour
  {
    Red,
    Black
  };

  //  Node Data
  //  Stores a value and its position in various lists and trees.
  struct Node
  {
    //  Function to get the sibling of a node.
    //  Because leaves are stored as null pointers, it must be possible
    //  to get the sibling of a null pointer. If the object is a null pointer
    //  then the parent pointer is used to determine the sibling.
    Node *Sibling
    (
      Node *parent
    )
    {
      Node
        *sibling;

      if (this)
      {
        sibling = m_tree_parent->m_tree_less == this ? m_tree_parent->m_tree_more : m_tree_parent->m_tree_less;
      }
      else
      {
        sibling = parent->m_tree_less ? parent->m_tree_less : parent->m_tree_more;
      }

      return sibling;
    }

    //  Node Members
    Node
      *m_queue_next,
      *m_tree_less,
      *m_tree_more,
      *m_tree_parent,
      *m_list_greater,
      *m_list_lower;

    NodeColour
      m_colour;

    T
      m_value;
  };

public:
  //  Constructor
  //  Allocates memory for the node data and adds all the allocated
  //  nodes to the unused/free list of nodes.
  Range
  (
    int window_size
  ) :
    m_nodes (new Node [window_size]),
    m_queue_tail (m_nodes),
    m_queue_head (0),
    m_tree_root (0),
    m_list_min (0),
    m_list_max (0),
    m_free_list (m_nodes)
  {
    for (int i = 0 ; i < window_size - 1 ; ++i)
    {
      m_nodes [i].m_list_lower = &m_nodes [i + 1];
    }

    m_nodes [window_size - 1].m_list_lower = 0;
  }

  //  Destructor
  //  Tidy up allocated data.
  ~Range ()
  {
    delete [] m_nodes;
  }

  //  Function to add a new value into the data structure.
  void AddValue
  (
    T value
  )
  {
    Node
      *node = GetNode ();

    //  clear links
    node->m_queue_next = node->m_tree_more = node->m_tree_less = node->m_tree_parent = 0;

    //  set value of node
    node->m_value = value;

    //  insert node into tree
    if (m_tree_root)
    {
      InsertNodeIntoTree (node);
      BalanceTreeAfterInsertion (node);
    }
    else
    {
      m_tree_root = m_list_max = m_list_min = node;
      node->m_tree_parent = node->m_list_greater = node->m_list_lower = 0;
    }

    m_tree_root->m_colour = Black;
  }

  //  Accessor to determine if the first output value is ready for use.
  bool RangeAvailable ()
  {
    return !m_free_list;
  }

  //  Accessor to get the minimum value of all values in the current window.
  T Min ()
  {
    return m_list_min->m_value;
  }

  //  Accessor to get the maximum value of all values in the current window.
  T Max ()
  {
    return m_list_max->m_value;
  }

private:
  //  Function to get a node to store a value into.
  //  This function gets nodes from one of two places:
  //    1. From the unused/free list
  //    2. From the end of the fifo queue, this requires removing the node from the list and tree
  Node *GetNode ()
  {
    Node
      *node;

    if (m_free_list)
    {
      //  get new node from unused/free list and place at head
      node = m_free_list;

      m_free_list = node->m_list_lower;

      if (m_queue_head)
      {
        m_queue_head->m_queue_next = node;
      }

      m_queue_head = node;
    }
    else
    {
      //  get node from tail of queue and place at head
      node = m_queue_tail;

      m_queue_tail = node->m_queue_next;
      m_queue_head->m_queue_next = node;
      m_queue_head = node;

      //  remove node from tree
      node = RemoveNodeFromTree (node);
      RebalanceTreeAfterDeletion (node);

      //  remove node from linked list
      if (node->m_list_lower)
      {
        node->m_list_lower->m_list_greater = node->m_list_greater;
      }
      else
      {
        m_list_min = node->m_list_greater;
      }

      if (node->m_list_greater)
      {
        node->m_list_greater->m_list_lower = node->m_list_lower;
      }
      else
      {
        m_list_max = node->m_list_lower;
      }
    }

    return node;
  }

  //  Rebalances the tree after insertion
  void BalanceTreeAfterInsertion
  (
    Node *node
  )
  {
    node->m_colour = Red;

    while (node != m_tree_root && node->m_tree_parent->m_colour == Red)
    {
      if (node->m_tree_parent == node->m_tree_parent->m_tree_parent->m_tree_more)
      {
        Node
          *uncle = node->m_tree_parent->m_tree_parent->m_tree_less;

        if (uncle && uncle->m_colour == Red)
        {
          node->m_tree_parent->m_colour = Black;
          uncle->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          node = node->m_tree_parent->m_tree_parent;
        }
        else
        {
          if (node == node->m_tree_parent->m_tree_less)
          {
            node = node->m_tree_parent;
            LeftRotate (node);
          }

          node->m_tree_parent->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          RightRotate (node->m_tree_parent->m_tree_parent);
        }
      }
      else
      {
        Node
          *uncle = node->m_tree_parent->m_tree_parent->m_tree_more;

        if (uncle && uncle->m_colour == Red)
        {
          node->m_tree_parent->m_colour = Black;
          uncle->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          node = node->m_tree_parent->m_tree_parent;
        }
        else
        {
          if (node == node->m_tree_parent->m_tree_more)
          {
            node = node->m_tree_parent;
            RightRotate (node);
          }

          node->m_tree_parent->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          LeftRotate (node->m_tree_parent->m_tree_parent);
        }
      }
    }
  }

  //  Adds a node into the tree and sorted linked list
  void InsertNodeIntoTree
  (
    Node *node
  )
  {
    Node
      *parent = 0,
      *child = m_tree_root;

    bool
      greater;

    while (child)
    {
      parent = child;
      child = (greater = node->m_value > child->m_value) ? child->m_tree_more : child->m_tree_less;
    }

    node->m_tree_parent = parent;

    if (greater)
    {
      parent->m_tree_more = node;

      //  insert node into linked list
      if (parent->m_list_greater)
      {
        parent->m_list_greater->m_list_lower = node;
      }
      else
      {
        m_list_max = node;
      }

      node->m_list_greater = parent->m_list_greater;
      node->m_list_lower = parent;
      parent->m_list_greater = node;
    }
    else
    {
      parent->m_tree_less = node;

      //  insert node into linked list
      if (parent->m_list_lower)
      {
        parent->m_list_lower->m_list_greater = node;
      }
      else
      {
        m_list_min = node;
      }

      node->m_list_lower = parent->m_list_lower;
      node->m_list_greater = parent;
      parent->m_list_lower = node;
    }
  }

  //  Red/Black tree manipulation routine, used for removing a node
  Node *RemoveNodeFromTree
  (
    Node *node
  )
  {
    if (node->m_tree_less && node->m_tree_more)
    {
      //  the complex case, swap node with a child node
      Node
        *child;

      if (node->m_tree_less)
      {
        // find largest value in lesser half (node with no greater pointer)
        for (child = node->m_tree_less ; child->m_tree_more ; child = child->m_tree_more)
        {
        }
      }
      else
      {
        // find smallest value in greater half (node with no lesser pointer)
        for (child = node->m_tree_more ; child->m_tree_less ; child = child->m_tree_less)
        {
        }
      }

      swap (child->m_colour, node->m_colour);

      if (child->m_tree_parent != node)
      {
        swap (child->m_tree_less, node->m_tree_less);
        swap (child->m_tree_more, node->m_tree_more);
        swap (child->m_tree_parent, node->m_tree_parent);

        if (!child->m_tree_parent)
        {
          m_tree_root = child;
        }
        else
        {
          if (child->m_tree_parent->m_tree_less == node)
          {
            child->m_tree_parent->m_tree_less = child;
          }
          else
          {
            child->m_tree_parent->m_tree_more = child;
          }
        }

        if (node->m_tree_parent->m_tree_less == child)
        {
          node->m_tree_parent->m_tree_less = node;
        }
        else
        {
          node->m_tree_parent->m_tree_more = node;
        }
      }
      else
      {
        child->m_tree_parent = node->m_tree_parent;
        node->m_tree_parent = child;

        Node
          *child_less = child->m_tree_less,
          *child_more = child->m_tree_more;

        if (node->m_tree_less == child)
        {
          child->m_tree_less = node;
          child->m_tree_more = node->m_tree_more;
          node->m_tree_less = child_less;
          node->m_tree_more = child_more;
        }
        else
        {
          child->m_tree_less = node->m_tree_less;
          child->m_tree_more = node;
          node->m_tree_less = child_less;
          node->m_tree_more = child_more;
        }

        if (!child->m_tree_parent)
        {
          m_tree_root = child;
        }
        else
        {
          if (child->m_tree_parent->m_tree_less == node)
          {
            child->m_tree_parent->m_tree_less = child;
          }
          else
          {
            child->m_tree_parent->m_tree_more = child;
          }
        }
      }

      if (child->m_tree_less)
      {
        child->m_tree_less->m_tree_parent = child;
      }

      if (child->m_tree_more)
      {
        child->m_tree_more->m_tree_parent = child;
      }

      if (node->m_tree_less)
      {
        node->m_tree_less->m_tree_parent = node;
      }

      if (node->m_tree_more)
      {
        node->m_tree_more->m_tree_parent = node;
      }
    }

    Node
      *child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;

    if (node->m_tree_parent->m_tree_less == node)
    {
      node->m_tree_parent->m_tree_less = child;
    }
    else
    {
      node->m_tree_parent->m_tree_more = child;
    }

    if (child)
    {
      child->m_tree_parent = node->m_tree_parent;
    }

    return node;
  }

  //  Red/Black tree manipulation routine, used for rebalancing a tree after a deletion
  void RebalanceTreeAfterDeletion
  (
    Node *node
  )
  {
    Node
      *child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;

    if (node->m_colour == Black)
    {
      if (child && child->m_colour == Red)
      {
        child->m_colour = Black;
      }
      else
      {
        Node
          *parent = node->m_tree_parent,
          *n = child;

        while (parent)
        {
          Node
            *sibling = n->Sibling (parent);

          if (sibling && sibling->m_colour == Red)
          {
            parent->m_colour = Red;
            sibling->m_colour = Black;

            if (n == parent->m_tree_more)
            {
              LeftRotate (parent);
            }
            else
            {
              RightRotate (parent);
            }
          }

          sibling = n->Sibling (parent);

          if (parent->m_colour == Black &&
            sibling->m_colour == Black &&
            (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
            (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
          {
            sibling->m_colour = Red;
            n = parent;
            parent = n->m_tree_parent;
            continue;
          }
          else
          {
            if (parent->m_colour == Red &&
              sibling->m_colour == Black &&
              (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
              (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
            {
              sibling->m_colour = Red;
              parent->m_colour = Black;
              break;
            }
            else
            {
              if (n == parent->m_tree_more &&
                sibling->m_colour == Black &&
                (sibling->m_tree_more && sibling->m_tree_more->m_colour == Red) &&
                (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
              {
                sibling->m_colour = Red;
                sibling->m_tree_more->m_colour = Black;
                RightRotate (sibling);
              }
              else
              {
                if (n == parent->m_tree_less &&
                  sibling->m_colour == Black &&
                  (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
                  (sibling->m_tree_less && sibling->m_tree_less->m_colour == Red))
                {
                  sibling->m_colour = Red;
                  sibling->m_tree_less->m_colour = Black;
                  LeftRotate (sibling);
                }
              }

              sibling = n->Sibling (parent);
              sibling->m_colour = parent->m_colour;
              parent->m_colour = Black;

              if (n == parent->m_tree_more)
              {
                sibling->m_tree_less->m_colour = Black;
                LeftRotate (parent);
              }
              else
              {
                sibling->m_tree_more->m_colour = Black;
                RightRotate (parent);
              }
              break;
            }
          }
        }
      }
    }
  }

  //  Red/Black tree manipulation routine, used for balancing the tree
  void LeftRotate
  (
    Node *node
  )
  {
    Node
      *less = node->m_tree_less;

    node->m_tree_less = less->m_tree_more;

    if (less->m_tree_more)
    {
      less->m_tree_more->m_tree_parent = node;
    }

    less->m_tree_parent = node->m_tree_parent;

    if (!node->m_tree_parent)
    {
      m_tree_root = less;
    }
    else
    {
      if (node == node->m_tree_parent->m_tree_more)
      {
        node->m_tree_parent->m_tree_more = less;
      }
      else
      {
        node->m_tree_parent->m_tree_less = less;
      }
    }

    less->m_tree_more = node;
    node->m_tree_parent = less;
  }

  //  Red/Black tree manipulation routine, used for balancing the tree
  void RightRotate
  (
    Node *node
  )
  {
    Node
      *more = node->m_tree_more;

    node->m_tree_more = more->m_tree_less;

    if (more->m_tree_less)
    {
      more->m_tree_less->m_tree_parent = node;
    }

    more->m_tree_parent = node->m_tree_parent;

    if (!node->m_tree_parent)
    {
      m_tree_root = more;
    }
    else
    {
      if (node == node->m_tree_parent->m_tree_less)
      {
        node->m_tree_parent->m_tree_less = more;
      }
      else
      {
        node->m_tree_parent->m_tree_more = more;
      }
    }

    more->m_tree_less = node;
    node->m_tree_parent = more;
  }

  //  Member Data.
  Node
    *m_nodes,
    *m_queue_tail,
    *m_queue_head,
    *m_tree_root,
    *m_list_min,
    *m_list_max,
    *m_free_list;
};

//  A complex but more efficent method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t Complex
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  Range <int>
    range (window);

  clock_t
    start = clock ();

  for (int i = 0 ; i < count ; ++i)
  {   
    range.AddValue (input ());

    if (range.RangeAvailable ())
    {
      output (range.Min (), range.Max ());
    }
  }

  clock_t
    end = clock ();

  return end - start;
}

Другие вопросы по теме