Solution using generic class Stack<T>
User Sithfire proposes the following solution using the generic class Stack<T>
. First of all, this method will create a variable of type Stack<string>
, which is where those directions, that will form the simplified path, will be stored. The next step will be to iterate over the array, using a foreach
loop. Within this loop the first instruction will be as follows: if the stack contains at least one element (that is, if stack.Count > 0
), we will get the element that is at the top of the stack and will assign it to lastElement
variable. Otherwise, if the stack is empty, we will assign null
as value.
Below there is a switch
statement whose cases will be the directions it will find in the array. Depending on which direction switch
statement meets, if the one that is stored inside lastElement
is its opposite, it will remove such direction from the stack and will not add the current one. This means that none of these directions will be part of the simplified path, since they represent an unnecessary waste of energy. Else, it will add the current element to the stack. This operation is not conclusive, because it doesn't assure us that this element will be part of the final path, and we will have to wait to compare it with the next element in the array. Basically what this algorithm does is to compare the current element with the previous one and, in case of being opposite, we discard both, so it will always have to wait for its successor in order to determine whether it will be definitively discarded or part of the path, once they are compared.
Finally, once we get out of the foreach
loop, we will turn the stack into an array and, once this is done, we will reverse the array. The reason behind this is that when taking elements out of the stack, they will go in reverse order, because elements at the top of the stack are those that entered last.
El usuario SithFire nos propone la siguiente solución usando la clase genérica
Stack<T>
. En primer lugar creará una variable de tipoStack<string>
, que es donde va a ir almacenando aquellas direcciones que van a conformar el camino simplificado. El siguiente paso será recorrer el array, usando un bucleforeach
. Dentro de este bucle la primera instrucción será la siguiente: si la pila contiene al menos un elemento (o sea, sistack.Count > 0
), vamos a obtener aquel elemento que está en la cima de la pila y se lo asignaremos a la variablelastElement
. En caso contrario, si la pila está vacía, el valor que le asignaremos seránull
.
A continuación hay un bloque condicionalswitch
que tendrá como casos las direcciones que encontrará en el array. En dependencia de la dirección con la que se encuentre, si la dirección almacenada enlastElement
es su opuesta, eliminará dicha dirección de la pila y, por supuesto, no añadirá la dirección actual. Esto quiere decir que ninguna de esas direcciones formarán parte del camino simplificado, al representar un gasto innecesario de energía. En caso contrario, la operación será añadir el elemento actual a la pila. Esta operación no es concluyente, pues no nos asegura que dicho elemento formará parte del camino final, y tendremos que esperar a compararlo con el siguiente elemento del array. Veámoslo de esta manera: básicamente este algoritmo lo que hace es comparar el elemento actual con el elemento anterior y, en caso de ser opuestos, desechamos ambos, por lo que siempre tendrá que esperar a ser comparado con su sucesor para decidir si será desechado definitivamente o si formará parte del camino.
Por último, una vez salgamos del bucleforeach
, convertiremos la pila en array y, una vez hecho esto, invertiremos los elementos el array. La razón detrás de esto es que a la hora de sacar los elementos de la pila, estos irán en orden inverso, pues los primeros elementos en salir son aquellos que entraron al final.
In this case I would choose a much simpler solution, which will save us a few steps. Instead of the above, we will do the following: we will create a for
loop and iterate over the array in reverse order. In this array we will add the elements that we get out of the stack. Of coruse, the array will have the same amount of elements as the stack. As we know they come out in reverse order, we will also add them to the array in reverse order and this way we will be doing 2 operations in only 1 step, instead of turning the stack into an array and then having to reverse it. This part of the algorithm would be as follows:
En este caso yo optaría por una solución mucho más simple, que nos permite ahorrarnos unos cuantos pasos. En lugar de lo anterior, haremos lo siguiente: crearemos un bucle
for
que recorrerá el array en orden inverso, desde el final hasta el inicio. En dicho array, que tendrá la misma cantidad de elementos que la pila, iremos añadiendo los elementos que extraigamos de ella. Como sabemos que salen en orden inverso, también serán añadidos en orden inverso al interior del array y, de esta manera, estaremos realizando 2 operaciones en 1, en lugar de convertir la pila en array para luego tener que invertir sus elementos. Por tanto esta parte del algoritmo quedaría así.
Checking the comments
In the comments of this solution there are several users who propose a couple of ways of simplifying the switch
statement. A first user says: "I think the switch can be simplified i.e. with a switch expression introduced in C# 8.0. Also, in my opinion the rules are better readable this way:"
En los comentarios de esta solución hay varios usuarios que proponen un par de formas de simplificar el bloque condicional
switch
. Un primer usuario dice: "Creo que el switch puede ser simplificado, por ejemplo, usando switch expressions introducidas en C# 8.0. Además, en mi opinión, las reglas se leen mejor de esta manera".
What are switch expressions
?
From C# 8
, you can use switch
in the context of an expression. Assuming that cardNumber
variable in this example is of type int
, the following illustrates its use:
A partir de
C# 8
puedes usarswitch
en el contexto de una expresión. Asumiendo que la variablecardNumber
en este ejemplo es de tipoint
, la imagen anterior explica su uso.
You can also use switch expressions
on multiple values (the tuple pattern):
También puedes usar
switch expressions
con múltiples valores (the tuple pattern).
In a second comment, another user says: "I think the switch can be simplified, you can refer bpk356's code" (which is the user whose solution we will be analyzing next).
He is basically saying that, instead of using a switch
statement, it is better to use a dictionary in order to simplify those conditions (we will seeing this in the next solution).
En un segundo comentario, otro usuario dice: "Creo que el switch se puede simplificar. Puedes remitirte al código del usuario bpk356" (que es el usuario cuya solución veremos a continuación).
Básicamente está diciendo que, en lugar de usar un bloque condicionalswitch
, utilice un diccionario para simplificar las condiciones (lo cual estaremos viendo en la siguiente solución).
Improving user SithFire solution
Following the advices of these users and the correction we had done before, when turning the stack into an array, user Sithfire solution could be simplified and improved in this way:
Siguiendo los consejos de estos usuarios y la corrección que habíamos hecho anteriormente a la hora de convertir la pila en array, la solución del usuario SithFire podría simplificarse y mejorarse de esta manera.
Solution using Dictionary<T>
User bpk356 starts creating a variable of type Dictionary <string, string>
, in which he stores as keys the strings: "NORTH", "SOUTH", "EAST" and "WEST", and as values of each one its opposite directions. Then he iterates over the array using a foreach
loop and inside of it he creates an if-else
statement. First he checks (as in the previous solution, although in this case he uses a list instead of a stack) if the last element of the list is the opposite of the current element. To do this, he searches for this element using LastOrDefault()
method and compares it with the value in the dictionary whose key is the current element. If this condition is fulfilled, he will remove this element from the list. Since it is its opposite and it is next to it, it cannot be part of the simplified path. Otherwise, he will simply add this direction into the list. Finally he takes the list with the simplified path and turns it into an array to return it.
El usuario bpk356 comienza creando una variable de tipo
Dictionary<string, string>
, en la cual almacena como claves: "NORTH", "SOUTH", "EAST" y "WEST" y, como valores de cada una de estas, las direcciones opuestas. Luego recorre el array utilizando un bucleforeach
y en su interior crea un bloque condicionalif-else
. Primero chequea (tal y como en la solución anterior aunque en este caso lo hace con una lista en lugar de con una pila) si el último elemento de la lista es el opuesto del elemento actual. Para ello busca este elemento utilizando el métodoLastOrDefault()
y lo compara con aquel valor en el diccionario cuya clave es el elemento actual. Si se cumple esta condición, eliminará dicho elemento de la lista ya que, al ser su opuesto y encontrarse a su lado, no podrá formar parte del camino simplificado. En caso contrario, de no cumplirse esta condición, simplemente añadirá dicha dirección a su interior. Finalmente toma la lista con el camino simplificado y la convierte en un array para devolverlo.
Bibliography [Bibliografía]
C# 12 in a Nutshell: The Definitive Reference - Joseph Albahari
C# Data Structures and Algorithms - Marcin Jamro
English translation made with DeeplTranslate
Traducción al inglés hecha con DeeplTranslate