Técnica Java Two Pointer
1. Visão geral
Neste tutorial, discutiremos a abordagem de dois ponteiros para resolver problemas que envolvem matrizes e listas. This technique is an easy and efficient way to improve the performance of our algorithm.
2. Descrição da técnica
Em muitos problemas envolvendo matrizes ou listas, precisamos analisar cada elemento da matriz em comparação com outros elementos.
Para resolver problemas como esses, geralmente começamos a partir do primeiro índice e percorremos o array uma ou mais vezes, dependendo da nossa implementação. Às vezes, também temos que criar uma matriz temporária dependendo dos requisitos do nosso problema.
A abordagem acima pode nos dar o resultado correto, mas provavelmente não nos dará a solução mais eficiente em termos de espaço e tempo.
Como resultado, geralmente é bom considerar se nosso problema pode ser resolvido de forma eficiente usandotwo-pointers approach.
Na abordagem de dois ponteiros, os ponteiros referem-se aos índices de uma matriz. Usando ponteiros, podemos processar dois elementos por loop, em vez de apenas um.
Padrões comuns na abordagem de dois ponteiros envolvem:
-
Dois ponteiros, cada um começando do começo e do fim até que ambos se encontrem
-
Um ponteiro se move em um ritmo lento enquanto o outro ponteiro se move em um ritmo mais rápido
Ambos os padrões acima podem nos ajudar a reduzir atime and space complexity de nossos problemas à medida que obtemos o resultado esperado em menos iterações e sem usar muito espaço adicional.
Agora, vamos dar uma olhada em alguns exemplos que nos ajudarão a entender essa técnica um pouco melhor.
3. A soma existe em uma matriz
Problema: Dada uma matriz classificada de números inteiros, precisamos ver se há dois números, de forma que sua soma seja igual a um valor específico.
Por exemplo, se nosso array de entrada for[1, 1, 2, 3, 4, 6, 8, 9]e o valor alvo for11, então nosso método deve retornartrue. No entanto, se o valor de destino for20, ele deve retornarfalse.
Vamos primeiro ver uma solução ingênua:
public boolean twoSumSlow(int[] input, int targetValue) {
for (int i = 0; i < input.length; i++) {
for (int j = 1; j < input.length; j++) {
if (input[i] + input[j] == targetValue) {
return true;
}
}
}
return false;
}
Na solução acima, passamos o loop pela matriz de entrada duas vezes para obter todas as combinações possíveis. Verificamos a soma da combinação em relação ao valor alvo e retornamostrue se corresponder. The time complexity of this solution is [.katex-mathml]#O(n^2) [.katex] [. katex-mathml]. #
Agora vamos ver como podemos aplicar a técnica de dois ponteiros aqui:
public boolean twoSum(int[] input, int targetValue) {
int pointerOne = 0;
int pointerTwo = input.length - 1;
while (pointerOne < pointerTwo) {
int sum = input[pointerOne] + input[pointerTwo];
if (sum == targetValue) {
return true;
} else if (sum < targetValue) {
pointerOne++;
} else {
pointerTwo--;
}
}
return false;
}
Como a matriz já está classificada, podemos usar dois ponteiros. Um ponteiro começa no início da matriz, e o outro ponteiro começa no final da matriz e, em seguida, adicionamos os valores nesses ponteiros. Se a soma dos valores for menor que o valor alvo, incrementamos o ponteiro esquerdo e, se a soma for maior que o valor alvo, diminuímos o ponteiro direito.
Continuamos movendo esses ponteiros até obtermos a soma que corresponde ao valor alvo ou chegamos ao meio da matriz e nenhuma combinação foi encontrada. The time complexity of this solution is [.katex-mathml]#O(n)[.katex][.katex-mathml] and[.katex][.katex-mathml] space complexity is O(1)[.katex-mathml], # uma melhoria significativa em relação à nossa primeira implementação.
4. Rotate Arrayk Steps
Problema: dada uma matriz, gire a matriz para a direita por etapas dek, ondek não é negativo. Por exemplo, se nossa matriz de entrada for[1, 2, 3, 4, 5, 6, 7]ek for4, então a saída deve ser[4, 5, 6, 7, 1, 2, 3].
Podemos resolver isso tendo dois loops novamente, o que tornará a complexidade do tempoO(n^2) ou usando um array extra e temporário, mas isso tornará a complexidade do espaçoO(n).
Vamos resolver isso usando a técnica de dois ponteiros:
public void rotate(int[] input, int step) {
step %= input.length;
reverse(input, 0, input.length - 1);
reverse(input, 0, step - 1);
reverse(input, step, input.length - 1);
}
private void reverse(int[] input, int start, int end) {
while (start < end) {
int temp = input[start];
input[start] = input[end];
input[end] = temp;
start++;
end--;
}
}
Nos métodos acima, invertemos as seções da matriz de entrada no local, várias vezes, para obter o resultado necessário. Para reverter as seções, usamos a abordagem de dois ponteiros, na qual a troca de elementos era feita nas duas extremidades da seção da matriz.
Especificamente, primeiro revertemos todos os elementos da matriz. Em seguida, invertemos os primeiros elementosk e, em seguida, invertemos o restante dos elementos. The time complexity of this solution is [.katex-mathml]#O(n)[.katex][.katex-mathml] and [.katex] [. katex-mathml] space complexity is O(1).#
5. Elemento do meio em umLinkedList
Problema: Dado um únicoLinkedList, encontre seu elemento do meio. Por exemplo, se nossa entradaLinkedList for1→2→3→4→5,, então a saída deve ser3.
Também podemos usar a técnica de dois ponteiros em outras estruturas de dados semelhantes a matrizes como aLinkedList:
public T findMiddle(MyNode head) {
MyNode slowPointer = head;
MyNode fastPointer = head;
while (fastPointer.next != null && fastPointer.next.next != null) {
fastPointer = fastPointer.next.next;
slowPointer = slowPointer.next;
}
return slowPointer.data;
}
Nesta abordagem, percorremos a lista vinculada usando dois ponteiros. Um ponteiro é incrementado em um enquanto o outro é incrementado em dois. Quando o ponteiro rápido chegar ao fim, o ponteiro lento estará no meio da lista vinculada. The time complexity of this solution is [.katex-mathml]#O(n)[.katex][.katex-mathml], and[.katex][.katex-mathml]# space complexity is O(1).
6. Conclusão
Neste artigo, discutimos como podemos aplicar a técnica de dois ponteiros vendo alguns exemplos e analisamos como ela melhora a eficiência de nosso algoritmo.
O código neste artigo está disponívelover on Github.