Suponhamos que temos uma sequência de \(n\) valores (e.g. inteiros) \[ v[0], v[1], v[2], \ldots, v[n-1] \]
Queremos saber se um valor \(x\) ocorre na sequência, isto é:
encontrar \(i\) tal que \(0 \leq i < n\) e \(v[i]=x\)
Já vimos um algoritmo para resolver este problema, a pesquisa sequencial:
Este algoritmo encontra o menor índice \(i\) tal que \(v[i] = x\) (se existir).
int pesquisa_seq(int vec[], int n, int x)
{
for(int i = 0; i < n; i++) {
if(vec[i] == x)
return i; // encontrou
}
return -1; // não encontrou
}
i
tal que vec[i] == x
i == n
)val
na posição após o último valor vec[n]
vec[i] == x
i < n
na condição do cicloint pesquisa_seq(int vec[], int n, int x)
{
int i, t; // t é "temporário"
t = vec[n]; // guardar o estado inicial
vec[n] = x; // colocar a "sentinela"
i = 0;
while(vec[i] != x) // procurar
i++;
vec[n] = t; // repor estado inicial
// testar se encontrou e retornar
if(i < n) return i; else return -1;
}
Podemos fazer melhor?
Sim, se a sequência estiver ordenada.
\[ v[0] \leq v[1] \leq v[2] \leq \cdots \leq v[n-2] \leq v[n-1] \]
Dois índices sobre a variável indexada:
Se o valor \(x\) ocorre na sequência original então tem de estar entre \(i\) e \(j\).
Em cada iteração:
\[ \begin{array}{l} i \leftarrow 0\\ j \leftarrow n-1\\ \text{Repetir enquanto}~ i \leq j:\\ \qquad k\leftarrow i + \lfloor (j-i)/2\rfloor \\ \qquad \text{se}~ x=v[k]~ \text{então terminamos com resposta}~ k \\ \qquad \text{se}~ x<v[k]~ \text{então}~ j \leftarrow k-1 \\ \qquad \text{se}~ x>v[k]~ \text{então}~ i \leftarrow k+1 \\ \text{No chegamos ao final do ciclo: o valor não ocorre} \end{array} \]
int pesquisa_bin(int vec[], int n, int x) {
int i = 0, j = n-1;
while (i <= j) {
int k = i + (j-i)/2; // ponto médio
if(vec[k] == x)
return k; // encontrou
else if (x > vec[k])
i = k+1; // x > vec[k]
else
j = k-1; // x < vec[k]
}
// no final do ciclo: i > j
return -1;
}
\(n\) | \(\log_2 n\) |
---|---|
8 | 3 |
16 | 4 |
32 | 5 |
64 | 6 |
128 | 7 |
256 | 8 |
512 | 9 |
1024 | 10 |
Exemplo para \(1000\) valores:
Seja \(v[0], v[1], \ldots, v[n-1]\) uma sequência de \(n\) valores.
A sequência está por ordem ascendente se e só se: \[ v[i] \leq v[i+1]\,, \quad \text{para}~ 0\leq i\leq n-2 \]
A sequência está por ordem descendente se e só se: \[ v[i] \geq v[i+1]\,, \quad \text{para}~ 0\leq i\leq n-2 \]
Nas definições acima usamos \(\leq\) e \(\geq\) para permitir valores repetidos.
Vamos ver um algoritmo clássicos de ordenação por seleção ("selection sort").
Assumimos:
Repetimos para \(i\) de 0 até \(n-1\):
void select_sort(int vec[], int n) {
int i, j;
for(i = 0; i < n; i++) {
int imin = i; // índice inicial do mínimo
for(j = i+1; j < n; j++) {
if(vec[j] < vec[imin]) imin = j;
}
// trocar o mínimo com vec[i]
if(imin != i) {
int temp = vec[i];
vec[i] = vec[imin];
vec[imin] = temp;
}
}
}
\[ \overbrace{\begin{array}{|c|c|c|} \hline \texttt{v[0]} &\cdots &\texttt{v[i-1]} \\ \hline \end{array}}^{\text{por ordem crescente}} \overbrace{\begin{array}{|c|c|c|} \hline \texttt{v[i]} & \cdots & \texttt{v[n-1]} \\ \hline \end{array}}^{\text{?}} \]
Em cada iteração do ciclo exterior, os valores entre \(0\) e \(i-1\):