<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Algoritmos - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Descubre miles de cursos de programación escritos por expertos. Aprende Desarrollo Web, Ciencia de Datos, DevOps, Seguridad y obtén asesoramiento profesional para desarrolladores. ]]>
        </description>
        <link>https://www.freecodecamp.org/espanol/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Algoritmos - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 08:36:28 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/tag/algoritmos/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Explicación de algoritmos y estructuras de datos de grafos con Ejemplos en Java y C++ ]]>
                </title>
                <description>
                    <![CDATA[ ¿Qué es un Algoritmo de Grafos? Los algoritmos de grafos son un conjunto de instrucciones que recorren (visitan los nodos de) un grafo.  Algunos algoritmos son usados para hallar un nodo específico o el camino entre dos nodos dados. ¿Por qué son Importantes los Algoritmos de Grafos? Los grafos ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/explicacion-de-algoritmos-y-estructuras-de-datos-de-grafos-con-ejemplos-en-java-y-c/</link>
                <guid isPermaLink="false">6455780be59d8f07c2db4221</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gibran Pelayo M. ]]>
                </dc:creator>
                <pubDate>Thu, 18 May 2023 17:39:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/05/5f9c9e3e740569d1a4ca3c1f.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/graph-algorithms-and-data-structures-explained-with-java-and-c-examples/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Graph Algorithms and Data Structures Explained with Java and C++ Examples</a>
      </p><h2 id="-qu-es-un-algoritmo-de-grafos"><strong>¿Qué es un Algoritmo de Grafos?</strong></h2><p>Los algoritmos de grafos son un conjunto de instrucciones que recorren (visitan los nodos de) un grafo. </p><p>Algunos algoritmos son usados para hallar un nodo específico o el camino entre dos nodos dados.</p><h3 id="-por-qu-son-importantes-los-algoritmos-de-grafos"><strong>¿Por qué son Importantes los Algoritmos de Grafos?</strong></h3><p>Los grafos son estructuras de datos muy útiles que se usan para modelar varios problemas. Estos algoritmos tienen aplicaciones directas en sitios de redes sociales, modelado de máquinas de estado y más.</p><h3 id="algunos-algoritmos-de-grafos-comunes"><strong>Algunos Algoritmos de Grafos Comunes</strong></h3><p>Algunos de los algoritmos de grafos más comunes son: </p><ul><li>Búsqueda en Amplitud o Anchura (Breadth First Search, BFS)</li><li>Búsqueda en Profundidad (Depth First Search, DFS)</li><li>Dijkstra</li><li>Algoritmo de Floyd-Warshall</li></ul><h2 id="algoritmo-de-bellman-ford"><strong>Algoritmo de Bellman Ford</strong></h2><p>El algoritmo de Bellman Ford es un algoritmo de búsqueda del camino más corto para grafos que puede tener pesos negativos. El algoritmo de Bellman Ford es también ideal para detectar ciclos de pesos negativos, ya que el algoritmo converge hacia una solución óptima en O(V*E) pasos. Si la resultante no es óptima, entonces el grafo contiene un ciclo de pesos negativos.</p><p>Aquí está una implementación en Python:</p><pre><code>infinito = 1e10

def bellman_ford(grafo, inicio, fin):
    num_vertices = grafo.get_num_vertices()
    aristas = grafo.get_edges()

    distancia = [infinito para vértice en range(num_vertices)]
    previo = [ninguno para vértice en range(num_vertices)]

    distancia[inicio] = 0
    for i range(fin+1):
        for (u, v) in aristas:
            if distancia[v] &gt; distancia[u] + grafo.get_weight(u, v):
                distancia[v] = distancia[u] + grafo.get_weight(u, v)
                previo[v] = u

    for (u,v) in aristas:
        if distancia[v] &gt; distancia[u] + grafo.get_weight(u, v):
            raise ErrorCicloPesoNegativo()
    return distancia, previo
# 'distancia' es la distancia desde el inicio hasta ese nodo en el camino más corto, útil para imprimir la distancia más corta.
# Previo es un arreglo que muestra el nodo que es anterior al actual, útil para imprimir el camino.
</code></pre><h2 id="b-squeda-en-profundidad-depth-first-search-dfs-"><strong>Búsqueda en Profundidad (Depth First Search, DFS)</strong></h2><p>La Búsqueda en Profundidad es uno de los algoritmos de grafos más sencillos. Recorre el grafo revisando primero el nodo actual y moviéndose después a uno de sus sucesores para repetir el proceso. Si el nodo actual no tiene sucesor a revisar, regresamos a su predecesor y el proceso continúa (moviéndose a otro sucesor). Si la solución es encontrada, la búsqueda termina.</p><h3 id="visualizaci-n"><strong>Visualización</strong></h3><h3 id="implementaci-n-c-14-"><strong>Implementación (C++14)</strong></h3><pre><code>#include &lt;iostream&gt; 
#include &lt;vector&gt; 
#include &lt;queue&gt;  
#include &lt;algorithm&gt;
using namespace std; 
 
class Grafo{ 
   int v;    // número de vértices 
 
   // puntero a un vector que contiene listas de adyacencia
   vector &lt; int &gt; *adj;
public: 
   Grafo(int v);  // Constructor 
 
   // función para añadir una arista al grafo
   void agregar_arista(int v, int w);  
 
   // imprime el recorrido dfs a partir de una fuente `s` dada
   void dfs();
   void dfs_util(int s, vector &lt; bool&gt; &amp;visitado);   
}; 
 
Grafo::Grafo(int v){ 
   this -&gt; v = v; 
   adj = new vector &lt; int &gt;[v]; 
} 
 
void Grafo::agregar_arista(int u, int v){ 
   adj[u].push_back(v); // agregar v a la lista de u
   adj[v].push_back(v); // agregar u a la lista de v (¡elimina esta declaración si el grafo es dirigido!)
} 
void Grafo::dfs(){
   // vector visitado - para llevar registro de los nodos visitados durante la búsqueda en profundidad)
   vector &lt; bool &gt; visitado(v, false);  // marcar todos los nodos/vértices como no visitados
   for(int i = 0; i &lt; v; i++)
       if(!visitado[i])
           dfs_util(i, visitado);
} 
// ¡observa el uso de la llamada por referencia aquí!
void Grafo::dfs_util(int s, vector &lt; bool &gt; &amp;visitado){ 
   // marcar el nodo/vértice actual como visitado
   visitado[s] = true;
    // enviarlo a la salida estándar(pantalla)
   cout &lt;&lt; s &lt;&lt; " ";
   
   // ¡recorrer su lista de adyacencia y llamar recursivamente a dfs_util para todos sus vecinos! 
   // (¡solo si el vecino aún no ha sido visitado!)
   for(vector &lt; int &gt; :: iterator itr = adj[s].begin(); itr != adj[s].end(); itr++)
       if(!visitado[*itr])
           dfs_util(*itr, visitado); 
} 
 
int main() 
{ 
   // crear un grafo usando la clase Grafo que definimos anteriormente
   Grafo g(4); 
   g.agregar_arista(0, 1); 
   g.agregar_arista(0, 2); 
   g.agregar_arista(1, 2); 
   g.agregar_arista(2, 0); 
   g.agregar_arista(2, 3); 
   g.agregar_arista(3, 3); 
 
   cout &lt;&lt; "A continuación el recorrido de la búsqueda en profundidad del grafo proporcionado"
        &lt;&lt; "(a partir del vértice 0): "; 
   g.dfs(); 
   // la salida sería: 0 1 2 3
   return 0; 
} 
</code></pre><h3 id="evaluaci-n"><strong>Evaluación</strong></h3><p>Complejidad del Espacio: O(n)</p><p>Complejidad del Tiempo en el Peor de los Casos: O(n) La Búsqueda en Profundidad es completa en un conjunto finito de nodos. Funciona mejor en árboles poco profundos.</p><h3 id="implementaci-n-de-dfs-en-c-"><strong>Implementación de DFS en C++</strong></h3><pre><code>#include&lt;iostream&gt;
#include&lt;vector&gt;
#include&lt;queue&gt;

using namespace std;

struct Grafo{
	int v;
	bool **adj;
	public:
		Grafo(int conteov);
		void agregarArista(int u,int v);
		void borrarArista(int u,int v);
		vector&lt;int&gt; DFS(int s);
		void DFSUtil(int s,vector&lt;int&gt; &amp;dfs,vector&lt;bool&gt; &amp;visitado);
};
Grafo::Grafo(int conteov){
	this-&gt;v = conteov;
	this-&gt;adj=new bool*[conteov];
	for(int i=0;i&lt;conteov;i++)
		this-&gt;adj[i]=new bool[conteov];
	for(int i=0;i&lt;conteov;i++)
		for(int j=0;j&lt;conteov;j++)
			adj[i][j]=false;
}

void Grafo::agregarArista(int u,int w){
	this-&gt;adj[u][w]=true;
	this-&gt;adj[w][u]=true;
}

void Grafo::borrarArista(int u,int w){
	this-&gt;adj[u][w]=false;
	this-&gt;adj[w][u]=false;
}

void Grafo::DFSUtil(int s, vector&lt;int&gt; &amp;dfs, vector&lt;bool&gt; &amp;visitado){
	visitado[s]=true;
	dfs.push_back(s);
	for(int i=0;i&lt;this-&gt;v;i++){
		if(this-&gt;adj[s][i]==true &amp;&amp; visitado[i]==false)
			DFSUtil(i,dfs,visitado);
	}
}

vector&lt;int&gt; Grafo::DFS(int s){
	vector&lt;bool&gt; visitado(this-&gt;v);
	vector&lt;int&gt; dfs;
	DFSUtil(s,dfs,visitado);
	return dfs;
}
</code></pre><h2 id="algoritmo-de-floyd-warshall"><strong>Algoritmo de Floyd Warshall</strong></h2><p>El algoritmo de Floyd Warshall es un gran algoritmo para encontrar la distancia más corta entre todos los vértices del grafo. Tiene un algoritmo muy conciso y complejidad del tiempo O(V^3) (donde V es el número de vértices). Puede ser usado con pesos negativos, aunque los ciclos de pesos negativos no deben estar presentes en el grafo.</p><h3 id="evaluaci-n-1"><strong>Evaluación</strong></h3><p>Complejidad del Espacio: O(V^2)</p><p>Complejidad del Tiempo en el Peor de los Casos: O(V^3)</p><h3 id="implementaci-n-en-python"><strong>Implementación en Python</strong></h3><figure class="kg-card kg-code-card"><pre><code># Un valor grande como infinito
inf = 1e10 

def floyd_warshall(pesos):
    V = len(pesos)
    matriz_distancia = pesos
    for k in range(V):
        siguiente_matriz_distancia = [list(row) for row in matriz_distancia] # hacer una copia de matriz de distancia
        for i in range(V):
            for j in range(V):
                # Elegir si el vértice k puede funcionar como un camino con menor distancia
                siguiente_matriz_distancia[i][j] = min(matriz_distancia[i][j], matriz_distancia[i][k] + matriz_distancia[k][j])
        matriz_distancia = siguiente_matriz_distancia # actualizar
    return matriz_distancia

# Un grafo representado como matriz de adyacencia
grafo = [
    [0, inf, inf, -3],
    [inf, 0, inf, 8],
    [inf, 4, 0, -2],
    [5, inf, 3, 0]
]

print(floyd_warshall(grafo))
</code></pre><figcaption></figcaption></figure><h2 id="b-squeda-en-amplitud-o-anchura-breadth-first-search-bfs-"><strong>Búsqueda en Amplitud o Anchura (Breadth First Search, BFS)</strong></h2><p>Búsqueda en Amplitud o Anchura es uno de los algoritmos de grafos más sencillo. Recorre el grafo al primero comprobar el nodo actual y luego expandirlo al agregar sus sucesores al siguiente nivel. El proceso se repite para todos los nodos del nivel actual &nbsp;antes de moverse al siguiente. Si se encuentra la solución, la búsqueda termina.</p><h3 id="visualizaci-n-1"><strong>Visualización</strong></h3><figure class="kg-card kg-image-card"><img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" class="kg-image" alt="Animated_BFS" width="187" height="175" loading="lazy"></figure><h3 id="evaluaci-n-2"><strong>Evaluación</strong></h3><p>Complejidad del Espacio: O(n)</p><p>Complejidad del Tiempo en el Peor de los Casos: O(n)</p><p>La Búsqueda en Amplitud o Anchura es completa en un conjunto finito de nodos y es óptima si el costo de moverse de un nodo a otro es constante.</p><h3 id="c-digo-c-para-la-implementaci-n-de-bfs"><strong>Código C++ para la implementación de BFS</strong></h3><pre><code>// Programa para imprimir el recorrido BFS desde un 
// vértice origen dado. BFS(int s) recorre los vértices  
// alcanzables desde s. 
#include&lt;iostream&gt; 
#include &lt;list&gt; 
  
using namespace std; 
  
// Esta clase representa un grafo dirigido
// utilizando la representación de listas de adyacencias 
class Grafo 
{ 
    int V;    // No. de vértices 
  
    // Puntero a un arreglo que contiene listas de adyacencia
    list&lt;int&gt; *adj;    
public: 
    Grafo(int V);  // Constructor 
  
    // función para agregar una arista al grafo
    void agregarArista(int v, int w);  
  
    // imprime el recorrido de BFS desde una fuente s dada
    void BFS(int s);   
}; 
  
Grafo::Grafo(int V) 
{ 
    this-&gt;V = V; 
    adj = new list&lt;int&gt;[V]; 
} 
  
void Grafo::agregarArista(int v, int w) 
{ 
    adj[v].push_back(w); // Agregar w a la lista de v.
} 
  
void Grafo::BFS(int s) 
{ 
    // Marcar todos los vértices como no visitados
    bool *visitado = new bool[V]; 
    for(int i = 0; i &lt; V; i++) 
        visitado[i] = false; 
  
    // Crear una cola para BFS 
    list&lt;int&gt; cola; 
  
    // Marcar el nodo actual como visitado y ponerlo en cola
    visitado[s] = true; 
    cola.push_back(s); 
  
    // 'i' será usado para obtener todos los vértices
    // adyacentes de un vértice 
    list&lt;int&gt;::iterator i; 
  
    while(!cola.empty()) 
    { 
        // Sacar de la cola un vértice e imprimirlo
        s = cola.front(); 
        cout &lt;&lt; s &lt;&lt; " "; 
        cola.pop_front(); 
  
        // Obtener todos los vértices adyacentes del
        // vértice s sacado de la cola. Si un adyacente
        // no ha sido visitado, entonces marcarlo como 
        // visitado y ponerlo en la cola
        for (i = adj[s].begin(); i != adj[s].end(); ++i) 
        { 
            if (!visitado[*i]) 
            { 
                visitado[*i] = true; 
                cola.push_back(*i); 
            } 
        } 
    } 
} 
  
// Programa controlador para probar métodos de la clase grafo
int main() 
{ 
    // Crear un grafo como el del diagrama anterior
    Grafo g(4); 
    g.agregarArista(0, 1); 
    g.agregarArista(0, 2); 
    g.agregarArista(1, 2); 
    g.agregarArista(2, 0); 
    g.agregarArista(2, 3); 
    g.agregarArista(3, 3); 
  
    cout &lt;&lt; "A continuación se muestra el recorrido en amplitud o anchura "
         &lt;&lt; "(empezando desde el vértice 2) \n"; 
    g.BFS(2); 
  
    return 0; 
}
</code></pre><h2 id="algoritmo-de-dijkstra"><strong>Algoritmo de Dijkstra</strong></h2><p>El Algoritmo de Dijkstra es un algoritmo de grafo presentado por E. W. Dijkstra. Encuentra el camino más corto de origen único en un grafo con aristas no negativas. (¿Por qué?)</p><p>Creamos dos arreglos: visitado y distancia, que registran si un vértice es visitado y cuál es la mínima distancia desde el vértice origen, respectivamente. Inicialmente, el arreglo visitado se asigna como falso y distancia como infinito. </p><p>Partimos del vértice origen. Dejemos que el vértice actual sea u y sus vértices adyacentes sean v. Ahora, por cada v que es adyacente a u, la distancia se actualiza si no ha sido visitado antes y la distancia desde u es menor que su distancia actual. Luego seleccionamos el siguiente vértice con la menor distancia y que no haya sido visitado.</p><p>La Cola de Prioridad se usa a menudo para cumplir este último requerimiento en el menor tiempo posible. Abajo se ve una implementación de la misma idea usando la cola de prioridad en Java.</p><pre><code>import java.util.*;
public class Dijkstra {
    class Grafo {
	LinkedList&lt;Pair&lt;Integer&gt;&gt; adj[];
	int n; // Número de vertices.
	Grafo(int n) {
	    this.n = n;
	    adj = new LinkedList[n];
	    for(int i = 0;i&lt;n;i++) adj[i] = new LinkedList&lt;&gt;();
	}
	// agregar una arista dirigida entre los vértices a y b con el costo como peso
	public void agregarAristaDirigida(int a, int b, int costo) {
	    adj[a].add(new Pair(b, costo));
	}
	public void agregarAristaNoDirigida(int a, int b, int costo) {
	    agregarAristaDirigida(a, b, costo);
	    agregarAristaDirigida(b, a, costo);
	}
    }
    class Pair&lt;E&gt; {
	E first;
	E second;
	Pair(E f, E s) {
	    first = f;
	    second = s;
	}
    }

    // Comparador para ordenar Pares en la Cola de Prioridad
    class ComparadorPar implements Comparator&lt;Pair&lt;Integer&gt;&gt; {
	public int comparar(Pair&lt;Integer&gt; a, Pair&lt;Integer&gt; b) {
	    return a.second - b.second;
	}
    }

    // Calcular el camino más corto para cada vértice del origen y devuelve la distancia
    public int[] dijkstra(Grafo g, int src) {
	int distancia[] = new int[g.n]; // la distancia más corta de cada vértice desde src
	boolean visitado[] = new boolean[g.n]; // el vértice es visitado o no
	Arrays.fill(distancia, Integer.MAX_VALUE);
	Arrays.fill(visitado, false);
	PriorityQueue&lt;Pair&lt;Integer&gt;&gt; pq = new PriorityQueue&lt;&gt;(100, new ComparadorPar());
        pq.add(new Pair&lt;Integer&gt;(src, 0));
	distancia[src] = 0;
	while(!pq.isEmpty()) {
	    Pair&lt;Integer&gt; x = pq.remove(); // Extraer el vértice con la distancia más corta desde src
	    int u = x.first;
	    visitado[u] = true;
	    Iterator&lt;Pair&lt;Integer&gt;&gt; iter = g.adj[u].listIterator();
	    // Iterar sobre los vecinos de u y actualizar sus distancias
	    while(iter.hasNext()) {
		Pair&lt;Integer&gt; y = iter.next();
		int v = y.first;
		int peso = y.second;
		// Comprobar si el vértice v no es visitado
		// Si el nuevo camino a través de u ofrece menos costo, entonces actualizar el arreglo de distancia y agregarlo a cp
		if(!visitado[v] &amp;&amp; distancia[u]+peso&lt;distancia[v]) {
		    distancia[v] = distancia[u]+peso;
		    pq.add(new Pair(v, distancia[v]));
		}
	    }
	}
	return distancia;
    }

    public static void main(String args[]) {
	Dijkstra d = new Dijkstra();
	Dijkstra.Grafo g = d.new Grafo(4);
	g.agregarAristaNoDirigida(0, 1, 2);
	g.agregarAristaNoDirigida(1, 2, 1);
	g.agregarAristaNoDirigida(0, 3, 6);
	g.agregarAristaNoDirigida(2, 3, 1);
	g.agregarAristaNoDirigida(1, 3, 3);

	int dist[] = d.dijkstra(g, 0);
	System.out.println(Arrays.toString(dist));
    }
}
</code></pre><h2 id="algoritmo-de-ford-fulkerson"><strong>Algoritmo de Ford &nbsp;Fulkerson</strong></h2><p>El algoritmo de Ford Fulkerson resuelve el problema de grafo de flujo máximo. Encuentra la mejor organización del flujo mediante las aristas de grafos, de tal modo que obtienes el flujo máximo de salida en el otro extremo. La fuente tiene una tasa específica de entrada y cada arista tiene un peso asociado que es la sustancia máxima que puede pasar a través de esa arista.</p><p>Al algoritmo Ford Fulkerson también se le conoce como algoritmo Edmund-Karp, ya que fue provisto en especificación completa por Jack Edmonds y Richard Karp.</p><p>Trabaja creando caminos de aumento, es decir, caminos desde el origen hasta el sumidero que tienen flujo no cero. Pasamos el flujo a través de los caminos y actualizamos los límites. Esto puede llevar a una situación donde no tenemos más movimientos. Es aquí donde la habilidad 'deshacer' de este algoritmo juega un papel importante. En caso de quedarse atorado, disminuimos el flujo y abrimos la arista para pasar nuestra sustancia actual.</p><h2 id="pasos"><strong>Pasos</strong></h2><ol><li>Establecer flujo cero para todas las aristas.</li><li>Mientras haya un camino del origen al hundimiento hacer, </li><li>Hallar el peso mínimo en el camino, que sea el <code>límite</code>.</li><li>Para todas las aristas (u, v) en el camino hacer,<br>1. Agregar <code>límite</code> al flujo de u a v. (Para el movimiento actual)<br>2. Restar <code>límite</code> &nbsp;al flujo de v a u. (Para deshacer en un movimiento posterior)</li></ol><h3 id="evaluaci-n-3"><strong>Evaluación</strong></h3><p>Complejidad del tiempo: <code>O(V*E^2)</code></p><h3 id="implementaci-n-en-python-1"><strong>Implementación en Python</strong></h3><pre><code># Número grande como infinito
inf = 1e10

def flujo_maximo(grafo, fuente, destino):
  flujo_maximo = 0
  padre = bfs(grafo, fuente, destino)
  while camino:
    limite = inf
    v = destino
    while v != fuente:
        u = padre[s]
        flujo_camino = min(limite, grafo[u][v])
        v = padre[v]
    flujo_maximo += flujo_camino

    v = destino
    while v != fuente:
        u = padre[v]
        grafo[u][v] -= flujo_camino
        grafo[v][u] += flujo_camino
        v = padre[v]

    camino = bfs(grafo, fuente, destino)
  return flujo_maximo</code></pre> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmos de ordenación explicados con ejemplos en JavaScript, Python, Java y C++ ]]>
                </title>
                <description>
                    <![CDATA[ ¿Qué es un algoritmo de ordenación? Los algoritmos de ordenación son un conjunto de instrucciones que toman un arreglo o lista como entrada y organizan los elementos en un orden particular. Las ordenaciones suelen ser numéricas o una forma de orden alfabético (o lexicográfico), y pueden ser en orden ascendente ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/algoritmos-de-ordenacion-explicados-con-ejemplos-en-javascript-python-java-y-c/</link>
                <guid isPermaLink="false">64455d642ef44808032fa708</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ BlackeyeB ]]>
                </dc:creator>
                <pubDate>Mon, 01 May 2023 00:45:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/04/5f9c9ede740569d1a4ca3f9d-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/sorting-algorithms-explained-with-examples-in-python-java-and-c/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Sorting Algorithms Explained with Examples in JavaScript, Python, Java, and C++</a>
      </p><h2 id="-qu-es-un-algoritmo-de-ordenaci-n"><strong>¿Qué es un algoritmo de ordenación?</strong></h2><p>Los algoritmos de ordenación son un conjunto de instrucciones que toman un arreglo o lista como entrada y organizan los elementos en un orden particular.</p><p>Las ordenaciones suelen ser numéricas o una forma de orden alfabético (o lexicográfico), y pueden ser en orden ascendente (AZ, 0-9) o descendente (ZA, 9-0).</p><h2 id="por-qu-los-algoritmos-de-ordenaci-n-son-importantes"><strong>Por qué los algoritmos de ordenación son importantes</strong></h2><p>Dado que a menudo pueden reducir la complejidad de un problema, los algoritmos de ordenación son muy importantes en informática. Estos algoritmos tienen aplicaciones directas en algoritmos de búsqueda, algoritmos de bases de datos, métodos divide y vencerás, algoritmos de estructura de datos y muchos más.</p><h2 id="compensaciones-de-los-algoritmos-de-ordenaci-n"><strong>Compensaciones de los algoritmos de </strong>ordenación</h2><p>Al elegir un algoritmo de ordenación, se deben hacer algunas preguntas: ¿Cuán grande es la colección que se ordena? ¿Cuánta memoria hay disponible? ¿La colección necesita crecer?</p><p>Las respuestas a estas preguntas pueden determinar qué algoritmo funcionará mejor para cada situación. Algunos algoritmos como la ordenación por combinación pueden necesitar mucho espacio o memoria para ejecutarse, mientras que la ordenación por inserción no siempre es la más rápida, pero no requiere muchos recursos para ejecutarse.</p><p>Debes determinar cuáles son tus requisitos y considerar las limitaciones de tu sistema antes de decidir qué algoritmo de ordenación usar.</p><h2 id="algunos-algoritmos-de-ordenaci-n-comunes"><strong>Algunos algoritmos de </strong>ordenación<strong> comunes</strong></h2><p>Algunos de los algoritmos de ordenación más comunes son:</p><ul><li>Selection sort (selección)</li><li>Bubble sort (burbuja)</li><li>Insertion sort (inserción)</li><li>Merge sort (combinación)</li><li>Quick sort (rápida)</li><li>Heap sort (montón)</li><li>Counting sort (conteo)</li><li>Radix sort (raíz)</li><li>Bucket sort (cubo)</li></ul><p>Pero antes de entrar en cada uno de estos, aprendamos un poco más sobre qué clasifica a un algoritmo de ordenación.</p><h2 id="clasificaci-n-de-un-algoritmo-de-ordenaci-n"><strong>Clasificación de un algoritmo de </strong>ordenación</h2><p>Los algoritmos de ordenación se pueden clasificar en función de los siguientes parámetros:</p><ol><li><strong><strong>La cantidad de interca</strong>m<strong>bios o inversiones requeridas:</strong></strong> Esta es la cantidad de veces que el algoritmo intercambia elementos para ordenar la entrada. La ordenación por selección requiere el número mínimo de intercambios.</li><li><strong><strong>El número de comparaciones:</strong></strong> Este es el número de veces que el algoritmo compara elementos para ordenar la entrada. Usando <a href="https://guide.freecodecamp.org/computer-science/notation/big-o-notation/">la notación Big-O</a> , los ejemplos de algoritmos de ordenación enumerados anteriormente requieren al menos <code>O(nlogn)</code>comparaciones en el mejor de los casos y <code>O(n^2)</code> comparaciones en el peor de los casos para la mayoría de los resultados.</li><li><strong><strong>Ya sea que usen recursividad o no:</strong></strong> Algunos algoritmos de ordenación, como la ordenación rápida, usan técnicas recursivas para ordenar la entrada. Otros algoritmos de ordenación, como la ordenación por selección o la ordenación por inserción, utilizan técnicas no recursivas. Por último, algunos algoritmos de ordenación, como la ordenación por fusión, utilizan técnicas tanto recursivas como no recursivas para ordenar la entrada.</li><li><strong><strong>Ya sean estables o inestables:</strong></strong> Los algoritmos de ordenación estables mantienen el orden relativo de los elementos con valores iguales o claves. Los algoritmos de ordenación inestables no mantienen el orden relativo de los elementos con valores/claves iguales.<br><br>Por ejemplo, imagina que tienes el arreglo de entrada <code>[1, 2, 3, 2, 4]</code>. Y para ayudar a diferenciar entre los dos valores iguales, <code>2</code> actualicémoslos a <code>2a</code> y <code>2b</code>, creando el arreglo de entrada <code>[1, 2a, 3, 2b, 4]</code>.<br><br>Los algoritmos de ordenación estables mantendrán el orden de <code>2a</code> y <code>2b</code>, lo que significa que el arreglo de salida será <code>[1, 2a, 2b, 3, 4]</code>. Los algoritmos de ordenación inestables no mantienen el orden de los valores iguales y el arreglo de salida puede ser <code>[1, 2b, 2a, 3, 4]</code>.<br><br>La ordenación por inserción, la ordenación por fusión y la ordenación por burbuja son estables. La ordenación en montón y la ordenación rápida son inestables.</li><li><strong><strong>La cantidad de espacio adicional requerido:</strong></strong> Algunos algoritmos de ordenación pueden ordenar una lista sin crear una lista completamente nueva. Estos se conocen como algoritmos de ordenación en el lugar y requieren un <code>O(1)</code> espacio adicional constante para la ordenación. Mientras tanto, los algoritmos de ordenación fuera de lugar crean una nueva lista durante la ordenación.<br><br>La ordenación por inserción y la ordenación rápida son algoritmos de ordenación en el lugar, ya que los elementos se mueven alrededor de un punto de pivote y no usan un arreglo separado.<br><br>Merge sort es un ejemplo de un algoritmo de ordenación fuera del lugar, ya que el tamaño de la entrada debe asignarse de antemano para almacenar la salida durante el proceso de ordenación, lo que requiere memoria adicional.</li></ol><h2 id="bucket-sort-ordenaci-n-de-cubo-"><strong><strong>Bucket Sort</strong> (O</strong>rdenación<strong> de cubo)</strong></h2><p>La ordenación de cubos es un algoritmo de ordenación por comparación que opera en elementos dividiéndolos en diferentes cubos y luego ordenando estos cubos individualmente. Cada depósito se ordena individualmente utilizando un algoritmo de ordenación independiente, como la ordenación por inserción, o aplicando el algoritmo de ordenación de cubos de forma recursiva.</p><p>La ordenación de cubos es principalmente útil cuando la entrada se distribuye uniformemente en un rango. Por ejemplo, imagina que tienes una gran variedad de enteros de punto flotante distribuidos uniformemente entre un límite superior e inferior.</p><p>Puedes usar otro algoritmo de ordenación, como la ordenación por combinación, la ordenación por montones o la ordenación rápida. Sin embargo, esos algoritmos garantizan una complejidad de tiempo en el mejor de los casos de <code>O(nlogn)</code>.</p><p>Usando la ordenación de cubos, la ordenación del mismo arreglo se puede completar a <code>O(n)</code>tiempo.</p><h3 id="pseudoc-digo-para-ordenaci-n-de-cubo-"><strong>Pseudocódigo para </strong>ordenación<strong> de cubo:</strong></h3><pre><code>void bucketSort(float[] a,int n)

{

    for(each floating integer 'x' in n)

    {
        insert x into bucket[n*x]; 
    }

    for(each bucket)

    {
        sort(bucket);
    }

}
</code></pre><h2 id="counting-sort-ordenaci-n-de-conteo-"><strong><strong>Counting Sort</strong> (</strong>Ordenación<strong> de conteo)</strong></h2><p>El algoritmo de ordenación por conteo funciona creando primero una lista de los conteos u ocurrencias de cada valor único en la lista. Luego crea una lista ordenada final basada en la lista de conteos.</p><p>Una cosa importante que debes recordar es que la ordenación por conteo solo se puede usar cuando conoces de antemano el rango de valores posibles en la entrada.</p><h3 id="ejemplo-"><strong>Ejemplo:</strong></h3><pre><code>Digamos que tienes una lista de números enteros del 0 al 5:
 
input = [2, 5, 3, 1, 4, 2]

Primero, debes crear una lista de recuentos para cada valor único en
la lista `entrada`. Como sabes que el rango de la `entrada` es de 0 a
5, puedes crear una lista con cinco marcadores de posición para los valores del 0 al 5, respectivamente:

count = [0, 0, 0, 0, 0, 0]
  # val: 0  1  2  3  4  5

Luego, revisas la lista `input` e iteras el índice para cada valor en uno.

Por ejemplo, el primer valor en la lista `input` es 2, por lo que agrega uno
al valor en el segundo índice de la lista `count`, que representa
el valor 2:

count = [0, 0, 1, 0, 0, 0]
  # val: 0  1  2  3  4  5
       
El siguiente valor en la lista `input` es 5, por lo que agrega uno al valor en el último índice de la lista `count`, que representa el valor 5:

count = [0, 0, 1, 0, 0, 1]
  # val: 0  1  2  3  4  5

Continúa hasta que tengas el recuento total de cada valor en la `entrada`
lista:

count = [0, 1, 2, 1, 1, 1]
  # val: 0  1  2  3  4  5

Finalmente, dado que sabes cuántas veces cada valor en la lista `input`
aparece, puedes crear fácilmente una lista de `salida` ordenada. Bucle a través de la lista `recuento`, y para cada recuento, agrega el valor correspondiente (0 - 5) al arreglo `output` tantas veces.

Por ejemplo, no había 0 en la lista de 'entradas', pero había una aparición del valor 1, por lo que agrega ese valor al arreglo `output`
una vez:

output = [1]

Luego hubo dos apariciones del valor 2, por lo que las agrega a la
lista `output`:

output = [1, 2, 2]

Y así sucesivamente hasta que tengas la lista final ordenada de "salida" (output):

output = [1, 2, 2, 3, 4, 5]
</code></pre><h3 id="propiedades"><strong>Propiedades</strong></h3><ul><li>Complejidad de espacio:<code>O(k)</code></li><li>Rendimiento del mejor caso:<code>O(n+k)</code></li><li>Rendimiento del caso promedio:<code>O(n+k)</code></li><li>Rendimiento en el peor de los casos:<code>O(n+k)</code></li><li>Estable: Sí ( <code>k</code>es el rango de los elementos en el arreglo)</li></ul><h3 id="implementaci-n-en-javascript"><strong>Implementación en JavaScript</strong></h3><pre><code class="language-js">let numbers = [1, 4, 1, 2, 7, 5, 2];
let count = [];
let output =[];
let i = 0;
let max = Math.max(...numbers);

// inicializa el contador
for (i = 0; i &lt;= max; i++) {
  count[i] = 0;
}

// inicializa el arreglo de salida
for (i = 0; i &lt; numbers.length; i++) {
  output[i] = 0;
}

for (i = 0; i &lt; numbers.length; i++) {
  count[numbers[i]]++;
}

for (i = 1; i &lt; count.length; i++) {
  count[i] += count[i-1];
}

for (i = numbers.length - 1; i &gt;= 0; i--) { 
  output[--count[numbers[i]]] = numbers[i];
}

// arreglo ordenado de salida
for (i = 0; i &lt; output.length; i++) {
  console.log(output[i]);
}</code></pre><h3 id="implementaci-n-de-c-"><strong>Implementación de C++</strong></h3><pre><code class="language-cpp">#include &lt;iostream&gt;

#include &lt;vector&gt;

void countSort(int upperBound, int lowerBound, std::vector &lt; int &gt; numbersToSort) 
// Límites inferior y superior de los números en el vector
{
  int i;
  int range = upperBound - lowerBound; // Crea un rango lo suficientemente grande para obtener todos los números entre el mínimo y el máximo
  std::vector &lt; int &gt; counts(range + 1); // Inicializar conteos del tamaño del rango
  std::fill(counts.begin(), counts.end(), 0); // Rellenar vector de ceros
  std::vector &lt; int &gt; storedNumbers(numbersToSort.size()); // Inicializar los números almacenados del mismo tamaño que el vector de entrada
  std::fill(storedNumbers.begin(), storedNumbers.end(), 0); // Rellenar vector de números almacenados de ceros
  for (i = 0; i &lt; numbersToSort.size(); i++) {
    int index = numbersToSort[i] - lowerBound; // Por ejemplo, si 5 es el límite inferior y numberToSort[i] es 5, el índice será 0 y el
    counts[index] += 1; // recuento de 5 se almacenará en recuentos[0]
  }

  for (i = 1; i &lt; counts.size(); i++) {
    counts[i] += counts[i - 1];
  }

  for (i = numbersToSort.size() - 1; i &gt;= 0; i--) { // Copiar elementos de numbersToSort a storedNumbers según el conteo
    storedNumbers[--counts[numbersToSort[i] - lowerBound]] = numbersToSort[i];
  }

  for (i = 0; i &lt; storedNumbers.size(); i++) {
    std::cout &lt;&lt; storedNumbers[i];
    if (i != storedNumbers.size() - 1)
      std::cout &lt;&lt; ", ";
  }
  std::cout &lt;&lt; std::endl;
}</code></pre><h3 id="implementaci-n-en-swift"><strong>Implementación en Swift</strong></h3><pre><code class="language-swift">func countingSort(_ array: [Int]) {
  // Crea un arreglo para almacenar el conteo de cada elemento
  let maxElement = array.max() ?? 0
  var countArray = [Int](repeating: 0, count: Int(maxElement + 1))
  
  for element in array {
    countArray[element] += 1
  }
  
  for i in 1 ..&lt; countArray.count {
    countArray[i] += countArray[i-1];
  }
  
  var sortedArray = [Int](repeating: 0, count: array.count)
  
  // copia elementos del arreglo a sortedArray de acuerdo con el conteo
  for i in (0 ..&lt; array.count) .reversed() {
    countArray[array[i]] -= 1
    sortedArray[countArray[array[i]]] = array[i];
  }
  
  print(sortedArray)
}</code></pre><h2 id="insertion-sort-ordenaci-n-de-inserci-n-"><strong><strong>Insertion Sort</strong> (</strong>Ordenación<strong> de inserción)</strong></h2><p>La ordenación por inserción es un algoritmo de ordenación simple para una pequeña cantidad de elementos.</p><h3 id="ejemplo--1"><strong>Ejemplo:</strong></h3><p>En la ordenación por inserción, compara el elemento <code>key</code> con los elementos anteriores. Si los elementos anteriores son mayores que el elemento <code>key</code>, mueve el elemento anterior a la siguiente posición.</p><p>Comienza desde el índice 1 hasta el tamaño del arreglo de entrada.</p><p>[ 8 3 5 1 4 2 ]</p><p>Paso 1 :</p><pre><code>      key = 3 //a partir del 1er índice.

      Aquí `key`(clave) se comparará con los elementos anteriores.

      En este caso, `key` se compara con 8. como 8 &gt; 3, mueve el elemento 8
      a la siguiente posición e inserta `key` en la posición anterior.

      Result: [ 3 8 5 1 4 2 ]
</code></pre><p>Paso 2 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/2.png?raw=true" class="kg-image" alt="[ 3 8 5 1 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      key = 5 //2º índice

      8 &gt; 5 // mueve 8 al segundo índice e inserta 5 en el primer índice.

      Result: [ 3 5 8 1 4 2 ]
</code></pre><p>Paso 3 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/3.png?raw=true" class="kg-image" alt="[ 3 5 8 1 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      //3er índice

      8 &gt; 1     =&gt; [ 3 5 1 8 4 2 ]  

      5 &gt; 1     =&gt; [ 3 1 5 8 4 2 ]

      3 &gt; 1     =&gt; [ 1 3 5 8 4 2 ]

      Result: [ 1 3 5 8 4 2 ]
</code></pre><p>Paso 4 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/4.png?raw=true" class="kg-image" alt="[ 1 3 5 8 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      key = 4 // 4º índice

      8 &gt; 4   =&gt; [ 1 3 5 4 8 2 ]

      5 &gt; 4   =&gt; [ 1 3 4 5 8 2 ]

      3 &gt; 4   ≠&gt;  stop

      Result: [ 1 3 4 5 8 2 ]
</code></pre><p>Paso 5 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/5.png?raw=true" class="kg-image" alt="[ 1 3 4 5 8 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      key = 2 // 5º índice

      8 &gt; 2   =&gt; [ 1 3 4 5 2 8 ]

      5 &gt; 2   =&gt; [ 1 3 4 2 5 8 ]

      4 &gt; 2   =&gt; [ 1 3 2 4 5 8 ]

      3 &gt; 2   =&gt; [ 1 2 3 4 5 8 ]

      1 &gt; 2   ≠&gt; stop

      Result: [1 2 3 4 5 8]
</code></pre><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/6.png?raw=true" class="kg-image" alt="[ 1 2 3 4 5 8 ]" width="600" height="400" loading="lazy"></figure><p>El algoritmo que se muestra a continuación es una versión ligeramente optimizada para evitar cambiar el elemento <code>key</code> &nbsp;en cada iteración. Aquí, el elemento &nbsp;<code>key</code> &nbsp; se intercambiará al final de la iteración (paso).</p><pre><code>    InsertionSort(arr[])
      for j = 1 to arr.length
         key = arr[j]
         i = j - 1
         while i &gt; 0 and arr[i] &gt; key
            arr[i+1] = arr[i]
            i = i - 1
         arr[i+1] = key
</code></pre><p>Aquí hay una implementación detallada en JavaScript:</p><pre><code class="language-js">function insertion_sort(A) {
    var len = array_length(A);
    var i = 1;
    while (i &lt; len) {
        var x = A[i];
        var j = i - 1;
        while (j &gt;= 0 &amp;&amp; A[j] &gt; x) {
            A[j + 1] = A[j];
            j = j - 1;
        }
        A[j+1] = x;
        i = i + 1;
    }
}
</code></pre><p>A continuación se muestra una implementación rápida en Swift:</p><pre><code class="language-swift">  var array = [8, 3, 5, 1, 4, 2]

  func insertionSort(array:inout Array&lt;Int&gt;) -&gt; Array&lt;Int&gt;{
      for j in 0..&lt;array.count {
          let key = array[j]
          var i = j-1

          while (i &gt; 0 &amp;&amp; array[i] &gt; key){
              array[i+1] = array[i]
              i = i-1
          }
          array[i+1] = key
      }
      return array
  }
</code></pre><p>El ejemplo de Java se muestra a continuación:</p><pre><code class="language-java">public int[] insertionSort(int[] arr)
      for (j = 1; j &lt; arr.length; j++) {
         int key = arr[j]
         int i = j - 1
         while (i &gt; 0 and arr[i] &gt; key) {
            arr[i+1] = arr[i]
            i -= 1
         }
         arr[i+1] = key
      }
      return arr;
</code></pre><p>Y en c...</p><pre><code class="language-c">void insertionSort(int arr[], int n) 
{ 
   int i, key, j; 
   for (i = 1; i &lt; n; i++) 
   { 
       key = arr[i]; 
       j = i-1;
       while (j &gt;= 0 &amp;&amp; arr[j] &gt; key) 
       { 
           arr[j+1] = arr[j]; 
           j = j-1; 
       } 
       arr[j+1] = key; 
   } 
} 
</code></pre><h3 id="propiedades-"><strong>Propiedades:</strong></h3><ul><li>Complejidad de espacio: O(1)</li><li>Complejidad de tiempo: O(n), O(n* n), O(n* n) para los mejores, promedios y peores casos respectivamente.</li><li>Mejor caso: El arreglo ya está ordenado </li><li>Caso promedio: El arreglo se ordena aleatoriamente</li><li>En el peor de los casos: El arreglo se ordena de forma inversa. </li><li>Ordenación en el lugar: Sí</li><li>Estable: Sí</li></ul><h2 id="heapsort-ordenaci-n-por-montones-"><strong>Heapsort (Ordenación por montones)</strong></h2><p>Heapsort es un algoritmo de ordenación eficiente basado en el uso de montones máximos/mínimos. Un montón es una estructura de datos basada en un árbol que satisface la propiedad del montón, es decir, para un montón máximo, la clave de cualquier nodo es menor o igual que la clave de su padre (si tiene un padre).</p><p>Esta propiedad se puede aprovechar para acceder al elemento máximo en el montón en tiempo O (logn) usando el método maxHeapify. Realizamos esta operación n veces, cada vez que movemos el elemento máximo en el montón a la parte superior del montón y lo extraemos del montón y lo colocamos en un arreglo ordenado. Por lo tanto, después de n iteraciones, tendremos una versión ordenada del arreglo de entrada. </p><p>El algoritmo no es un algoritmo en el lugar y requeriría que se construyera primero una estructura de datos de montón. El algoritmo también es inestable, lo que significa que al comparar objetos con la misma clave, no se conservará el orden original.</p><p>Este algoritmo se ejecuta en tiempo O(nlogn) y espacio adicional O(1) [O(n) incluido el espacio requerido para almacenar los datos de entrada] ya que todas las operaciones se realizan completamente en el lugar.</p><p>La complejidad de tiempo del caso mejor, peor y promedio de Heapsort es O (nlogn). Aunque heapsort tiene una mejor complejidad en el peor de los casos que quicksort, una ordenación rápida bien implementada se ejecuta más rápido en la práctica. Este es un algoritmo basado en la comparación, por lo que se puede usar para conjuntos de datos no numéricos en la medida en que se pueda definir alguna relación (propiedad del montón) sobre los elementos.</p><p>Una implementación en Java es como se muestra a continuación:</p><pre><code class="language-java">import java.util.Arrays;
public class Heapsort {

	public static void main(String[] args) {
		//test array
        //arreglo de prueba
		Integer[] arr = {1, 4, 3, 2, 64, 3, 2, 4, 5, 5, 2, 12, 14, 5, 3, 0, -1};
		String[] strarr = {"hope you find this helpful!", "wef", "rg", "q2rq2r", "avs", "erhijer0g", "ewofij", "gwe", "q", "random"};
		arr = heapsort(arr);
		strarr = heapsort(strarr);
		System.out.println(Arrays.toString(arr));
		System.out.println(Arrays.toString(strarr));
	}
	
	//O(nlogn) TIME, O(1) SPACE, NOT STABLE
    //O(nlogn) TIEMPO, O(1) ESPACIO, NO ESTABLE
	public static &lt;E extends Comparable&lt;E&gt;&gt; E[] heapsort(E[] arr){
		int heaplength = arr.length;
		for(int i = arr.length/2; i&gt;0;i--){
			arr = maxheapify(arr, i, heaplength);
		}
		
		for(int i=arr.length-1;i&gt;=0;i--){
			E max = arr[0];
			arr[0] = arr[i];
			arr[i] = max;
			heaplength--;
			arr = maxheapify(arr, 1, heaplength);
		}
		
		return arr;
	}
	
	//Creates maxheap from array
//Crea maxheap a partir de un arreglo
	public static &lt;E extends Comparable&lt;E&gt;&gt; E[] maxheapify(E[] arr, Integer node, Integer heaplength){
		Integer left = node*2;
		Integer right = node*2+1;
		Integer largest = node;
		
		if(left.compareTo(heaplength) &lt;=0 &amp;&amp; arr[left-1].compareTo(arr[node-1]) &gt;= 0){
			largest = left;
		}
		if(right.compareTo(heaplength) &lt;= 0 &amp;&amp; arr[right-1].compareTo(arr[largest-1]) &gt;= 0){
			largest = right;
		}	
		if(largest != node){
			E temp = arr[node-1];
			arr[node-1] = arr[largest-1];
			arr[largest-1] = temp;
			maxheapify(arr, largest, heaplength);
		}
		return arr;
	}
}
</code></pre><p>Implementación en C++</p><pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; 
    int l = 2*i + 1;  
    int r = 2*i + 2;
    if (l &lt; n &amp;&amp; arr[l] &gt; arr[largest]) 
        largest = l;
    if (r &lt; n &amp;&amp; arr[r] &gt; arr[largest]) 
        largest = r;
    if (largest != i) 
    { 
        swap(arr[i], arr[largest]); 
  
        
        heapify(arr, n, largest); 
    } 
} 
  
 
void heapSort(int arr[], int n) 
{ 
    
    for (int i = n / 2 - 1; i &gt;= 0; i--) 
        heapify(arr, n, i); 
  
    
    for (int i=n-1; i&gt;=0; i--) 
    { 

        swap(arr[0], arr[i]); 
  
        
        heapify(arr, i, 0); 
    } 
} 
void printArray(int arr[], int n) 
{ 
    for (int i=0; i&lt;n; ++i) 
        cout &lt;&lt; arr[i] &lt;&lt; " "; 
    cout &lt;&lt; "\n"; 
} 
int main() 
{ 
    int arr[] = {12, 11, 13, 5, 6, 7}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
  
    heapSort(arr, n); 
  
    cout &lt;&lt; "Sorted array is \n"; 
    printArray(arr, n); 
}
</code></pre><h2 id="ordenaci-n-radix">Ordenación<strong> Radix</strong></h2><p>Requisito previo: Ordenar por conteo</p><p>QuickSort, MergeSort y HeapSort son algoritmos de ordenación basados ​​en comparaciones. CountSort no lo es. Tiene la complejidad de O(n+k), donde k es el elemento máximo del arreglo de entrada. Entonces, si k es O(n), CountSort se convierte en una ordenación lineal, que es mejor que los algoritmos de ordenación basados ​​en comparación que tienen una complejidad de tiempo O(nlogn).</p><p>La idea es extender el algoritmo CountSort para obtener una mejor complejidad de tiempo cuando k pasa a O(n2). Aquí viene la idea de Radix Sort.</p><h3 id="el-algoritmo-"><strong>El algoritmo:</strong></h3><p>Para cada dígito i donde i varía del dígito menos significativo al dígito más significativo de un número, ordena el arreglo de entrada utilizando el algoritmo de ordenación de acuerdo con el i-ésimo dígito. Usamos la ordenación por conteo porque es una ordenación estable.</p><p>Ejemplo: Supón que el arreglo de entrada es:</p><p>10, 21, 17, 34, 44, 11, 654, 123</p><p>Según el algoritmo, ordenaremos el arreglo de entrada según el dígito de uno (dígito menos significativo).</p><p>0: 10<br>1: 21 11<br>2:<br>3: 123<br>4: 34 44 654<br>5:<br>6:<br>7: 17<br>8:<br>9:</p><p>Entonces, el arreglo se convierte en 10, 21, 11, 123, 24, 44, 654, 17.</p><p>Ahora, ordenaremos según el dígito de las decenas:</p><p>0:<br>1: 10 11 17<br>2: 21 123<br>3: 34<br>4: 44<br>5: 654<br>6:<br>7:<br>8:<br>9:</p><p>Ahora, el arreglo se convierte en: 10, 11, 17, 21, 123, 34, 44, 654.</p><p>Finalmente, ordenamos según el dígito de las centenas (dígito más significativo):</p><p>0: 010 011 017 021 034 044<br>1: 123<br>2:<br>3:<br>4:<br>5:<br>6: 654<br>7:<br>8:<br>9:</p><p>El arreglo se convierte en: 10, 11, 17, 21, 34, 44, 123, 654 que está ordenado. Así es como funciona nuestro algoritmo.</p><p>Una implementación en C:</p><pre><code class="language-c">void countsort(int arr[],int n,int place){

        int i,freq[range]={0};         //el rango de números enteros es 10 ya que los dígitos van del 0 al 9

        int output[n];

        for(i=0;i&lt;n;i++)
                freq[(arr[i]/place)%range]++;

        for(i=1;i&lt;range;i++)
                freq[i]+=freq[i-1];
        
        for(i=n-1;i&gt;=0;i--){
                output[freq[(arr[i]/place)%range]-1]=arr[i];
                freq[(arr[i]/place)%range]--;
        }
        
        for(i=0;i&lt;n;i++)
                arr[i]=output[i];
}

void radixsort(ll arr[],int n,int maxx){            //maxx es el elemento máximo en el arreglo

        int mul=1;
        while(maxx){
                countsort(arr,n,mul);
                mul*=10;
                maxx/=10;
        }
}
</code></pre><h2 id="selection-sort-ordenaci-n-de-selecci-n-"><strong><strong>Selection Sort</strong> (</strong>Ordenación<strong> de selección)</strong></h2><p>Selection Sort es uno de los algoritmos de ordenación más simples. Este algoritmo recibe su nombre de la forma en que itera a través del arreglo: Selecciona el elemento más pequeño actual y lo cambia de lugar.</p><p>Así es como funciona:</p><ol><li>Encuentra el elemento más pequeño en el arreglo y lo intercambia con el primer elemento.</li><li>Encuentra el segundo elemento más pequeño y lo intercambia con el segundo elemento del arreglo.</li><li>Encuentra el tercer elemento más pequeño y lo intercambia con el tercer elemento del arreglo.</li><li>Repite el proceso de encontrar el siguiente elemento más pequeño y cambiarlo a la posición correcta hasta que se ordene todo el arreglo.</li></ol><p>Pero, ¿cómo escribirías el código para encontrar el índice del segundo valor más pequeño en un arreglo?</p><p>Una manera fácil es notar que el valor más pequeño ya se ha cambiado al índice 0, por lo que el problema se reduce a encontrar el elemento más pequeño en el arreglo que comienza en el índice 1.</p><p>La ordenación por selección siempre toma el mismo número de comparaciones clave — N(N − 1)/2.</p><h3 id="implementaci-n-en-c-c-"><strong>Implementación en C/C++</strong></h3><p>El siguiente programa en C++ contiene una implementación iterativa y recursiva del algoritmo de ordenación por selección. Ambas implementaciones se invocan en la función &nbsp;<code>main()</code>.</p><pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

template&lt;typename T, size_t n&gt;
void print_array(T const(&amp;arr)[n]) {
    for (size_t i = 0; i &lt; n; i++)
        std::cout &lt;&lt; arr[i] &lt;&lt; ' ';
    cout &lt;&lt; "\n";
}

int minIndex(int a[], int i, int j) {
    if (i == j)
        return i;
    int k = minIndex(a, i + 1, j);
    return (a[i] &lt; a[k]) ? i : k;
}

void recurSelectionSort(int a[], int n, int index = 0) {
    if (index == n)
        return;
    int k = minIndex(a, index, n - 1);
    if (k != index)
        swap(a[k], a[index]);
    recurSelectionSort(a, n, index + 1);
}

void iterSelectionSort(int a[], int n) {
    for (int i = 0; i &lt; n; i++)
    {
        int min_index = i;
        int min_element = a[i];
        for (int j = i + 1; j &lt; n; j++)
        {
            if (a[j] &lt; min_element)
            {
                min_element = a[j];
                min_index = j;
            }
        }
        swap(a[i], a[min_index]);
    }
}

int main() {
    int recurArr[6] = { 100,35, 500, 9, 67, 20 };
    int iterArr[5] = { 25, 0, 500, 56, 98 };

    cout &lt;&lt; "Recursive Selection Sort"  &lt;&lt; "\n";
    print_array(recurArr); // 100 35 500 9 67 20
    recurSelectionSort(recurArr, 6);
    print_array(recurArr); // 9 20 35 67 100 500

    cout &lt;&lt; "Iterative Selection Sort" &lt;&lt; "\n";
    print_array(iterArr); // 25 0 500 56 98
    iterSelectionSort(iterArr, 5);
    print_array(iterArr); // 0 25 56 98 500
}
</code></pre><h3 id="implementaci-n-en-javascript-1"><strong>Implementación en JavaScript</strong></h3><pre><code class="language-js">function selection_sort(A) {
    var len = A.length;
    for (var i = 0; i &lt; len - 1; i = i + 1) {
        var j_min = i;
        for (var j = i + 1; j &lt; len; j = j + 1) {
            if (A[j] &lt; A[j_min]) {
                j_min = j;
            } else {}
        }
        if (j_min !== i) {
            swap(A, i, j_min);
        } else {}
    }
}

function swap(A, x, y) {
    var temp = A[x];
    A[x] = A[y];
    A[y] = temp;
}
</code></pre><h3 id="implementaci-n-en-python"><strong>Implementación en Python</strong></h3><pre><code class="language-ps">def seletion_sort(arr):
         if not arr:
         return arr
    for i in range(len(arr)):
         min_i = i
         for j in range(i + 1, len(arr)):
              if arr[j] &lt; arr[min_i]:
                  min_i = j
         arr[i], arr[min_i] = arr[min_i], arr[i]
</code></pre><h3 id="implementaci-n-en-java"><strong>Implementación en Java</strong></h3><pre><code class="language-java">public void selectionsort(int array[])
{
    int n = array.length;            // método para encontrar la longitud del arreglo
    for (int i = 0; i &lt; n-1; i++)
    {
        int index = i;
        int min = array[i];                  // tomando el elemento min como i-ésimo elemento del arreglo
        for (int j = i+1; j &lt; n; j++)
        {
            if (array[j] &lt; array[index])
            {
                index = j;
                min = array[j];
            }
        }
        int t = array[index];         //Intercambiar los lugares de los elementos
        array[index] = array[i];
        array[i] = t;
    }
}
</code></pre><h3 id="implementaci-n-en-matlab"><strong>Implementación en MATLAB</strong></h3><pre><code>function [sorted] = selectionSort(unsorted)
    len = length(unsorted);
    for i = 1:1:len
        minInd = i;
        for j = i+1:1:len
           if unsorted(j) &lt; unsorted(minInd) 
               minInd = j;
           end
        end
        unsorted([i minInd]) = unsorted([minInd i]);    
    end
    sorted = unsorted;
end
</code></pre><h3 id="propiedades-1"><strong>Propiedades</strong></h3><ul><li>Complejidad espacial: &nbsp; <strong><strong>O(n)</strong></strong></li><li>Complejidad del tiempo: &nbsp; <strong><strong>O(n2)</strong></strong></li><li>Ordenación en el lugar: &nbsp; <strong><strong>Sí</strong></strong></li><li>Estable: &nbsp; <strong>No</strong></li></ul><h2 id="bubble-sort-ordenaci-n-de-burbuja-"><strong><strong>Bubble Sort</strong> (Ordenación de burbuja)</strong></h2><p>Al igual que las burbujas se elevan desde el fondo de un vaso, <strong><strong>la </strong>ordenación<strong> de burbujas</strong></strong> es un algoritmo simple que ordena una lista, lo que permite que los valores más bajos o más altos aparezcan en la parte superior. El algoritmo atraviesa una lista y compara valores adyacentes, intercambiandolos si no están en el orden correcto.</p><p>Con una complejidad en el peor de los casos de O(n^2), la ordenación de burbujas es muy lenta en comparación con otros algoritmos de ordenación como la ordenación rápida. La ventaja es que es uno de los algoritmos de ordenación más fáciles de entender y codificar desde cero.</p><p>Desde una perspectiva técnica, la ordenación por burbuja es razonable para ordenar arreglos de tamaño pequeño o especialmente cuando se ejecutan algoritmos de ordenación en ordenadores con recursos de memoria notablemente limitados.</p><h3 id="ejemplo--2"><strong>Ejemplo:</strong></h3><h3 id="primer-paso-por-la-lista-"><strong>Primer paso por la lista:</strong></h3><ul><li>Comenzando con <code>[4, 2, 6, 3, 9]</code>, el algoritmo compara los dos primeros elementos del arreglo, 4 y 2. Los intercambia porque 2 &lt; 4:<code>[2, 4, 6, 3, 9]</code></li><li>Compara los siguientes dos valores, 4 y 6. Como 4 &lt; 6, estos ya están en orden, y el algoritmo continúa:<code>[2, 4, 6, 3, 9]</code></li><li>Los siguientes dos valores también se intercambian porque 3 &lt; 6:<code>[2, 4, 3, 6, 9]</code></li><li>Los dos últimos valores, 6 y 9, ya están en orden, por lo que el algoritmo no los intercambia.</li></ul><h3 id="second-pass-through-the-list-segundo-paso-por-la-lista-"><strong><strong>Second pass through the list:</strong></strong><br><strong>Segundo paso por la lista:</strong></h3><ul><li>2 &lt; 4, por lo que no hay necesidad de intercambiar posiciones:<code>[2, 4, 3, 6, 9]</code></li><li>El algoritmo intercambia los siguientes dos valores porque 3 &lt; 4:<code>[2, 3, 4, 6, 9]</code></li><li>Sin intercambio porque 4 &lt; 6:<code>[2, 3, 4, 6, 9]</code></li><li>De nuevo, 6 &lt; 9, por lo que no se produce intercambio:<code>[2, 3, 4, 6, 9]</code></li></ul><p>La lista ya está ordenada, pero el algoritmo de ordenación de burbujas no se da cuenta de esto. Más bien, necesita completar una pasada completa a través de la lista sin intercambiar ningún valor para saber que la lista está ordenada.</p><h3 id="tercer-paso-por-la-lista-"><strong>Tercer paso por la lista:</strong></h3><ul><li><code>[2, 3, 4, 6, 9]</code> =&gt; <code>[2, 3, 4, 6, 9]</code></li><li><code>[2, 3, 4, 6, 9]</code> =&gt; <code>[2, 3, 4, 6, 9]</code></li><li><code>[2, 3, 4, 6, 9]</code> =&gt; <code>[2, 3, 4, 6, 9]</code></li><li><code>[2, 3, 4, 6, 9]</code> =&gt; <code>[2, 3, 4, 6, 9]</code></li></ul><p>Claramente, la ordenación por burbujas está lejos de ser el algoritmo de ordenación más eficiente. Aún así, es simple entenderlo e implementarlo por ti mismo.</p><h4 id="propiedades-2"><strong>Propiedades</strong></h4><ul><li>Complejidad del espacio: O(1)</li><li>Rendimiento en el mejor caso: O(n)</li><li>Rendimiento promedio de casos: O(n*n)</li><li>Rendimiento en el peor de los casos: O(n*n)</li><li>Estable: Sí</li></ul><h3 id="v-deo-explicaci-n-en-ingl-s-"><strong>Vídeo Explicación (en inglés)</strong></h3><p><a href="https://www.youtube.com/watch?v=Jdtq5uKz-w4">Algoritmo de ordenación de burbujas</a></p><h3 id="ejemplo-en-javascript"><strong>Ejemplo en JavaScript</strong></h3><pre><code class="language-js">let arr = [1, 4, 7, 45, 7,43, 44, 25, 6, 4, 6, 9],
    sorted = false;

while(!sorted) {
  sorted = true;
  for(var i=0; i &lt; arr.length; i++) {
    if(arr[i] &lt; arr[i-1]) {
      let temp = arr[i];
      arr[i] = arr[i-1];
      arr[i-1] = temp;
      sorted = false;
    }
  }
}
</code></pre><h3 id="ejemplo-en-java"><strong>Ejemplo en Java</strong></h3><pre><code class="language-java">public class BubbleSort {
    static void sort(int[] arr) {
        int n = arr.length;
        int temp = 0;
         for(int i=0; i &lt; n; i++){
                 for(int x=1; x &lt; (n-i); x++){
                          if(arr[x-1] &gt; arr[x]){
                                 temp = arr[x-1];
                                 arr[x-1] = arr[x];
                                 arr[x] = temp;
                         }

                 }
         }

    }
    public static void main(String[] args) {

		for(int i=0; i &lt; 15; i++){
			int arr[i] = (int)(Math.random() * 100 + 1);
		}

                System.out.println("array before sorting\n");
                for(int i=0; i &lt; arr.length; i++){
                        System.out.print(arr[i] + " ");
                }
                bubbleSort(arr);
                System.out.println("\n array after sorting\n");
                for(int i=0; i &lt; arr.length; i++){
                        System.out.print(arr[i] + " ");
                }

        }
}
</code></pre><h3 id="ejemplo-en-c-"><strong>Ejemplo en C++</strong></h3><pre><code class="language-cpp">// Implementación recursiva
void bubblesort(int arr[], int n)
{
	if(n==1)	//Initial Case  
//Caso inicial
		return;
	bool swap_flag = false;
	for(int i=0;i&lt;n-1;i++)	//Después de este paso, el elemento más grande se moverá a la ubicación deseada.
	{
		if(arr[i]&gt;arr[i+1])
		{
			int temp=arr[i];
			arr[i]=arr[i+1];
			arr[i+1]=temp;
			swap_flag = true;
		}
	}
               
// SI no se intercambiaron dos elementos en el ciclo, luego regresa, ya que el arreglo está ordenado
	if(swap_flag == false)
		return;
	bubblesort(arr,n-1);	//Recursividad para el arreglo restante
}
</code></pre><h3 id="ejemplo-en-swift"><strong>Ejemplo en Swift</strong></h3><pre><code class="language-swift">func bubbleSort(_ inputArray: [Int]) -&gt; [Int] {
    guard inputArray.count &gt; 1 else { return inputArray } 
    // asegúrandose de que nuestro arreglo de entrada tenga más de 1 elemento
    var numbers = inputArray // los argumentos de la función son constantes por defecto en Swift, así que hacemos una copia
    for i in 0..&lt;(numbers.count - 1) {
        for j in 0..&lt;(numbers.count - i - 1) {
            if numbers[j] &gt; numbers[j + 1] {
                numbers.swapAt(j, j + 1)
            }
        }
    }
    return numbers // devuelve el arreglo ordenado
} 
</code></pre><h3 id="ejemplo-en-python"><strong>Ejemplo en Python</strong></h3><pre><code class="language-py">def bubbleSort(arr): 
    n = len(arr) 
    for i in range(n):
        for j in range(0, n-i-1):
                if arr[j] &gt; arr[j+1] : 
                        arr[j], arr[j+1] = arr[j+1], arr[j]
    print(arr)
</code></pre><h3 id="ejemplo-en-php"><strong>Ejemplo en PHP</strong></h3><pre><code class="language-php">function bubble_sort($arr) {
    $size = count($arr)-1;
    for ($i=0; $i&lt;$size; $i++) {
        for ($j=0; $j&lt;$size-$i; $j++) {
            $k = $j+1;
            if ($arr[$k] &lt; $arr[$j]) {
                // Intercambiar elementos en índices: $j, $k
                list($arr[$j], $arr[$k]) = array($arr[$k], $arr[$j]);
            }
        }
    }
    return $arr; // devuelve el arreglo ordenado
}

$arr = array(1,3,2,8,5,7,4,0);
print("Antes de ordenar");
print_r($arr);

$arr = bubble_sort($arr);
print("Después de ordenar usando ordenación por burbujas");
print_r($arr);
</code></pre><h3 id="ejemplo-en-c"><strong><strong>E</strong>jemplo en C</strong><br></h3><pre><code class="language-c">#include &lt;stdio.h&gt;

int BubbleSort(int array[], int n);

int main(void) {
  int arr[] = {10, 2, 3, 1, 4, 5, 8, 9, 7, 6};
  BubbleSort(arr, 10);

  for (int i = 0; i &lt; 10; i++) {
    printf("%d", arr[i]);
  }
  return 0;
}
int BubbleSort(int array[], n)
{
for (int i = 0 ; i &lt; n - 1; i++)
  {
    for (int j = 0 ; j &lt; n - i - 1; j++)     //n es la longitud del arreglo
    {
      if (array[j] &gt; array[j+1])      // Para uso en orden decreciente
      {
        int swap   = array[j];
        array[j]   = array[j+1];
        array[j+1] = swap;
      }
    }
  }
}
</code></pre><h2 id="ordenaci-n-r-pida"><strong>Ordenación rápida</strong></h2><p>Quick sort es un eficiente algoritmo de ordenación divide y vencerás. La complejidad de tiempo del caso promedio de Quick Sort es O (nlog (n)) y la complejidad de tiempo del peor caso es O (n ^ 2) dependiendo de la selección del elemento pivote, que divide el arreglo actual en dos sub arreglos.</p><p>Por ejemplo, la complejidad de tiempo de Quick Sort es aproximadamente <code>O(nlog(n))</code> cuando la selección del pivote divide el arreglo original en dos subarreglos de tamaño casi igual.</p><p>Por otro lado, si el algoritmo, que selecciona el elemento pivote de los arreglos de entrada, genera consistentemente 2 subarreglos con una gran diferencia en términos de tamaños de arreglo, el algoritmo de ordenación rápida puede lograr la complejidad de tiempo del peor caso de O(n^2 ).</p><p>Los pasos involucrados en Quick Sort son:</p><ul><li>Elige un elemento para que sirva como pivote, en este caso, el último elemento del arreglo es el pivote. </li><li>Particionamiento: Ordena el arreglo de tal manera que todos los elementos menores que el pivote estén a la izquierda y todos los elementos mayores que el pivote estén a la derecha.</li><li>Llama a Quicksort de forma recursiva, teniendo en cuenta el pivote anterior para subdividir adecuadamente los arreglos izquierdo y derecho. (Se puede encontrar una explicación más detallada en los comentarios a continuación)</li></ul><h2 id="implementaciones-de-ejemplo-en-varios-lenguajes"><strong>Implementaciones de ejemplo en varios lenguajes</strong></h2><h3 id="implementaci-n-en-javascript-"><strong>Implementación en JavaScript:</strong></h3><pre><code class="language-js">const arr = [6, 2, 5, 3, 8, 7, 1, 4];

const quickSort = (arr, start, end) =&gt; {

  if(start &lt; end) {

   // Puedes obtener información sobre cómo se deriva el valor de pivote en los comentarios a continuación
    let pivot = partition(arr, start, end);

   // Asegúrate de leer los comentarios a continuación para comprender por qué se usan pivote - 1 y pivote + 1
// Estas son las llamadas recursivas a quickSort
    quickSort(arr, start, pivot - 1);
    quickSort(arr, pivot + 1, end);
  } 

}

const partition = (arr, start, end) =&gt; { 
  let pivot = end;
    // Establece i en start - 1 para que puedas acceder al primer índice en caso de que el valor en arr[0] sea mayor que arr[pivot]
  // Los comentarios posteriores se expondrán sobre el comentario anterior
  let i = start - 1,
      j = start;

  // Incrementa j hasta el índice que precede al pivote
  while (j &lt; pivot) {

    // If the value is greater than the pivot increment j
    // Si el valor es mayor que el pivote incrementa j
    if (arr[j] &gt; arr[pivot]) {
      j++;
    }

    // Cuando el valor en arr[j] es menor que el pivote:
    // incrementa i (arr[i] será un valor mayor que arr[pivot]) e intercambia el valor en arr[i] y arr[j]
    else {
      i++;
      swap(arr, j, i);
      j++;
    }

  }

  //El valor de arr[i + 1] será mayor que el valor de arr[pivot]
  swap(arr, i + 1, pivot);

    //Devuelves i + 1, ya que los valores a la izquierda son menores que arr[i+1], y los valores a la derecha son mayores que arr[i + 1]
   // Como tal, cuando se llama a las ordenaciones rápidas recursivas, los nuevos subconjuntos no incluirán este valor de pivote utilizado anteriormente 
  return i + 1;
}

const swap = (arr, firstIndex, secondIndex) =&gt; {
  let temp = arr[firstIndex];
  arr[firstIndex] = arr[secondIndex];
  arr[secondIndex] = temp;
}

quickSort(arr, 0, arr.length - 1);
console.log(arr);
</code></pre><h3 id="implementaci-n-en-c"><strong>Implementación en C</strong></h3><pre><code class="language-c">#include&lt;stdio.h&gt;  
void swap(int* a, int* b) 
{ 
    int t = *a; 
    *a = *b; 
    *b = t; 
}
int partition (int arr[], int low, int high) 
{ 
    int pivot = arr[high];     
    int i = (low - 1);  
  
    for (int j = low; j &lt;= high- 1; j++) 
    { 
        if (arr[j] &lt;= pivot) 
        { 
            i++;    
            swap(&amp;arr[i], &amp;arr[j]); 
        } 
    } 
    swap(&amp;arr[i + 1], &amp;arr[high]); 
    return (i + 1); 
}
void quickSort(int arr[], int low, int high) 
{ 
    if (low &lt; high) 
    {
        int pi = partition(arr, low, high); 
  
        quickSort(arr, low, pi - 1); 
        quickSort(arr, pi + 1, high); 
    } 
} 
  

void printArray(int arr[], int size) 
{ 
    int i; 
    for (i=0; i &lt; size; i++) 
        printf("%d ", arr[i]); 
    printf("n"); 
} 
  

int main() 
{ 
    int arr[] = {10, 7, 8, 9, 1, 5}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
    quickSort(arr, 0, n-1); 
    printf("Sorted array: n"); 
    printArray(arr, n); 
    return 0; 
} 
</code></pre><h3 id="implementaci-n-en-python3"><strong>Implementación en Python3</strong></h3><pre><code>import random

z=[random.randint(0,100) for i in range(0,20)]

def quicksort(z):
    if(len(z)&gt;1):        
        piv=int(len(z)/2)
        val=z[piv]
        lft=[i for i in z if i&lt;val]
        mid=[i for i in z if i==val]
        rgt=[i for i in z if i&gt;val]

        res=quicksort(lft)+mid+quicksort(rgt)
        return res
    else:
        return z
        
ans1=quicksort(z)
print(ans1)
</code></pre><h3 id="implementaci-n-en-matlab-1"><strong>Implementación en MATLAB </strong></h3><pre><code>a = [9,4,7,3,8,5,1,6,2];

sorted = quicksort(a,1,length(a));

function [unsorted] =  quicksort(unsorted, low, high)
    if low &lt; high
        [pInd, unsorted] = partition(unsorted, low, high);
        unsorted = quicksort(unsorted, low, pInd-1);
        unsorted = quicksort(unsorted, pInd+1, high);
    end

end

function [pInd, unsorted] = partition(unsorted, low, high)
    i = low-1;
    for j = low:1:high-1
        if unsorted(j) &lt;= unsorted(high)
            i = i+1;
            unsorted([i,j]) = unsorted([j,i]);
            
        end
    end
    unsorted([i+1,high]) = unsorted([high,i+1]);
    pInd = i+1;

end
</code></pre><p>La complejidad espacial de ordenación rápida es <code>O(n)</code>. Esta es una mejora con respecto a otros algoritmos de ordenación de divide y vencerás, que ocupan espacio <code>O(nlong(n))</code>.</p><p>La ordenación rápida logra esto cambiando el orden de los elementos dentro del arreglo dado. Compara esto con el algoritmo <a href="https://guide.freecodecamp.org/algorithms/sorting-algorithms/merge-sort">de ordenación por combinación</a> que crea 2 arreglos, cada longitud <code>n/2</code>, en cada llamada de función.</p><p>Sin embargo, existe el problema de que este algoritmo de ordenación es de tiempo <code>O(n*n)</code>si el pivote siempre se mantiene en el medio. Esto se puede superar utilizando un pivote aleatorio.</p><h3 id="complejidad"><strong>Complejidad</strong></h3><p>Mejor, promedio, peor, memoria: n log(n)n log(n)n 2log(n). No es un algoritmo estable, y la ordenación rápida generalmente se realiza en el lugar con el espacio de pila O (log (n)).</p><p>La complejidad espacial de ordenación rápida es O(n). Esta es una mejora con respecto a otros algoritmos de ordenación de divide y vencerás, que ocupan espacio O(n log(n)).</p><h2 id="timsort"><strong>Timsort</strong></h2><p>Timsort es un algoritmo de ordenación rápido que funciona con una complejidad estable de O(N log(N)).</p><p>Timsort es una mezcla de Insertion Sort y Mergesort. Este algoritmo se implementa en Arrays.sort() de Java, así como en sorted() y sort() de Python. Las partes más pequeñas se ordenan mediante Ordenación por inserción y luego se fusionan mediante Mergesort.</p><p>Una implementación rápida en Python:</p><pre><code class="language-py">def binary_search(the_array, item, start, end):
    if start == end:
        if the_array[start] &gt; item:
            return start
        else:
            return start + 1
    if start &gt; end:
        return start

    mid = round((start + end)/ 2)

    if the_array[mid] &lt; item:
        return binary_search(the_array, item, mid + 1, end)

    elif the_array[mid] &gt; item:
        return binary_search(the_array, item, start, mid - 1)

    else:
        return mid

"""
Ordenación por inserción que usa timsort si el tamaño del arreglo es pequeño o si el tamaño de la "carrera" es pequeño
"""
def insertion_sort(the_array):
    l = len(the_array)
    for index in range(1, l):
        value = the_array[index]
        pos = binary_search(the_array, value, 0, index - 1)
        the_array = the_array[:pos] + [value] + the_array[pos:index] + the_array[index+1:]
    return the_array

def merge(left, right):
    """
  Toma dos listas ordenadas y devuelve una sola lista ordenada comparando los
    elementos de uno en uno.
    [1, 2, 3, 4, 5, 6]
    """
    if not left:
        return right
    if not right:
        return left
    if left[0] &lt; right[0]:
        return [left[0]] + merge(left[1:], right)
    return [right[0]] + merge(left, right[1:])

def timsort(the_array):
    runs, sorted_runs = [], []
    length = len(the_array)
    new_run = [the_array[0]]

    # para cada i en el rango de 1 a la longitud del arreglo
    for i in range(1, length):
        # si i está al final de la lista
        if i == length - 1:
            new_run.append(the_array[i])
            runs.append(new_run)
            break
        # si el i-ésimo elemento del arreglo es menor que el anterior
        if the_array[i] &lt; the_array[i-1]:
            # si new_run se establece en Ninguno (NULL)
            if not new_run:
                runs.append([the_array[i]])
                new_run.append(the_array[i])
            else:
                runs.append(new_run)
                new_run = []
        # de lo contrario si es igual o mayor 
        else:
            new_run.append(the_array[i])

    # para cada elemento en las ejecuciones, agrégalo usando la ordenación por inserción
    for item in runs:
        sorted_runs.append(insertion_sort(item))
    
    # por cada ejecución en sorted_runs, fusionarlos

    sorted_array = []
    for run in sorted_runs:
        sorted_array = merge(sorted_array, run)

    print(sorted_array)

timsort([2, 3, 1, 5, 6, 7])
</code></pre><h3 id="complejidad-"><strong>Complejidad:</strong></h3><p>Tim sort tiene una complejidad estable de O(N log(N)) y se compara muy bien con Quicksort. <a href="https://cdn-images-1.medium.com/max/1600/1*1CkG3c4mZGswDShAV9eHbQ.png">En este cuadro</a> se puede encontrar una comparación de complejidades.</p><h2 id="merge-sort-ordenar-por-fusi-n-"><strong><strong>Merge Sort</strong> (Ordenar por fusión)</strong></h2><p>Merge Sort es un algoritmo <a href="https://guide.freecodecamp.org/algorithms/divide-and-conquer-algorithms">Divide y vencerás</a>. Divide el arreglo de entrada en dos mitades, se llama a sí mismo para las dos mitades y luego fusiona las dos mitades ordenadas. La mayor parte del algoritmo tiene dos matrices ordenadas, y tenemos que fusionarlas en un único arreglo ordenado. Todo el proceso de ordenar un arreglo de N enteros se puede resumir en tres pasos:</p><ul><li>Divide el arreglo en dos mitades.</li><li>Ordena la mitad izquierda y la mitad derecha usando el mismo algoritmo recurrente.</li><li>Combina las mitades ordenadas.</li></ul><p>Hay algo conocido como el <a href="https://en.wikipedia.org/wiki/Cheney%27s_algorithm">algoritmo de dos dedos</a> que nos ayuda a fusionar dos arreglos ordenados. El uso de esta subrutina y la llamada a la función de ordenación por fusión en las mitades del arreglo de forma recursiva nos dará el arreglo ordenado final que estamos buscando.</p><p>Dado que este es un algoritmo basado en la recursión, tenemos una relación de recurrencia para él. Una relación de recurrencia es simplemente una forma de representar un problema en términos de sus subproblemas.</p><p><code>T(n) = 2 * T(n / 2) + O(n)</code></p><p>Poniéndolo en lenguaje sencillo, dividimos el subproblema en dos partes en cada paso y tenemos una cantidad lineal de trabajo que tenemos que hacer para unir las dos mitades ordenadas en cada paso.</p><h3 id="complejidad-1"><strong>Complejidad</strong></h3><p>La mayor ventaja de usar Merge sort es que la <a href="https://www.youtube.com/watch?v=V42FBiohc6c&amp;list=PL2_aWCzGMAwI9HK8YPVBjElbLbI3ufctn" rel="nofollow">complejidad del tiempo</a> es solo n*log(n) para ordenar un Array completo. Es mucho mejor que n ^ 2 tiempo de ejecución de ordenación de burbuja o ordenación de inserción.</p><p>Antes de escribir el código, comprendamos cómo funciona la ordenación por combinación o fusión con la ayuda de un diagrama.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/06/4712ef1a5d856dbb4af393fcc08a820a38787395.png" class="kg-image" alt="4712ef1a5d856dbb4af393fcc08a820a38787395" width="600" height="400" loading="lazy"></figure><ul><li>Inicialmente tenemos un arreglo de 6 enteros no ordenados Arr(5, 8, 3, 9, 1, 2) </li><li>Dividimos el arreglo en dos mitades Arr1 = (5, 8, 3) y Arr2 = (9, 1, 2).</li><li>De nuevo, los dividimos en dos mitades: Arr3 = (5, 8) and Arr4 = (3) and Arr5 = (9, 1) and Arr6 = (2)</li><li>De nuevo, los dividimos en dos mitades: Arr7 = (5), Arr8 = (8), Arr9 = (9), Arr10 = (1) y Arr6 = (2)</li><li>Ahora compararemos los elementos en estos subconjuntos para fusionarlos.</li></ul><h3 id="propiedades--1"><strong>Propiedades:</strong></h3><ul><li>Complejidad espacial: O(n)</li><li>Complejidad temporal: O(n*log(n)). La complejidad del tiempo para Merge Sort podría no ser obvia a primera vista. El factor log(n) que entra se debe a la relación de recurrencia que hemos mencionado antes.</li><li>Ordenar en el lugar: No en una implementación típica</li><li>Estable: Sí</li><li>Paralelizable: sí (varias variantes paralelas se analizan en la tercera edición de Introducción a los algoritmos de Cormen, Leiserson, Rivest y Stein).</li></ul><h3 id="implementaci-n-de-c--1"><strong><strong><strong>I</strong></strong>m<strong><strong>ple</strong></strong>m<strong><strong>entación de C++</strong></strong></strong></h3><pre><code class="language-cpp">void merge(int array[], int left, int mid, int right)
{
    int i, j, k;

    // Tamaño de la sublista izquierda
int size_left = mid - left + 1;

// Tamaño de la sublista derecha
int size_right =  right - mid;

/* crea arreglos temporales */
int Left[size_left], Right[size_right];

/* Copiar datos a arreglos temporales L[] y R[] */
for(i = 0; i &lt; size_left; i++)
{
    Left[i] = array[left+i];
}

for(j = 0; j &lt; size_right; j++)
{
    Right[j] = array[mid+1+j];
}

// Combinar los arreglos temporales de nuevo en arr[left..right]
i = 0; // Índice inicial del subarreglo izquierdo
j = 0; // Índice inicial del subarreglo derecho
k = left; // Índice inicial del subarreglo fusionado

while (i &lt; size_left &amp;&amp; j &lt; size_right)
{
    if (Left[i] &lt;= Right[j])
    {
        array[k] = Left[i];
        i++;
    }
    else
    {
        array[k] = Right[j];
        j++;
    }
    k++;
}

// Copia los elementos restantes de Left[]
while (i &lt; size_left)
{
    array[k] = Left[i];
    i++;
    k++;
}

// Copiar el resto de elementos de Right[]
while (j &lt; size_right)
{
    array[k] = Right[j];
    j++;
    k++;
}
}

void mergeSort(int array[], int left, int right)
{
    if(left &lt; right)
    {
        int mid = (left+right)/2;

        // Ordenar la primera y la segunda mitad
    mergeSort(array, left, mid);
    mergeSort(array, mid+1, right);

    // Finalmente fusionarlas
    merge(array, left, mid, right);
}
}</code></pre><h3 id="implementaci-n-de-javascript"><strong><strong><strong>Implementación de JavaScript</strong></strong></strong></h3><pre><code class="language-js">function mergeSort (arr) {
  if (arr.length &lt; 2) return arr;
  var mid = Math.floor(arr.length /2);
  var subLeft = mergeSort(arr.slice(0,mid));
  var subRight = mergeSort(arr.slice(mid));
  return merge(subLeft, subRight);
}</code></pre><p>Primero verificamos la longitud del arreglo. Si es 1, simplemente devolvemos el arreglo. Este sería nuestro caso base. De lo contrario, encontraremos el valor medio y dividiremos el arreglo en dos mitades. Ahora ordenaremos ambas mitades con llamadas recursivas a la función MergeSort.</p><pre><code class="language-js">function merge (a,b) {
    var result = [];
    while (a.length &gt;0 &amp;&amp; b.length &gt;0)
        result.push(a[0] &lt; b[0]? a.shift() : b.shift());
    return result.concat(a.length? a : b);
}</code></pre><p>Cuando fusionamos las dos mitades, almacenamos el resultado en un arreglo auxiliar. Compararemos el elemento inicial del arreglo izquierdo con el elemento inicial del arreglo derecho. Cualquiera que sea menor se insertará en el arreglo de resultados y lo eliminaremos de los arreglos respectivos usando el operador [shift()]. Si aún terminamos con valores en el arreglo izquierdo o derecho, simplemente los concatenaremos al final del resultado. Aquí está el resultado ordenado:</p><pre><code class="language-js">var test = [5,6,7,3,1,3,15];
console.log(mergeSort(test));

&gt;&gt; [1, 3, 3, 5, 6, 7, 15]</code></pre><h3 id="un-tutorial-de-youtube-de-ordenaci-n-por-combinaci-n"><strong>Un tutorial de YouTube de ordenación por combinación</strong></h3><p>Aquí hay un buen video de YouTube que <a href="https://www.youtube.com/watch?v=TzeBrDU-JaY">explica el tema en detalle</a> .</p><h3 id="implementaci-n-en-js"><strong>Implementación en JS</strong></h3><pre><code class="language-js">const list = [23, 4, 42, 15, 16, 8, 3]

const mergeSort = (list) =&gt;{
  if(list.length &lt;= 1) return list;
  const middle = list.length / 2 ;
  const left = list.slice(0, middle);
  const right = list.slice(middle, list.length);
  return merge(mergeSort(left), mergeSort(right));
}

const merge = (left, right) =&gt; {
  var result = [];
  while(left.length || right.length) {
    if(left.length &amp;&amp; right.length) {
      if(left[0] &lt; right[0]) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    } else if(left.length) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    }
  return result;
}

console.log(mergeSort(list)) // [ 3, 4, 8, 15, 16, 23, 42 ]
</code></pre><h3 id="implementaci-n-en-c-1"><strong>Implementación en C</strong></h3><pre><code class="language-c">#include&lt;stdlib.h&gt; 
#include&lt;stdio.h&gt;
void merge(int arr[], int l, int m, int r) 
{ 
    int i, j, k; 
    int n1 = m - l + 1; 
    int n2 =  r - m; 
  
    
    int L[n1], R[n2]; 
  
    for (i = 0; i &lt; n1; i++) 
        L[i] = arr[l + i]; 
    for (j = 0; j &lt; n2; j++) 
        R[j] = arr[m + 1+ j];
    i = 0; 
    j = 0; 
    k = l; 
    while (i &lt; n1 &amp;&amp; j &lt; n2) 
    { 
        if (L[i] &lt;= R[j]) 
        { 
            arr[k] = L[i]; 
            i++; 
        } 
        else
        { 
            arr[k] = R[j]; 
            j++; 
        } 
        k++; 
    } 
  
    
    while (i &lt; n1) 
    { 
        arr[k] = L[i]; 
        i++; 
        k++; 
    } 
  
    while (j &lt; n2) 
    { 
        arr[k] = R[j]; 
        j++; 
        k++; 
    } 
} 
  
void mergeSort(int arr[], int l, int r) 
{ 
    if (l &lt; r) 
    {  
        int m = l+(r-l)/2; 
  
        
        mergeSort(arr, l, m); 
        mergeSort(arr, m+1, r); 
  
        merge(arr, l, m, r); 
    } 
}
void printArray(int A[], int size) 
{ 
    int i; 
    for (i=0; i &lt; size; i++) 
        printf("%d ", A[i]); 
    printf("\n"); 
} 
int main() 
{ 
    int arr[] = {12, 11, 13, 5, 6, 7}; 
    int arr_size = sizeof(arr)/sizeof(arr[0]); 
  
    printf("Given array is \n"); 
    printArray(arr, arr_size); 
  
    mergeSort(arr, 0, arr_size - 1); 
  
    printf("\nSorted array is \n"); 
    printArray(arr, arr_size); 
    return 0; 
</code></pre><h3 id="implementaci-n-en-c-"><strong>Implementación en C++</strong></h3><p>Consideremos el arreglo A = {2,5,7,8,9,12,13} y el arreglo B = {3,5,6,9,15} y queremos que el arreglo C también esté en orden ascendente.</p><pre><code class="language-cpp">void mergesort(int A[],int size_a,int B[],int size_b,int C[])
{
     int token_a,token_b,token_c;
     for(token_a=0, token_b=0, token_c=0; token_a&lt;size_a &amp;&amp; token_b&lt;size_b; )
     {
          if(A[token_a]&lt;=B[token_b])
               C[token_c++]=A[token_a++];
          else
               C[token_c++]=B[token_b++];
      }
      
      if(token_a&lt;size_a)
      {
          while(token_a&lt;size_a)
               C[token_c++]=A[token_a++];
      }
      else
      {
          while(token_b&lt;size_b)
               C[token_c++]=B[token_b++];
      }

}
</code></pre><h3 id="implementaci-n-en-python-1"><strong>Implementación en Python</strong></h3><pre><code class="language-py">def merge(left,right,compare):
	result = [] 
	i,j = 0,0
	while (i &lt; len(left) and j &lt; len(right)):
		if compare(left[i],right[j]):
			result.append(left[i])
			i += 1
		else:
			result.append(right[j])
			j += 1
	while (i &lt; len(left)):
		result.append(left[i])
		i += 1
	while (j &lt; len(right)):
		result.append(right[j])
		j += 1
	return result

def merge_sort(arr, compare = lambda x, y: x &lt; y):
    #Utilizando la función lambda para ordenar el arreglo en orden (creciente y decreciente).
     #Por defecto ordena el arreglo en orden creciente
	if len(arr) &lt; 2:
		return arr[:]
	else:
		middle = len(arr) // 2
		left = merge_sort(arr[:middle], compare)
		right = merge_sort(arr[middle:], compare)
		return merge(left, right, compare) 

arr = [2,1,4,5,3]
print(merge_sort(arr))
</code></pre><h3 id="implementaci-n-en-java-1"><strong>Implementación en Java</strong></h3><pre><code class="language-java">public class mergesort {

	public static int[] mergesort(int[] arr,int lo,int hi) {
		
		if(lo==hi) {
			int[] ba=new int[1];
			ba[0]=arr[lo];
			return ba;
		}
		
		int mid=(lo+hi)/2;
		int arr1[]=mergesort(arr,lo,mid);
		int arr2[]=mergesort(arr,mid+1,hi);
		return merge(arr1,arr2);
	}
	
	public static int[] merge(int[] arr1,int[] arr2) {
		int i=0,j=0,k=0;
		int n=arr1.length;
		int m=arr2.length;
		int[] arr3=new int[m+n];
		while(i&lt;n &amp;&amp; j&lt;m) {
			if(arr1[i]&lt;arr2[j]) {
				arr3[k]=arr1[i];
				i++;
			}
			else {
				arr3[k]=arr2[j];
				j++;
			}
			k++;
		}
		
		while(i&lt;n) {
			arr3[k]=arr1[i];
			i++;
			k++;
		}
		
		while(j&lt;m) {
			arr3[k]=arr2[j];
			j++;
			k++;
		}
		
		return arr3;
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int arr[]= {2,9,8,3,6,4,10,7};
		int[] so=mergesort(arr,0,arr.length-1);
		for(int i=0;i&lt;arr.length;i++)
			System.out.print(so[i]+" ");
	}

}
</code></pre><h3 id="ejemplo-en-java-1"><strong>Ejemplo en Java</strong></h3><pre><code class="language-java">public class mergesort {
  public static int[] mergesort(int[] arr, int lo, int hi) {
    if (lo == hi) {
      int[] ba = new int[1];
      ba[0] = arr[lo];
      return ba;
    }
    int mid = (lo + hi) / 2;
    int arr1[] = mergesort(arr, lo, mid);
    int arr2[] = mergesort(arr, mid + 1, hi);
    return merge(arr1, arr2);
  }

  public static int[] merge(int[] arr1, int[] arr2) {
    int i = 0, j = 0, k = 0;
    int n = arr1.length;
    int m = arr2.length;
    int[] arr3 = new int[m + n];
    while (i &lt; n &amp;&amp; j &lt; m) {
      if (arr1[i] &lt; arr2[j]) {
        arr3[k] = arr1[i];
        i++;
      } else {
        arr3[k] = arr2[j];
        j++;
      }
      k++;
    }
    while (i &lt; n) {
      arr3[k] = arr1[i];
      i++;
      k++;
    }
    while (j &lt; m) {
      arr3[k] = arr2[j];
      j++;
      k++;
    }
    return arr3;
  }

  public static void main(String[] args) {
    int arr[] = {2, 9, 8, 3, 6, 4, 10, 7};
    int[] so = mergesort(arr, 0, arr.length - 1);
    for (int i = 0; i &lt; arr.length; i++)
      System.out.print(so[i] + " ");
  }
}
</code></pre> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo resolver el problema de la Torre de Hanoi: Una guía ilustrada del algoritmo ]]>
                </title>
                <description>
                    <![CDATA[ Antes de empezar, hablemos de qué es el problema de la Torre de Hanoi. Bueno, este es un divertido juego de rompecabezas en el que el objetivo es mover una pila completa de discos desde la posición de origen a otra posición. Se siguen tres reglas simples: 1.Solo se puede ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-resolver-el-problema-de-la-torre-de-hanoi-una-guia-ilustrada-del-algoritmo/</link>
                <guid isPermaLink="false">641d4027083d0a0781fde073</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrés  Torres ]]>
                </dc:creator>
                <pubDate>Mon, 03 Apr 2023 20:22:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/03/5f9ca6e9740569d1a4ca739a.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/analyzing-the-algorithm-to-solve-the-tower-of-hanoi-problem-686685f032e3/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Solve the Tower of Hanoi Problem - An Illustrated Algorithm Guide</a>
      </p><p>Antes de empezar, hablemos de qué es el problema de la Torre de Hanoi. Bueno, este es un divertido juego de rompecabezas en el que el objetivo es mover una pila completa de discos desde la posición de origen a otra posición. Se siguen tres reglas simples:</p><p>1.Solo se puede mover un disco a la vez.<br>2.Cada movimiento consiste en tomar el disco superior de una de las pilas y colocarlo encima de otra pila. En otras palabras, un disco solo se puede mover si es el disco superior de una pila.<br>3.No se puede colocar un disco más grande encima de un disco más pequeño.</p><p>Ahora, intentemos imaginar un escenario. Supongamos que tenemos una pila de tres discos. Nuestro trabajo es mover esta pila del origen A al destino C. ¿Cómo hacemos esto?</p><p>Antes de que podamos llegar allí, imaginemos que hay un punto intermedio B.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-21.png" class="kg-image" alt="image-21" width="500" height="375" loading="lazy"><figcaption><a href="http://www.texample.net/tikz/examples/towers-of-hanoi/">Tres discos.</a></figcaption></figure><p>Podemos usar a B como ayudante para terminar este trabajo. Ahora estamos listos para seguir adelante. Veamos cada uno de los pasos:</p><p>Mueve el primer disco de A a C<br>Mover el primer disco de A a B<br>Mueva el primer disco de C a B<br>Mueve el primer disco de A a C<br>Mueva el primer disco de B a A<br>Mueva el primer disco de B a C<br>Mueve el primer disco de A a C<br>¡Excelente! Hemos resuelto nuestro problema.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-22.png" class="kg-image" alt="image-22" width="320" height="98" loading="lazy"><figcaption><a href="https://en.wikipedia.org/wiki/Tower_of_Hanoi">Torre de Hanoi para 3 discos. Wikipedia</a></figcaption></figure><p><br><br>Puedes ver la imagen animada de arriba para una mejor comprensión.</p><p>Ahora, intentemos construir el algoritmo para resolver el problema. Espera, tenemos una nueva palabra aquí: "<strong>Algoritmo</strong>". ¿Qué es eso? ¿Alguna idea? No hay problema, vamos a ver.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-23.png" class="kg-image" alt="image-23" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/03/image-23.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-23.png 800w" sizes="(min-width: 720px) 720px" width="800" height="533" loading="lazy"><figcaption>Foto por <a href="https://unsplash.com/@brucemars?utm_source=medium&amp;utm_medium=referral" rel="noopener">bruce mars</a> en <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral" rel="noopener">Unsplash</a></figcaption></figure><h3 id="-qu-es-un-algoritmo"><strong>¿Qué es un algoritmo?</strong></h3><p>Un algoritmo es uno de los conceptos más importantes para un desarrollador de software. De hecho, creo que no solo es importante para el desarrollo o la programación de software, sino para todos. Los algoritmos nos afectan en nuestra vida cotidiana. Veamos cómo.</p><p>Supongamos que trabaja en una oficina. Así que cada mañana haces una serie de tareas en secuencia: primero te levantas, luego vas al baño, desayunas, te preparas para la oficina, sales de casa, luego puedes tomar un taxi o un autobús o empezar a caminar hacia la oficina y, después de un tiempo determinado, llega a su oficina. Puedes decir que todos esos pasos forman un algoritmo.</p><p>En términos simples, un algoritmo es un conjunto de tareas. Espero que no hayas olvidado los pasos que hicimos para mover tres pilas de discos de A a C. También puedes decir que esos pasos son el algoritmo para resolver el problema de la Torre de Hanoi.</p><p><em>En Matemáticas y Ciencias de la Computación; un <a href="https://en.wikipedia.org/wiki/Algorithm">algoritmo</a> es una especificación inequívoca de cómo resolver una clase de problemas. Los algoritmos pueden realizar tareas de cálculo, procesamiento de datos y razonamiento automatizado. —</em></p><p>Si observas esos pasos, puedes ver que estábamos haciendo la misma tarea varias veces: mover discos de una pila a otra. Podemos denominar a estos pasos como <strong>recursión.</strong></p><h3 id="recursi-n"><strong>Recursión</strong></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-24.png" class="kg-image" alt="image-24" width="384" height="312" loading="lazy"><figcaption>Recursión — <a href="https://giphy.com/gifs/homer-simpson-the-simpsons-3ov9jQX2Ow4bM5xxuM" rel="noopener">giphy</a></figcaption></figure><p><a href="https://en.wikipedia.org/wiki/Recursion_(computer_science)" rel="noopener"><strong><strong>Recursion</strong></strong></a><strong> </strong>es estar<strong> </strong>llamando a la misma acción desde esa acción. Al igual que en la imagen de arriba.</p><p>Entonces, hay una regla para hacer cualquier trabajo recursivo: debe haber una condición para detener la ejecución de esa acción. Espero que entiendas los conceptos básicos sobre la recursividad.</p><p>Ahora, intentemos construir un procedimiento que nos ayude a resolver el problema de la Torre de Hanoi. Estamos tratando de construir la solución usando pseudocódigo. El pseudocódigo es un método para escribir código de computadora utilizando el idioma inglés.</p><pre><code>torre(disco, origen, intermedio, destinacion)
{

}</code></pre><p>Este es el esqueleto de nuestra solución. Tomamos el número total de discos como argumento. Luego, debemos pasar el origen, el lugar intermedio y el destino para que podamos entender el mapa que usaremos para completar el trabajo.</p><p>Ahora necesitamos encontrar un estado terminal. El estado terminal es el estado en el que ya no vamos a llamar a esta función.</p><pre><code>IF disco is igual 1</code></pre><p>En nuestro caso, este sería nuestro estado terminal. Porque cuando haya un disco en nuestra pila, entonces es fácil hacer ese paso final y luego nuestra tarea estará completa. No te preocupes si no te queda claro. Cuando lleguemos al final, este concepto será más claro.</p><p>Muy bien, hemos encontrado nuestro punto de estado terminal donde movemos nuestro disco al destino de esta manera:</p><pre><code>mover disco desde el origen a nuestra destinación</code></pre><p>Ahora volvemos a llamar a nuestra función pasando estos argumentos. En ese caso, dividimos la pila de discos en dos partes. El disco más grande (disco n) está en una parte y todos los demás discos (n-1) están en la segunda parte. Allí llamamos al método dos veces para -(n-1).</p><pre><code>torre(disco - 1, origen, destinacion, intermedio)</code></pre><p>Ahora pasaremos a un ejemplo de pseudocódigo más realista. Usaremos <strong><strong>total_di</strong>scos<strong>_on_stack — 1</strong></strong> como un argumento. Después de ello, procedemos a mover nuestro disco nuevamente de esta forma:</p><pre><code>mover disco desde el origen a la destinación</code></pre><p>Después de eso, volvemos a llamar a nuestro método así:</p><pre><code>torre(disco - 1, intermedio, origen, destinacion)</code></pre><p>Veamos el pseudocódigo:</p><pre><code>torre(disco, origen, inter, dest)

IF disco is equal 1, THEN
       mover disco desde el origen a la destinación
   ELSE
       torre(disco- 1, origen, destinacion, intermedio)   // Step 1
       mover disco desde el origen a la destinación       // Step 2
       torre(disco - 1, intermedio, origen, destinacion)  // Step 3
   END IF
   
END</code></pre><p>Este es el árbol para tres discos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-25.png" class="kg-image" alt="image-25" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/03/image-25.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-25.png 800w" sizes="(min-width: 720px) 720px" width="800" height="315" loading="lazy"><figcaption>Esquema de la torre de hanoi (3 discos)</figcaption></figure><p>Este es el código completo en <strong><a href="https://www.freecodecamp.org/news/learning-ruby-from-zero-to-hero-90ad4eecc82d/">Ruby</a></strong>:</p><pre><code class="language-rb">def tower(disk_numbers, source, auxilary, destination)
  if disk_numbers == 1
    puts "#{source} -&gt; #{destination}"
    return
  end
  tower(disk_numbers - 1, source, destination, auxilary)
  puts "#{source} -&gt; #{destination}"
  tower(disk_numbers - 1, auxilary, source, destination)
  nil
end
</code></pre><p>Empleamos <code>tower(3, 'source','aux','dest')</code></p><p>Resultado:</p><pre><code>source -&gt; dest
source -&gt; aux
dest -&gt; aux
source -&gt; dest
aux -&gt; source
aux -&gt; dest
source -&gt; dest</code></pre><p>Se necesitaron siete pasos para que tres discos llegaran al destino. A esto lo llamamos un método recursivo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-26.png" class="kg-image" alt="image-26" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/03/image-26.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/03/image-26.png 800w" sizes="(min-width: 720px) 720px" width="800" height="533" loading="lazy"><figcaption>Foto por <a href="https://unsplash.com/@aronvisuals?utm_source=medium&amp;utm_medium=referral" rel="noopener">Aron Visuals</a> en <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral" rel="noopener">Unsplash</a></figcaption></figure><h3 id="c-lculos-en-la-complejidad-del-tiempo-y-el-espacio"><strong>Cálculos en la complejidad del tiempo y el espacio</strong></h3><h4 id="complejidad-temporal"><strong><a href="https://es.wikipedia.org/wiki/Complejidad_temporal">Complejidad Temporal</a></strong></h4><p>Cuando ejecutamos código o una aplicación en nuestra máquina, lleva tiempo: ciclos de CPU. Pero no es lo mismo para todas las computadoras. Por ejemplo, el tiempo de procesamiento de un core i7 y de un dual core no es el mismo. Para resolver este problema existe un concepto utilizado en informática llamado <strong>complejidad temporal.</strong></p><p><em>La complejidad del tiempo es un concepto en informática que se ocupa de la cuantificación de la cantidad de tiempo que tarda un conjunto de código o algoritmo en procesarse o ejecutarse en función de la cantidad de entrada.</em></p><p><em>En otras palabras, la complejidad del tiempo es esencialmente eficiencia, o cuánto tarda una función de programa en procesar una entrada determinada.</em></p><p>La complejidad temporal de los algoritmos se expresa más comúnmente utilizando la <a href="https://es.wikipedia.org/wiki/Cota_superior_asint%C3%B3tica#searchInput">Cota superior asintótica</a>. Una notación asintótica para representar la complejidad del tiempo.</p><p>Ahora, el tiempo requerido para mover n discos es <strong>T(n)</strong>. Hay dos llamadas recursivas para <strong>(n-1)</strong>. Hay una operación de tiempo constante para mover un disco desde el origen hasta el destino, sea m1. Por lo tanto:</p><pre><code>T(n) = 2T(n-1) + m1    ..... eq(1)</code></pre><p>Y también:</p><pre><code>T(0) = m2, a constant   ...... eq(2)
From eq (1)
T(1) = 2T(1-1) + m1
     = 2T(0)+m1
     = 2m2 + m1 ..... eq(3) [From eq 2]
T(2) = 2T(2-1) + m1
     = 2T(1) + m1
     = 4m2 + 2m1 + m1 .... eq(4) [From eq(3)]
T(3) = 2T(3-1) + m1
     = 2T(2) + m1
     = 8m2 + 4m1 + 2m1 + m1  [From eq(4)]</code></pre><p>A partir de estos patrones, eq(2) hasta el último, podemos decir que la complejidad temporal de este algoritmo es O(2^n) u O(a^n) donde a es una constante mayor que 1. Por lo tanto, tiene una exponencial complejidad del tiempo. Para el aumento único del tamaño del problema, el tiempo requerido es el doble del anterior. Esto es computacionalmente muy costoso.<strong> La mayoría de los programas recursivos toman un tiempo exponencial, y por eso es muy difícil escribirlos iterativamente.</strong></p><h4 id="complejidad-espacial"><strong><a href="https://www.cs.northwestern.edu/academics/courses/311/html/space-complexity.html" rel="noopener">Complejidad Espacial</a></strong></h4><p>Después de la explicación del análisis de la complejidad del tiempo, creo que ahora puedes adivinar qué es esto... Este es el cálculo del espacio requerido en RAM para ejecutar un código o una aplicación.</p><p>En nuestro caso, el espacio para el parámetro de cada llamada es independiente de n, lo que significa que es constante. Que sea J. Cuando hacemos la segunda llamada recursiva, la primera ha terminado. Eso significa que podemos reutilizar el espacio después de terminar el primero. Por eso:</p><p>Por tanto:</p><pre><code>T(n) = T(n-1) + k .... eq(1)
T(0) = k, [constant] .... eq(2)
T(1) = T(1-1) + k
     = T(0) + k
     = 2K
T(2) = 3k
T(3) = 4k</code></pre><p>Entonces la complejidad del espacio es O(n).</p><p>Después de estos análisis, podemos ver que la complejidad temporal de este algoritmo es exponencial, pero la complejidad espacial es lineal.</p><p><strong>Conclusión</strong><br>A partir de este artículo, espero que ahora puedas entender el rompecabezas de la <strong>Torre de Hanoi</strong> y cómo resolverlo. Además, traté de brindarle una comprensión básica sobre los<strong> algoritmos, su importancia, recursividad, pseudocódigo, complejidad temporal </strong>y <strong>complejidad espacial. </strong><br></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ La complejidad de los algoritmos simples y las estructuras de datos en JS ]]>
                </title>
                <description>
                    <![CDATA[ En el articulo anterior "Algoritmos simples y estructuras de datos en JS" [https://medium.com/free-code-camp/a-step-towards-computing-as-a-science-algorithms-data-structures-4c0e2d6ae79a] . Discutimos algoritmos simples(búsquedas lineales y binarias; selección e inserción ordenada). Aquí, continúo con el concepto de complejidad y su aplicación a algoritmos y estructuras de datos. Complejidad La complejidad es un factor involucrado en un proceso ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/la-complejidad-de-los-algoritmos-simples-y-las-estructuras-de-datos-en-js/</link>
                <guid isPermaLink="false">60077956a4e0700982a9f2a8</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mitchell Contreras ]]>
                </dc:creator>
                <pubDate>Fri, 14 May 2021 05:30:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2021/05/1_1bB3zrN0WrNC6ErArRF6cQ-1-.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>En el articulo anterior <a href="https://medium.com/free-code-camp/a-step-towards-computing-as-a-science-algorithms-data-structures-4c0e2d6ae79a">"Algoritmos simples y estructuras de datos en JS"</a>. Discutimos algoritmos simples(búsquedas lineales y binarias; selección e inserción ordenada). Aquí, continúo con el concepto de complejidad y su aplicación a algoritmos y estructuras de datos.</p>
<h2 id="complejidad">Complejidad</h2>
<p>La <strong>complejidad</strong> es un factor involucrado en un proceso complejo. Con respecto a los algoritmos y las estructuras de datos; esto puede ser por <strong>tiempo</strong> o <strong>espacio</strong> (espacio en disco) necesario para realizar una tarea específica (buscar, clasificar o acceder a los datos) en una estructura de datos determinada. La eficiencia del rendimiento de una tarea depende del número de operaciones requeridas para completarla.</p>
<p><strong>Sacar la basura</strong> puede requerir 3 pasos (atar, sacar y tirar en el basurero). <strong>Sacar la basura</strong> puede ser simple, pero si la vas a sacar después de una larga semana, es posible que no puedas completar la tarea debido a la <strong>falta de espacio</strong> en el basurero.</p>
<p><strong>Aspirar una habitación</strong> puede requerir muchos pasos repetitivos (encender la aspiradora, aspirar repetidamente los espacios de la habitación y apagarla). Mientras más grande sea la habitación, <strong>más tiempo llevará</strong> aspirar la habitación.</p>
<p><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/first-Image.png" alt="n" width="600" height="371" loading="lazy"></p>
<p>Entonces, existe una relación entre el número de operaciones realizadas y el número de elementos operados. Tener mucha basura (elementos) requiere sacarla muchas veces. Esto puede generar un problema de <strong>complejidad espacial (space complexity)</strong>. Tener una gran cantidad metros cuadrados (elementos) requiere aspirar muchas veces. Esto puede dar lugar a un problema de <strong>complejidad en tiempo (time complexity)</strong>.</p>
<p>Ya sea que esté <strong>sacando la basura</strong> o <strong>aspirando una habitación</strong>, podría decir que el <strong>orden de la operación (O)</strong> incrementa exactamente con el <strong>número de elementos (n)</strong>. Si tengo 1 bolsa de basura, tengo que sacar la basura una vez. Si tengo 2 bolsas de basura, tengo que hacer la misma tarea dos veces, asumiendo que físicamente no puedo levantar más de una bola a la vez. Entonces, the Big-O de estas tareas es O = n o O = function(n) o <strong>O(n)</strong>. Esto es una <strong>complejidad lineal (linear complexity)</strong> (una línea recta entre 1 operación: 1 elemento). Entonces, 30 operaciones equivalen a 30 elementos (línea amarilla del gráfico anterior).</p>
<p>Esto es similar a lo que sucede cuando se consideran algoritmos y estructuras de datos</p>
<h2 id="bsquedas">Búsquedas</h2>
<h3 id="bsquedalineal">Búsqueda lineal</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/lineal-search-2.gif" class="kg-image" alt="lineal-search-2" width="596" height="188" loading="lazy"></figure><!--kg-card-begin: html--><p style="text-align:center;"><a href="https://blog.penjee.com/wp-content/uploads/2015/04/binary-and-linear-search-animations.gif">origen</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>El <strong>mejor caso</strong> para buscar un elemento en una lista ordenada, es uno después del otro. Es una constante <strong>O(1)</strong>, asumiendo que es el primer elemento en tu lista. Por lo tanto, si el elemento que estás buscando siempre aparece primero en la lista, independientemente del tamaño de la lista, encontrarás el elemento inmediatamente. La complejidad de la búsqueda es constante con el tamaño de la lista. <strong>El peor caso</strong> de este tipo de busqueda es de complejidad lineal o <strong>O(n)</strong>. En otras palabras, para n elementos, tengo que buscar n veces antes de encontrar mi elemento, por eso es una búsqueda lineal.</p>
<h3 id="bsquedabinaria">Búsqueda binaria</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/binary-search-1.gif" class="kg-image" alt="binary-search-1" width="596" height="182" loading="lazy"></figure><!--kg-card-begin: html--><p style="text-align:center;"><a href="https://blog.penjee.com/wp-content/uploads/2015/04/binary-and-linear-search-animations.gif">origen</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Para búsqueda binaria, el mejor caso es O(1), esto significa que el elemento que buscas se encuentra en el centro. El peor caso es log base 2 of n o:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/log2nexponent-1.png" class="kg-image" alt="log2nexponent-1" width="236" height="92" loading="lazy"></figure><!--kg-card-begin: markdown--><p>Logaritmo o log es la manera de expresar un exponente a una base dada. Entonces, si hay 16 elementos (n = 16), esto tomará, en el peor caso, 4 pasos para encontrar el número 15 (exponente = 4).</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/binarysearchImage-1.png" class="kg-image" alt="binarysearchImage-1" width="462" height="278" loading="lazy"></figure><!--kg-card-begin: markdown--><p>o simplemente: O(log n)</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/log2144-2.png" class="kg-image" alt="log2144-2" width="159" height="57" loading="lazy"></figure><!--kg-card-begin: markdown--><h2 id="ordenamiento">Ordenamiento</h2>
<h3 id="ordenamientoburbujabubble">Ordenamiento Burbuja (Bubble)</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/bubble-2.gif" class="kg-image" alt="bubble-2" width="277" height="257" loading="lazy"></figure><!--kg-card-begin: html--><p style="text-align:center;"><a href="https://upload.wikimedia.org/wikipedia/commons/5/54/Sorting_bubblesort_anim.gif">origen</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En el ordenamiento burbuja, cada elemento es comparado con el resto de la colección para determinar el máximo valor. Por esta razón, el <strong>peor caso</strong> es de complejidad <strong>O(n²)</strong>. Pensemos en un bucle dentro de otro bucle.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/example-binary-search-2.png" class="kg-image" alt="example-binary-search-2" width="340" height="326" loading="lazy"></figure><!--kg-card-begin: markdown--><p>Entonces, por cada elemento necesitas compararlo con el resto de la colección, un total de 16 comparaciones para 4 elementos (4² = 16). En <strong>el mejor de los casos</strong> la colección ya esta ordenada, excepto por un solo elemento. Esto equivale a una sola ronda de comparaciones. Es decir, se requiren cuatro comparaciones, que es una complejidad de <strong>O(n)</strong>.</p>
<h2 id="seleccin">Selección</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/selection-2.gif" class="kg-image" alt="selection-2" width="270" height="119" loading="lazy"></figure><!--kg-card-begin: html--><p style="text-align:center;"><a href="https://codepumpkin.com/selection-sort-algorithms/">origen</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>A diferencia del <strong>ordenamiento burbuja</strong>, en lugar de intercambiar el valor más alto, intercambia el valor más bajo. El <strong>ordenamiento por selección</strong> selecciona el valor más bajo para intercambiarlo con las primeras posiciones. Pero, como requiere comparar cada elemento con el resto de la colección también tiene una complejidad <strong>O(n²)</strong>.</p>
<h2 id="insercin">Inserción</h2>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/insertion-2.gif" class="kg-image" alt="insertion-2" width="946" height="514" loading="lazy"></figure><!--kg-card-begin: html--><p style="text-align:center;"><a href="https://gfycat.com/densebaggyibis">origen</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>A diferencia de los <strong>ordenamientos burbuja y selección</strong>, el ordenamiento por inserción inserta los elementos en su posición correcta. Pero, igual que el ordenamiento anterior requiere comparar cada elemento con el resto de la colección, por lo tanto, tiene una complejidad de orden <strong>O(n²)</strong>. Al igual que el <strong>ordenamiento burbuja</strong>, si solo queda un elemento por ordenar, solo se requiere una ronda de comparaciones para insertar el elemento en su posición correcta. Es decir, en su <strong>mejor caso</strong> tiene una complejidad <strong>O(n)</strong>.</p>
<h2 id="estructurasdedatos">Estructuras de datos</h2>
<h3 id="arreglos">Arreglos</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/array-2.png" class="kg-image" alt="array-2" width="550" height="133" loading="lazy"></figure><!--kg-card-begin: markdown--><p>Debido a que se necesita un solo paso para acceder a los elementos de un arreglo usando su índice, o agregar/eliminar un elemento al final del arreglo, la complejidad para <strong>acceder, añadir o eliminar</strong> un valor de un arreglo es <strong>O(1)</strong>. Considerando, <strong>buscar linealmente</strong> a través de un arreglo usando su índice, como lo vimos antes, tiene una complejidad de <strong>O(n)</strong>.</p>
<p>Además, debido a que un <strong>cambio de un valor hacia o desde el inicio de una matriz</strong> (<em>shift o unshift</em>) requiere <strong>volver a generar los índices</strong>(es decir, eliminar un elemento en el índice 0 requiere volver a etiquetar el elemento del índice 1 como índice 0, y así sucesivamente). Tiene una complejidad <strong>O(n)</strong> ya que la generación de los índices se realiza desde el inicio hasta el final del arreglo.</p>
<h3 id="clavevalor">clave — Valor</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/espanol/news/content/images/2021/03/key-value-2.jpeg" class="kg-image" alt="key-value-2" width="480" height="257" loading="lazy"></figure><!--kg-card-begin: markdown--><p><strong>Acceder, insertar o remover</strong> un valor usando la clave es instantáneo, por lo que se entiende complejidad <strong>O(1)</strong>. Buscando en un "depósito" un elemento específico usando todas las claves disponibles es en esencia una <strong>busqueda lineal</strong>, y tiene una complejidad <strong>O(n)</strong>.</p>
<h2 id="conclusin">Conclusión</h2>
<p><strong>Complejidad</strong> es más que un tema para discutir en algoritmos y estructuras de datos. Si se usa con prudencia, puede ser útil para medir la eficiencia del trabajo que se realiza y el código que se crea para resolver problemas.</p>
<h2 id="referencia">Referencia</h2>
<p><a href="https://www.udemy.com/js-algorithms-and-data-structures-masterclass/">https://www.udemy.com/js-algorithms-and-data-structures-masterclass/</a></p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>Traducido del artículo de <a href="http://">Yung L. Leung</a> - <a href="https://www.freecodecamp.org/news/the-complexity-of-simple-algorithms-and-data-structures-in-javascript-11e25b29de1e/">The complexity of simple algorithms and data structures in JS</a></p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Significado del algoritmo divide y vencerás: Explicado con ejemplos ]]>
                </title>
                <description>
                    <![CDATA[ ¿Qué son los algoritmos "divide y vencerás"? (Y no, no es divide y concurrir) Divide y vencerás es un paradigma algorítmico (a veces llamado por error Divide y Concurrir - una adaptación en broma), similar a los paradigmas de programación Dinámica y Algoritmos ávidos o glotones. Un algoritmo Divide y ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/significado-del-algoritmo-divide-y-venceras/</link>
                <guid isPermaLink="false">5ffc3ff88c7cd154bb98639c</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Stephanie Frias ]]>
                </dc:creator>
                <pubDate>Sun, 04 Apr 2021 04:56:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2021/03/photo-1460194436988-671f763436b7-1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="-qu-son-los-algoritmos-divide-y-vencer-s-y-no-no-es-divide-y-concurrir-"><strong>¿Qué son los algoritmos "divide y vencerás"? (Y no, no es divide y concurrir)</strong></h2><p>Divide y vencerás es un paradigma algorítmico (a veces llamado por error Divide y Concurrir - una adaptación en broma), similar a los paradigmas de programación Dinámica y Algoritmos ávidos o glotones. Un algoritmo <em>Divide y Vencerás</em> típico resuelve un problema siguiendo estos 3 pasos.</p><ol><li><strong>Dividir:</strong> Descomponer el problema en sub-problemas del mismo tipo. Este paso involucra descomponer el problema original en pequeños sub-problemas. Cada sub-problema debe representar una parte del problema original. Por lo general, este paso emplea un enfoque recursivo para dividir el problema hasta que no es posible crear un sub-problema más.</li><li><strong>Vencer:</strong> &nbsp;Resolver los sub-problemas recursivamente. Este paso recibe un gran conjunto de sub-problemas a ser resueltos. Generalmente a este nivel, los problemas se resuelven por sí solos.</li><li><strong>Combinar: </strong>Combinar las respuestas apropiadamente. Cuando los sub-problemas son resueltos, esta fase los combina recursivamente hasta que estos formulan la solución al problema original. Este enfoque algorítmico trabaja recursivamente y los pasos de conquista y fusión trabajan tan a la par que parece un sólo paso.</li></ol><p>Usualmente este método nos permite hacer una reducción bastante significativa en la complejidad tiempo del algoritmo a emplear.</p><p>Por ejemplo, el método de la burbuja conlleva una complejidad de &nbsp;<code>O(n^2)</code>, mientras que el quicksort (una aplicación de Dividir y Vencer) reduce la complejidad a <code>O(nlog(n))</code>. La búsqueda linear tiene una complejidad de <code>O(n)</code>, mientras que la búsqueda binaria(otra aplicación de dividir y vencer) reduce la complejidad a &nbsp;<code>O(log(n))</code>.</p><p>A continuación algunos de los algoritmos estándar de la variedad dividir y vencer</p><p><strong>Búsqueda Binaria,</strong> es un algoritmo de búsqueda. En cada paso, el algoritmo compara el parámetro de entrada (x) con el valor del elemento en la mitad del arreglo. Si los valores coinciden, retorna el índice del elemento medio. De lo contrario, si x es mejor al elemento medio, el algoritmo recurre a la mitad izquierda del elemento medio, de lo contrario retorna la mitad a la derecha del elemento medio. </p><p><strong><strong>Quicksort</strong>, </strong> es un algoritmo de ordenamiento. El algoritmo elige un elemento pivote, reordena los elementos del arreglo de forma que todos los elementos de menor valor al del elemento pivote se mueven hacia su izquierda y los elementos de mayor valor hacia su derecha. Finalmente, el algoritmo ordena los sub-arreglos recursivamente hacia la izquierda y derecha del elemento pivote.</p><p><strong><strong>Merge Sort</strong>, </strong>es también un algoritmo de ordenamiento. Este algoritmo divide el arreglo en dos partes, ordenada cada una de ellas recursivamente, y finalmente une las dos partes ya ordenadas. El tiempo de complejidad de este algoritmo es de &nbsp;<code>O(nLogn)</code>, en el mejor caso, caso medio y en el peor de los casos. La complejidad de tiempo puede ser entendida fácilmente recurriendo a la ecuación: <code>T(n) = 2T(n/2) + n</code>.</p><p><strong>Par de puntos más cercanos, </strong>El problema es encontrar el par de puntos más cercano a otra par dado en el plano XY. Este problema puede ser resuelto en un tiempo <code>O(n^2)</code> calculando la distancia de cada par de puntos y comparando las distancias hasta encontrar la menor. El algoritmo de tipo Dividir y Vencer resuelve este problema en un tiempo &nbsp;<code>O(nLogn)</code>.</p><p><strong>Algoritmo de <strong>Strassen</strong> </strong>es un algoritmo eficiente para multiplicar dos matrices. Un método simple para multiplicar dos matrices requiere 3 bucles anidados lo que nos da una complejidad de &nbsp;<code>O(n^3)</code>. &nbsp;El algoritmo de Strassen multiplica dos matrices en un tiempo de &nbsp;<code>O(n^2.8974)</code>.</p><p><strong>El Algoritmo</strong> <strong><strong>Cooley–Tukey</strong> de Transformación Rápida de Fourier <strong>(FFT) </strong></strong>es el algoritmo más común de FFT (Transformación Rápida de Fourier en inglés). Es un algoritmo de tipo Dividir y Vencer que opera en un tiempo de <strong> </strong><code>O(nlogn)</code>.</p><p><strong>El algortimo de Karatsuba, </strong>este fue el primer algoritmo de multiplicación más rápido asintóticamente que el algoritmo cuadrático clásico de multiplicación. Este algoritmo consigue reducir la multiplicación de dos números de n dígitos a como máximo de &nbsp;n^1.585( que es la aproximación del logaritmo de 3 en base 2) multiplicaciones de un dígito. Por lo tanto es más rápido que el algoritmo clásico que requiere el producto de n^2 de números de un dígito. </p><h3 id="dividir-y-vencer-d-v-vs-programaci-n-din-mica-dp-"><strong>Dividir y vencer (D &amp; V) vs programación dinámica (DP)</strong></h3><p>Ambos paradigmas (D &amp; V y PD) dividen un problema cualquiera en sub problema y resuelve cada uno de ellos. ¿Cómo elegir uno de ellos para un problema? Dividir y Vencer debe ser empleado cuando los sub problemas no son evaluados muchas veces. De lo contrario programación dinámica o memoización debería emplearse en su lugar.</p><p>Por ejemplo, la Búsqueda Binaria es un algoritmo Dividir y Vencer, nunca evaluaremos los sub problema más de una vez. Por otro lado, el cálculo del Número Fibonacci se debe preferir la aplicación de Programación Dinámica.</p><p>Traducción del Artículo - <strong><a href="https://www.freecodecamp.org/news/divide-and-conquer-algorithms/">Divide and Conquer Algorithm Meaning: Explained with Examples</a></strong></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
