Sei sulla pagina 1di 30

Estructuras de datos

Training Camp Argentina 2013

Fidel I. Schaposnik (UNLP) - fidel.s@gmail.com


17 de julio de 2013

Contenidos

Introduccion
set de C++
Mnimo en intervalos (RMQ)
caso estatico: sparse tables
caso dinamico: segment tree

Suma en intervalos
caso estatico: sumas parciales uni- y bi-dimensionales
caso dinamico: binary indexed tree

Operaciones mas generales en intervalos


caso estatico
caso dinamico: square root trick

Problema adicional: ancestro com


un mas bajo (LCA)

Training Camp Argentina 2013

Estructuras de datos

Introduccion
Qu
e es una estructura de datos?
Una forma de almacenar datos y operar con ellos de modo de
poder calcular eficientemente diversas magnitudes.
Puede pensarse como una caja negra donde estan los datos
(posiblemente sujetos a modificaciones), que nos respondera
cierto tipo de preguntas de modo eficiente.
C
omo se usa una estructura de datos en una competencia
de programaci
on?
Las etapas o pasos intermedios del algoritmo muchas veces pueden
verse como subproblemas menores, que debemos resolver para
hallar la respuesta final. Si bien los problemas completos rara vez
se repiten, los subproblemas muchas veces son clasicos, y
conocemos para ellos una estructura de datos adecuada.
Training Camp Argentina 2013

Estructuras de datos

set de C++
El set de C++ representa un conjunto de elementos, que
podemos pensar como ordenados de menor a mayor. Nos
permite realizar distintas operaciones en O(log N):
insertar y borrar elementos (insert y erase, resp.);
fijarnos si un elemento esta presente (find);
encontrar el primer elemento mayor o mayor o igual que
otro (upper bound y lower bound, respectivamente).
Algunas observaciones:
en un conjunto no puede haber repeticiones;
no controlamos el funcionamiento interno de la estructura,
solo la funcion de comparaci
on;
no podemos modificar elementos (pero s eliminar una version
vieja de un elemento y reemplazarla por una nueva).
Training Camp Argentina 2013

Estructuras de datos

Ejemplo del uso de set de C++


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#i n c l u d e <i o s t r e a m >
#i n c l u d e <s e t >
u s i n g namespace s t d ;
i n t main ( ) {
s e t <i n t > c ;
for ( int

i =15; i <17; i +=3) c . i n s e r t ( i ) ;

c o u t << c . s i z e ( ) << e n d l ;
f o r ( s e t <i n t > : : i t e r a t o r i t =c . b e g i n ( ) ;
c o u t << i t << ;

i t != c . end ( ) ;

i t ++)

c o u t << e n d l << ( c . f i n d ( 9)!= c . end ( ) ) << e n d l ;


c . e r a s e ( 9) ;
c o u t << ( c . f i n d ( 9)!= c . end ( ) ) << e n d l ;
c o u t <<c . l o w e r b o u n d ( 6 )<< <<c . u p p e r b o u n d ( 6 )<<e n d l ;
c o u t <<c . l o w e r b o u n d ( 7 )<< <<c . u p p e r b o u n d ( 7 )<<e n d l ;
return 0;
}

Ejemplo del uso de set de C++


Training Camp Argentina 2013

Estructuras de datos

Ejemplo del uso de set de C++ (cont.)


Podemos utilizar nuestros propios elementos definiendo un orden
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

s t r u c t pt {
int x , y ;
p t ( i n t xx =0 , i n t yy =0) {
x=xx ; y=yy ;
};
};
b o o l o p e r a t o r <( c o n s t p t &p1 , c o n s t p t &p2 ) {
d o u b l e a1=a t a n 2 ( p1 . y , p1 . x ) , a2=a t a n 2 ( p2 . y , p2 . x ) ;
i f ( a1 != a2 ) r e t u r n a1 < a2 ;
e l s e r e t u r n p1 . x p1 . x+p1 . y p1 . y < p2 . x p2 . x+p2 . y p2 . y ;
}
[...]
s e t <pt> p ;
p . i n s e r t ( pt (1 ,1) ) ; p . i n s e r t ( pt (2 ,2) ) ;
p . i n s e r t ( pt (2 ,1) ) ; p . i n s e r t ( pt (3 ,5) ) ;

Uso del set de C++ con una estructura propia


Training Camp Argentina 2013

Estructuras de datos

Aplicacion: Dijkstra
1 s e t < p a i r <i n t , i n t > > s ;
2
3 v o i d d i j k s t r a ( i n t s t a r t , i n t d e s t , i n t N) {
4
f o r ( i n t i =0; i <N ; i ++) n [ i ] . d = INF ;
5
n [ s t a r t ] . d = 0; s . i n s e r t ( make pair (0 , s t a r t ) ) ;
6
w h i l e ( ! s . empty ( ) ) {
7
i n t c u r = s . b e g i n ( )>s e c o n d ;
8
i f ( c u r == d e s t ) b r e a k ;
9
f o r ( i n t i =0; i <( i n t ) n [ c u r ] . c . s i z e ( ) ; i ++) {
10
i n t next = n [ cur ] . c [ i ] . f i r s t ;
11
i f ( n [ n e x t ] . d==INF | | n [ n e x t ] . d>n [ c u r ] . d+n [ c u r ] . c [ i ] . s e c o n d ) {
12
i f ( n [ n e x t ] . d != INF )
13
s . erase ( make pair (n [ next ] . d , next ) ) ;
14
n [ next ] . d = n [ cur ] . d + n [ cur ] . c [ i ] . second ;
15
s . i n s e r t ( make pair (n [ next ] . d , next ) ) ;
16
}
17
}
18
s . erase ( make pair (n [ cur ] . d , cur ) ) ;
19
}
20 }

Algoritmo de Dijkstra usando un set - O(N log N + E )


Training Camp Argentina 2013

Estructuras de datos

Problemas

Template Library Management (TJU - 3972): Algoritmo


goloso acelerado usando dos set elegantemente.

Training Camp Argentina 2013

Estructuras de datos

Mnimo en intervalos (RMQ)


Dado un arreglo de N n
umeros m0 , . . . , mN1 , queremos responder
preguntas de la forma
Cu
al es el menor n
umero en el intervalo mi , . . . , mj1 ?
RMQ [i, j )

El algoritmo ingenuo recorre todos los elementos en [i, j), y


requiere O(N) en el peor caso;
Si no se van a modificar los valores del arreglo, podemos
preprocesar en O(N log N) y responder preguntas en O(1);
Si los elementos del arreglo pueden cambiar, podemos
inicializar en O(N) y responder preguntas o modificar valores
en O(log N)
Training Camp Argentina 2013

Estructuras de datos

RMQ - Sparse tables


Si los elementos del arreglo no pueden cambiar de valor, podemos
preprocesar los datos para acelerar el calculo de las respuestas:
Hay O(N 2 ) preguntas posibles, y podemos precalcular todas
sus respuestas en O(N 2 ) usando programaci
on dinamica;
No hace falta guardar todas las respuestas: basta con
precalcular una cantidad suficiente como para poder responder
cualquier pregunta.
Definimos
stk (i) = min {mj } RMQ[i, i + 2k )
i j<i+2k

para

k = 0, 1, . . .

Observamos que podemos precalcular los O(N log N) valores de st


usando programacion dinamica
st0 (i) = mi
n
o
stk (i) = min stk1 (i), stk1 (i + 2k1 )
Training Camp Argentina 2013

Estructuras de datos

RMQ - Sparse tables (cont.)


Para calcular RMQ [i, j ), observamos que si 2k j i < 2k+1 ,
n
o
RMQ [i, j ) = min stk (i), stk (j 2k )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

void

s t i n i t ( i n t m, i n t N, i n t s t ) {
f o r ( i n t i =0; i <N ; i ++) s t [ 0 ] [ i ] = m[ i ] ;
f o r ( i n t k =1; (1<<k )<=N ; k++) {
f o r ( i n t i =0; i +(1<<k )<=N ; i ++) {
i f ( s t [ k 1 ] [ i ] < s t [ k 1 ] [ i +(1<<(k 1) ) ] )
s t [ k ] [ i ] = s t [ k 1][ i ] ;
e l s e s t [ k ] [ i ] = s t [ k 1 ] [ i +(1<<(k 1) ) ] ;
}

}
}
i n t s t q u e r y ( i n t s t , i n t s , i n t e ) {
i n t k = 31
b u i l t i n c l z ( es ) ;
i f ( s t [ k ] [ s ] < s t [ k ] [ e(1<<k ) ] ) r e t u r n s t [ k ] [ s ] ;
e l s e r e t u r n s t [ k ] [ e(1<<k ) ] ;
}

RMQ estatico con sparse tables - O(N log N) init. y O(1) query
Training Camp Argentina 2013

Estructuras de datos

RMQ - Segment tree


Si los elementos del arreglo pueden cambiar, el preproceso o
inicializaci
on debe ser complementado con el mantenimiento de
la estructura.
Vamos a usar un arbol binario del siguiente modo:
Cada nodo representa un intervalo, en particular la raz
representa al [0, N)
El hijo izquierdo de un nodo representa la primera mitad del
segmento de su padre, el derecho la otra
Las hojas del arbol representan segmentos de longitud uno, y
almacenan el correspondiente valor del arreglo
En cada nodo interno se almacena el mnimo de los dos
valores almacenados en sus dos hijos

Training Camp Argentina 2013

Estructuras de datos

RMQ - Segment tree (cont.)


Una forma elegante de definir un arbol binario usando un arreglo
El nombre de un nodo es su posici
on en el arreglo
La raz es el nodo 1
El hijo izquierdo de i es el nodo 2i, el derecho es el 2i + 1
El padre del nodo i es el i/2
El valor almacenado en el nodo es el valor en la posicion
correspondiente del arreglo
1

10

11

100

1000

101

1001

1010

110

1011

Training Camp Argentina 2013

1100

111

1101

1110

Estructuras de datos

1111

RMQ - Segment tree (cont.)

Para inicializar, debemos llenar los valores de los nodos desde


las hojas hasta la raz: O(N)
Para modificar un valor, debemos cambiar el valor de la hoja
correspondiente y luego actualizar los valores de todos los
nodos internos en el camino de la hoja hasta la raz: O(log N)
Para realizar una consulta, debemos tomar el mnimo entre los
valores almacenados en los nodos cuyos segmentos
corresponden exactamente al intervalo deseado: O(log N)
La estructura tipo arbol sugiere una implementacion recursiva
de todas estas funciones

Training Camp Argentina 2013

Estructuras de datos

RMQ - Segment tree (codigo)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#d e f i n e LEFT ( n ) ( 2 ( n ) )
#d e f i n e RIGHT ( n ) ( 2 ( n )+1 )
#d e f i n e NEUT 2147483647
i n t oper ( i n t a , i n t b) {
r e t u r n min ( a , b ) ;
}
v o i d r m q i n i t ( i n t n , i n t s , i n t e , i n t rmq , i n t m) {
i f ( s+1 == e ) rmq [ n ] = m[ s ] ;
else {
r m q i n i t ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , m) ;
r m q i n i t ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , m) ;
rmq [ n ] = o p e r ( rmq [ LEFT ( n ) ] , rmq [ RIGHT ( n ) ] ) ;
}
}

RMQ dinamico con segment tree - Inicializacion

Training Camp Argentina 2013

Estructuras de datos

RMQ - Segment tree (codigo cont.)


1 v o i d r m q u p d a t e ( i n t n , i n t s , i n t e , i n t rmq , i n t m, i n t p , i n t
v) {
2
i f ( s+1 == e ) rmq [ n ] = m[ s ] = v ;
3
else {
4
i f ( p < ( s+e ) / 2 )
5
r m q u p d a t e ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , m, p , v ) ;
6
e l s e r m q u p d a t e ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , m, p , v ) ;
7
rmq [ n ] = o p e r ( rmq [ LEFT ( n ) ] , rmq [ RIGHT ( n ) ] ) ;
8
}
9 }
10
11 i n t r m q q u e r y ( i n t n , i n t s , i n t e , i n t rmq , i n t a , i n t b ) {
12
i f ( a >= e | | b <= s ) r e t u r n NEUT ;
13
e l s e i f ( s >= a && e <= b ) r e t u r n rmq [ n ] ;
14
else {
15
i n t l = r m q q u e r y ( LEFT ( n ) , s , ( s+e ) / 2 , rmq , a , b ) ;
16
i n t r = r m q q u e r y ( RIGHT ( n ) , ( s+e ) / 2 , e , rmq , a , b ) ;
17
return oper ( l , r ) ;
18
}
19 }

RMQ dinamico con segment tree - Mantenimiento y consulta


Training Camp Argentina 2013

Estructuras de datos

RMQ - Segment tree (observaciones)

Todas las funciones se llaman con n = 1, s = 0 y e = N


En algunos casos conviene almacenar la posicion del menor
elemento, en lugar de su valor
oper puede ser otras operaciones ademas de <: podemos
usar >, pero tambien operaciones algebraicas como + y ,
operaciones binarias como , y |, etc.
NEUT debe ser el elemento neutro de la operacion oper

Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos
Dado un arreglo de N n
umeros m0 , . . . , mN1 , queremos responder
preguntas de la forma
Cu
al es el valor de S [ i, j) = mi + + mj1 ?
El algoritmo ingenuo recorre todos los elementos en [i, j), y
requiere O(N) en el peor caso;
Si no se van a modificar los valores del arreglo, podemos
preprocesar en O(N) y responder preguntas en O(1);
Si los elementos del arreglo pueden cambiar, podemos
inicializar en O(N log N) y responder preguntas o modificar
valores en O(log N)

Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos: sumas parciales


Si los elementos del arreglo no pueden modificarse, podemos
precalcular todas las respuestas en O(N 2 ) y responder en
O(1).
Observamos que basta poder responder cuando i = 0, porque
S [ i, j) = S [ 0, j) S [ 0, i)
Luego podemos precalcular s
olo los valores de S [ 0, i) en
O(N), y responder en O(1)
1
2
3
4
5
6
7
8

v o i d i n i t ( i n t m, i n t sm , i n t N) {
sm [ 0 ] = 0 ;
f o r ( i n t i =1; i <=N ; i ++) sm [ i ] = sm [ i 1]+m[ i 1 ] ;
}
i n t q u e r y ( i n t sm , i n t a , i n t b ) {
r e t u r n sm [ b]sm [ a ] ;
}

Suma en intervalos usando sumas parciales


Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos: sumas parciales bidimensionales


El problema anterior puede generalizarse para el caso de arreglos
bidimensionales: si queremos calcular
S2 (a, b, c, d) =

b1 d1
X
X

mij

i=a j=c

para a < c y b < d, basta conocer S2 (0, 0, i, j), dado que


S2 (a, b, c, d) = S2 (0, 0, c, d)S2 (0, 0, a, d)S2 (0, 0, c, b)+S2 (0, 0, a, b)
Luego el preprocesamiento sera en O(N 2 ) y las consultas en O(1).
El principio de inclusion exclusi
on nos permite generalizar esto a
arreglos n-dimensionales, obteniendo consultas en O(2n ) realizando
un preprocesamiento en O(N n )
Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos: binary indexed tree


Si los elementos del arreglo pueden modificarse, ya conocemos una
estructura eficiente: usamos un segment tree con oper 7 + y
NEUT 7 0.
Una estructura mas eficiente aprovecha la observacion
S [ i, j) = S [ 0, j) S [ 0, i) modificando los intervalos almacenados
en cada posicion del arreglo
P
Definimos bit(i) = ij=ik+1 mj con k la mayor potencia de
2 que divide a i (o k = 1 si i = 0).
Para calcular S [0, i + 1 ) sumamos los intervalos que lo
componen sacando progresivamente los 1s de la expresion
binaria de i.
Para modificar una posici
on (sumar una cantidad en una
posicion dada) agregamos la cantidad deseada a todos los
intervalos que contienen esa posici
on.
Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos: binary indexed tree (codigo)


P. Fenwick, A New Data Structure for Cumulative Frequency Tables,
Software - Practice and Experience, 24(3), pp. 327-336 (1994)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

i n t g e t c f ( i n t idx , i n t b i t ) {
int cf = bit [ 0 ] ;
wh ile ( i d x > 0) {
c f += b i t [ i d x ] ;
i d x &= i d x 1;
}
return cf ;
}
void upd f ( i n t idx , i n t f , i n t b i t ) {
i f ( i d x == 0 ) b i t [ i d x ] += f ;
e l s e w h i l e ( i d x < MAXN) {
b i t [ i d x ] += f ;
i d x += i d x &( i d x ) ;
}
}

Suma en intervalos usando binary indexed tree


Training Camp Argentina 2013

Estructuras de datos

Suma en intervalos: binary indexed tree (observaciones)


Las posiciones del arreglo acumulan los cambios, luego para
modificar una posici
on llamamos a upd f con
(nuevo)

v = mi

(viejo)

mi

Las dos operaciones son O(log N)


La inicializacion es O(N log N) porque debemos modificar
todas las posiciones una por una
Tambien puede modificarse para ser utilizado con otras
operaciones
Se puede extender facilmente esta estructura para operar
sobre arreglos bidimensionales

Training Camp Argentina 2013

Estructuras de datos

Operaciones mas generales: caso estatico

Dada una operacion asociativa y un arreglo de N elementos


m0 , . . . , mN1 que no se pueden modificar, si queremos responder
preguntas de la forma
Cu
al es el valor de O [ i, j) = mi mj1 ?
Definimos
Ok (i) =

k
i+2
O1

h

mj O i, i + 2k

j=i

y para calcular O [ i, j) aplicamos la operaci


on adelantando i a
medida que encontramos 1s en la cantidad j i

Training Camp Argentina 2013

Estructuras de datos

Operaciones mas generales: caso estatico (codigo)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

v o i d i n i t ( i n t m, i n t sm , i n t N)
f o r ( i n t i =0; i <N ; i ++) sm [ 0 ] [
f o r ( i n t k =1; (1<<k )<=N ; k++)
f o r ( i n t i =0; i +(1<<k )<=N ;
sm [ k ] [ i ]= o p e r ( sm [ k 1 ] [
}

{
i ] = m[ i ] ;
i ++)
i ] , sm [ k 1 ] [ i +(1<<(k 1) ) ] ) ;

i n t q u e r y ( i n t sm , i n t a , i n t b ) {
i n t RES = 0 , l = ba ;
f o r ( i n t i =0; (1<< i )<=l ; i ++) i f ( l &(1<< i ) ) {
RES = o p e r ( RES , sm [ i ] [ a ] ) ;
a += (1<< i ) ;
}
r e t u r n RES ;
}

Operaciones mas generales: caso estatico - O(N log N) inicializacion


y O(log N) consulta

Training Camp Argentina 2013

Estructuras de datos

Operaciones mas generales: caso dinamico


Si los elementos del arreglo pueden modificarse, podemos usar el
siguiente truco:

Partimos elintervalo [0, N) en N segmentos si con


i = 0, . . . , N

Para mayor
comodidad, definimos ai = i N y
bi = (i + 1) N; luego el i-esimo segmento corresponde a
[ai , bi ), luego
Para cada segmento, guardamos el valor de
Oi =

bO
i 1

mj O[ai , bi )

j=ai

Al realizar una modificaci


on en la posici
onj, debemos
recalcular el Oi tal que j [ai , bi ) = O( N)
Al realizar una consulta, debemos realizar
la operacion sobre
los segmentos completos (a lo sumo N) y los elementos

sueltos a ambos lados (a lo sumo 2 N) = O( N)


Training Camp Argentina 2013

Estructuras de datos

Problema adicional: ancestro comun mas bajo


El problema consiste en hallar el ancestro com
un mas bajo entre
pares de nodos de un arbol enraizado

Training Camp Argentina 2013

Estructuras de datos

Problema adicional: ancestro comun mas bajo


El problema consiste en hallar el ancestro com
un mas bajo entre
pares de nodos de un arbol enraizado
Puede reducirse a la aplicaci
on de RMQ sobre el arreglo
generado con el pre- y post-visit DFS transversal del arbol:
C
omo?

Training Camp Argentina 2013

Estructuras de datos

Problema adicional: ancestro comun mas bajo


El problema consiste en hallar el ancestro com
un mas bajo entre
pares de nodos de un arbol enraizado
Puede reducirse a la aplicaci
on de RMQ sobre el arreglo
generado con el pre- y post-visit DFS transversal del arbol:
C
omo?
Puede pensarse como un caso particular de la estructura
analizada para operaciones generales en arreglos estaticos:
C
omo?

Training Camp Argentina 2013

Estructuras de datos

Problema adicional: ancestro comun mas bajo


El problema consiste en hallar el ancestro com
un mas bajo entre
pares de nodos de un arbol enraizado
Puede reducirse a la aplicaci
on de RMQ sobre el arreglo
generado con el pre- y post-visit DFS transversal del arbol:
C
omo?
Puede pensarse como un caso particular de la estructura
analizada para operaciones generales en arreglos estaticos:
C
omo?
Puede utilizarse para calcular eficientemente distancias entre
los nodos de un arbol: C
omo?

Training Camp Argentina 2013

Estructuras de datos

Potrebbero piacerti anche