En un array, массив ¿ mejor método de actualizar índices al propio?

Llevo un par de días dándole vueltas esto:

Tengo el siguiente código:

struct Dato {
  char valor;
  int referido1, referido2;
};

Dato *array;

Довод "против" Y construyo un arreglo en memoria la siguiente estructura:

[{'a',-1,-1},{'a',2,3},{'c',4,5},{'c',0,-1},{'d',-1,-1},{'e',-1,-1}]

Комо se ve, es una lista de Dato, ordenada por su бразильская саванна valor.

Лос Кампус referido1 y referido2 hacen referencia los índices del propio arreglo: array[2].referido1 == 4 -> array[4].

Лос datos сын реалов aleatorios, pero el arreglo mantiene su estructura; siempre estará ordenado por Эль-Кампо valor. Además, танго que permitir la introducción dinámica de datos.

Ahora, el problema. Вставка Al ООН nuevo Dato подставьте su бразильскую саванну valor = 'b', quedaría así:

[{'a',-1,-1},{'a',2,3},{'b',-1,-1},{'c',4,5},{'c',0,-1},{'d',-1,-1},{'e',-1,-1}]

Esto я invalida los índices de varios de los Dato подарки ya. En este ejemplo, invalidaría Лос índices en [1], [4], [5], ...

Actualmente, Лос reconstruyo mediante ООН while( ), recorriendo todo el arreglo, comprobando si el índice apunta un elemento de índice следующий o igual al insertado; СИ es asi, añado 1 al índice correspondiente.

La pregunta es: ¿ alguna форма mas rápida, que никакой implique recorrer todo el arreglo? La struct Dato se puede modificar, pero Лос Кампус indicados сын obligatorios; puedo añadir, pero никакой eliminar.

Admito respuestas en C, C++, y имеет генералам идей, siempre que las entienda, claro.

EDITO

Esto está relacionado подставляют довод "против" la pregunta ¿Cómo recorrer un árbol binario buscando nodos el mismo dato? y ми respuesta а-ля misma. Умственный La respuesta en si es una masturbación, extremadamente Лента en la inserción de elementos, pero muy rápida al buscarlos y contarlos.

Я quedó la duda de si sería возможный mejorar алгоритм la inserción, оригиналы manteniendo mas o menos las ideas expuestas en la pregunta y en la respuesta: соло usar ООН std::vector, грешите estructuras de datos adicionales, y que el elemento Dato морской алгоритм parecido un nodo en un árbol binario.

El código fuente utilizado en mis pruebas; es ООН armazón fácilmente modificable. Нет он atinado hacerlo mas corto. Esta realizado en C ++, y компилируют довод "против"

g ++-std=c ++ 98 - Стена

#include <vector>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstddef>
#include <cstdlib> // rand( ) y srand( ).

struct Data {
  int value;
  std::ptrdiff_t left, right;

  Data( int v );
  bool operator<( const Data &o ) const { return value < o.value; }
};

// Facilita la lectura.
typedef std::vector< Data > VectorType;
typedef VectorType::iterator Iterator;

// Auxiliar para indicar quién es el padre.
// iter -> iterador al padre.
// left -> 'true' o 'false'.
struct ParentPos {
  Iterator iter;
  bool left;

  ParentPos( ) : iter( ), left( false ) { }
  ParentPos( const Iterator &i, bool l ) : iter( i ), left( l ) { }
  ParentPos &operator=( const ParentPos &o ) {
    iter = o.iter;
    left = o.left;
    return *this;
  }
};

VectorType Vector;
static unsigned Steps = 0; // Para comparar los resultados.

// Para volcado del vector.
std::ostream &operator<<( std::ostream &, const Data & );
// Las posibles optimizaciones
void PreInsert( Iterator &, const Data &, ParentPos &);
void PostInsert( Iterator &, const Data &, ParentPos & );

// Por si queremos volcar el vector durante las pruebas.
void Dump( std::ostream &os = std::cout ) {
  Iterator iter = Vector.begin( );

  while( iter != Vector.end( ) ) {
    os << *iter << std::endl;
    ++ iter;
  }
}

// Seleciona aleatoriamente quién será el padre del dato.
// Recibe el ÍNDICE en el que se insertará.
// Modifica el padre para que referencie al hijo.
// Devuelve un 'Iterator' al padre.
ParentPos SelectParent( ptrdiff_t pos ) {
  bool left = rand( ) & 1; // ¿ hijo left o right ?
  Iterator iter = Vector.begin( );

  while( true ) {
    if( left && ( iter->left == -1 ) ) {
      iter->left = pos;
      break;
    }

    if( ( !left ) && ( iter->right == -1 ) ) {
      iter->right = pos;
      break;
    }

    ++iter;
  }

  return ParentPos( iter, left );
}

void Insert( int v ) {
  Data aux( v );
  ParentPos pp;
  Iterator END;
  Iterator BEGIN;
  Iterator iter;

  std::cout << "Añadido " << v << " en la pos ";

  // Para evitar invalidar los iteradores, nos aseguramos
  // de que tengamos espacio para 1 elemento mas.
  Vector.reserve( Vector.size( ) + 1 );

  END = Vector.end( );
  BEGIN = Vector.begin( );

  // Buscamos el final de su grupo.
  iter = std::upper_bound( BEGIN, END, aux );

  std::cout << std::distance( BEGIN, iter );

  // Si 'Vector' NO ESTÁ VACÍO, escogemos un padre.
  if( !Vector.empty( ) ) {
    pp = SelectParent( std::distance( BEGIN, iter ) );
    std::cout << " al padre " << pp.iter->value;
  }

  std::cout << std::endl;

  // Caso FÁCIL; añadir al final del vector.
  // NO cambia el orden relativo de los enlaces.
  // NO es necesario optimizar nada.
  if( iter == END ) {
    Vector.push_back( aux );
  } else {
  // Caso normal. SE INSERTA EN ALGÚN PUNTO DISTINTO DEL FINAL.
    PreInsert( iter, aux, pp );
    Vector.insert( iter, aux );
    PostInsert( iter, aux, pp );
  }
}

int main( void ) {
  // int count = 4; // 1 mas de los que queramos.

  // Para tener resultados reproducibles.
  srand( 0 );
  // Borrar la pantalla (¡ Linux !)
  std::cout << "\033c";

//  while( --count )
//    Insert( rand( ) % 10 );
  Insert( 3 );
  Insert( 6 );
  Dump( );
  Insert( 5 );
  Dump( );

  std::cout << std::endl;
  std::cout << "Pasos: " << Steps << std::endl;

  return 0;
}

/***********************
*                      *
* Constructor de Data. *
*                      *
************************/
Data::Data( int v ) :
  value( v ),
  left( -1 ),
  right( -1 )
{

}
/*********************************************************
*                                                        *
* Método a implementar para el volcado de cada elemento. *
*                                                        *
**********************************************************/
std::ostream &operator<<( std::ostream &os, const Data &dat ) {
  os << " Value: ";
  os << std::setw( 2 ) << std::setfill( '0' ) << dat.value;
  os << " [" << dat.left << "][" << dat.right << "]";

  // La función 'Dump' añade al final un 'std::endl'.
  return os;
}

/*********************************************************
*                                                        *
*  Funciónes a reimplementar para buscar optimizaciones. *
*                                                        *
**********************************************************/
// pos -> Posición en la que se insertará.
// d -> Dato.
// parent -> padre.
void PreInsert( Iterator &pos, const Data &d, ParentPos &parent ) {
  Iterator iter = Vector.begin( );
  ptrdiff_t relative = std::distance( Vector.begin( ), pos );

  while( iter != Vector.end( ) ) {
    // Para mantener el orden relativo de los índices.
    if( parent.iter == iter ) {
      if( parent.left ) {
        if( iter->right >= relative )
          ++iter->right;
      } else {
        if( iter->left >= relative )
          ++iter->left;
      }

      ++iter;
      continue;
    }

    if( iter != pos ) {
      if( iter->left >= relative )
        ++iter->left;

      if( iter->right >= relative )
        ++iter->right;
    }

    ++iter;
  }
}

// pos -> Posición en la que se insertó.
// d -> dato.
// parent -> padre.
void PostInsert( Iterator &pos, const Data &d, ParentPos &parent ) {

}

Ese código recorre el std::vector completo en cada nueva inserción.

4
задан 13.04.2017, 16:00
0 ответов

Существует способ реализовывать этот процесс супер работоспособного способа, но достаточно обременительная и я не думаю, что он реальный, объяснять ее здесь. Если ты хочешь, дай ему беглый взгляд структуре передовых данных ABI (бинарное индексированное Дерево), или БИТ, из-за Ваших инициалов на английском. Мое самое простое предложение состояло бы в том, чтобы вместо того, чтобы сохранять предпочтенных как индексы договоренности, ты сохранял их как указатели в элементы, которые ты хочешь, и так у тебя будет всегда ссылка, независимо от места, которое он занимает внутри договоренности. Оно было бы чем-либо подобным:

struct Dato {
    char valor;
    Dato *preferido1, *preferido2;
}

Это известно, как он располагает в порядке autorreferenciada, так как, как Ваше имя это показывает, у него есть ссылки на элементы Вашего собственного типа в Вашем внутреннем помещении.

ЗАМЕТЬ: Ты должен быть осторожен, в случае когда используешь этот метод, с частью динамического ассигнования памяти, учитывая всегда, что ты должен освобождать все место, которое ты разместил в течение выполнения программы.

Я надеюсь, что он был полезен.

1
ответ дан 03.12.2019, 17:36
  • 1
    Большое спасибо, @IsaacVega, но это - точно то, что я не могу делать. Из-за беспокойства, и отсутствия указаний со своей стороны, +1. История приходит aquí: es.stackoverflow.com/questions/13734/… и то, к чему я стремлюсь, состоит в том, чтобы ускорять вставления, которые O (n). Я смотрю ABI. –  25.01.2017, 20:56

Обычно, когда ты оптимизируешь в скорости, ты наказываешь в потреблении памяти и наоборот и это такое, потому что, часто, оптимизировать в скорости - что-то, что получают предвычисляя информацию и сохраняя временные результаты.

Альтернативная форма в твой настоящий алгоритм ты она можешь находить mapeando вышеупомянутых. А именно, представь себе то, что узлы 3 и 4 имеют по отношению к узлу 6, как отнесенный, в этой карте будет два реестра, которые они покажут, что, если узел 6 переносит изменение индекса, будет нужно предупреждать в узлы 3 и 4:

// Clave -> Indice afectado
// Valor -> Lista de indices a actualizar
std::vector<std::vector<int>> mapeoReferidos;
Dato *array;

Тогда, введя элемент, оперативная осталась бы такой:

void ElementoAInsertar(int indice)
{
  std::for_each(std::next(mapeoReferidos.begin(),indice),
                mapeoReferidos.end(),
  [&](const std::vector<int>& referidos)
  {
    for( int indiceRef : referidos )
    {
      // No me gusta repetir codigo... es malo para el negocio
      auto lambda = [](int& referido, int indice)
      { if( referido == indice ) referido = indice+1; };

      Dato* dato = array[indiceRef];
      lambda(dato->referido1,indice);
      lambda(dato->referido2,indice);
    }
  });
}

Сосредоточись на имени функции. Подумайся, чтобы быть названной, непосредственно перед введя новый элемент. Как только был введен элемент индексы уже изменились и операции мне кажутся немножко более некомфортабельными.

Я выбрал реализовывать mapeo с укрытыми векторами, потому что идея индекс каждого элемента совпал в array и в mapeoReferidos. А именно, mapeoReferidos[i] он был бы должен возвращать тебе список узлов, которые ссылаются на узел, помещенный в datos[i].

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

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

Предполагаемое преимущество состоит в том, что ты сейчас не должен пробегать весь вектор ища совпадения..., и в том, что ты не должен изменять структуру Dato.

2
ответ дан 03.12.2019, 17:36
  • 1
    Он для вопроса á rbol, и я не хочу превратить ее в catá logo решений на optimizació n. Я conformarí когда, с может оптимизировать из более или менее простой формы цикл actualizació n í ndices, но я верю в то, что не será возможный, без, как ты говоришь, увеличивать использование памяти. С другой стороны, большой ответ, как всегда. Более одного aprovechará. +1 –  20.01.2017, 15:47
  • 2
    @Trauma podrí схвати интегрировать вектор в структуру. Así каждый узел tendrí в вектор с í ndices узлов, которые относятся в..., но он мне кажется diseñ или немного má s обременительный. –  20.01.2017, 15:48
  • 3
    Я думаю, что он не стоит. Сумма nodos + lista de referentes serí в почти равный общему количеству узлов. Остаточный прогресс, в comparació n с использованием памяти. В конце концов, я думаю, что нет многого возможного прогресса, не используя других добавочных структур. –  20.01.2017, 16:04
  • 4
    Исходя из твоей идеи, у меня есть что-то. Если я помещаю признак ' padrВ в каждом узле, так как у каждого одинокого узла есть 1 отец, мы могли бы пересылать вверх . Нужно обновлять узлы раньше ввождения, начатый в точке inserció n. Проблема состоит в том, что требуется признак ya_modificado, с которым нужно пробегать узлы, следующие за введенным 2 раза. Serí в специальный случай optimizable , если точка inserció n está в центре array или má s достигни. Слишком много работы, чтобы оптимизировать 50 % teó богатый случаев, и, как ты говоришь, serí в рассеиваться слишком много. Puff... –  20.01.2017, 16:30
  • 5
    Если ты помещаешь пример mí nimo выполнимый podrí чтобы смотреть с má s спокойствие –  20.01.2017, 16:39