Commits (2)
n,1000,2000,5000,10000,20000,50000
ASCENDANT,1231700,00,4418640,00,30034160,00,116455020,00,474819140,00,3056061360,00
ALEATOIRE,149580,00,167500,00,356340,00,830320,00,1814660,00,5609920,00
ASCENDANT,2741266,60,4242239,60,25764592,60,103848038,60,427439882,20,2674167635,60
ALEATOIRE,296387,40,450203,40,656077,20,1670061,80,3761467,60,11957840,20
n,1000,2000,5000,10000,20000,50000
ASCENDANT,2658220,00,9699160,00,60452520,00,233926280,00,907753720,00,5800536280,00
ALEATOIRE,128340,00,122680,00,310240,00,577920,00,1311820,00,3409080,00
ASCENDANT,4158852,60,12248819,80,76968346,20,319434800,40,1309307052,20,8234666725,00
ALEATOIRE,266154,80,235807,80,574324,40,1102771,40,2151257,40,5866763,00
n,1000,2000,5000,10000,20000,50000
ASCENDANT,221140,00,242880,00,405760,00,720540,00,761520,00,1803680,00
ALEATOIRE,153900,00,219500,00,457300,00,938200,00,2106160,00,6222500,00
ASCENDANT,429293,00,541214,60,1492209,80,1450807,80,1846437,80,4429069,40
ALEATOIRE,402464,80,359252,00,1019308,20,1808727,00,3546869,20,11937927,00
n,1000,2000,5000,10000,20000,50000
ASCENDANT,136940,00,128920,00,320120,00,733100,00,1475920,00,4872060,00
ALEATOIRE,51780,00,101540,00,308340,00,616120,00,1244160,00,3500360,00
ASCENDANT,353458,80,290777,00,621226,20,1336956,20,2635875,60,9406446,40
ALEATOIRE,63701,80,132267,60,378176,00,815282,80,1745943,60,5169691,20
......@@ -61,13 +61,43 @@
<artifactId>maven-site-plugin</artifactId>
<version>3.21.0</version>
</plugin>
</plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>default-check</id>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<configLocation>
google_checks.xml
</configLocation>
</configuration>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
......@@ -102,6 +132,19 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
</plugin>
</plugins>
</reporting>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<reportSets>
<reportSet>
<reports>
<!-- select non-aggregate reports -->
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>
......@@ -12,261 +12,282 @@ import java.util.Stack;
*/
public class ArbreBinaireRecherche<E extends Comparable<E>> implements Collection<E> {
/** Classe interne représentant un nœud de l'arbre */
private class Noeud {
E cle;
Noeud gauche, droit;
Noeud(E k) {
this.cle = k;
}
/** Classe interne représentant un nœud de l'arbre */
private class Noeud {
E cle;
Noeud gauche;
Noeud droit;
Noeud(E k) {
this.cle = k;
}
}
private Noeud racine;
private int size = 0;
@Override
public boolean add(E e) {
Objects.requireNonNull(e);
// Si l’arbre est vide : racine = nouvel élément
if (racine == null) {
racine = new Noeud(e);
size++;
return true;
}
Noeud cur = racine, parent = null;
int cmp = 0;
// Recherche de la position correcte pour insérer
while (cur != null) {
parent = cur;
cmp = e.compareTo(cur.cle);
if (cmp < 0)
cur = cur.gauche; // aller à gauche
else if (cmp > 0)
cur = cur.droit; // aller à droite
else
return false; // élément déjà présent
}
// Insertion du nœud
if (cmp < 0)
parent.gauche = new Noeud(e);
else
parent.droit = new Noeud(e);
size++;
return true;
private Noeud racine;
private int taille = 0;
@Override
public boolean add(E e) {
Objects.requireNonNull(e);
// Si l’arbre est vide : racine = nouvel élément
if (racine == null) {
racine = new Noeud(e);
taille++;
return true;
}
Noeud actuel = racine;
Noeud parent = null;
int cmp = 0;
// Recherche de la position correcte pour insérer
while (actuel != null) {
parent = actuel;
cmp = e.compareTo(actuel.cle);
if (cmp < 0) {
actuel = actuel.gauche; // aller à gauche
} else if (cmp > 0) {
actuel = actuel.droit; // aller à droite
} else {
return false; // élément déjà présent
}
}
// Insertion du nœud
if (cmp < 0) {
parent.gauche = new Noeud(e);
} else {
parent.droit = new Noeud(e);
}
/**
* Recherche d'un nœud contenant la clé donnée
*/
private Noeud findNode(E e) {
Noeud cur = racine;
while (cur != null) {
int cmp = e.compareTo(cur.cle);
if (cmp == 0)
return cur;
if (cmp < 0)
cur = cur.gauche;
else
cur = cur.droit;
}
return null;
taille++;
return true;
}
/**
* Recherche d'un nœud contenant la clé donnée
*/
private Noeud findNode(E e) {
Noeud actuel = racine;
while (actuel != null) {
int cmp = e.compareTo(actuel.cle);
if (cmp == 0) {
return actuel;
}
if (cmp < 0) {
actuel = actuel.gauche;
} else {
actuel = actuel.droit;
}
}
return null;
}
@Override
public boolean contains(Object o) {
if (o == null)
return false;
try {
@SuppressWarnings("unchecked")
E e = (E) o;
return findNode(e) != null;
} catch (ClassCastException ex) {
return false;
}
@Override
public boolean contains(Object o) {
if (o == null) {
return false;
}
@Override
public boolean remove(Object o) {
if (o == null)
return false;
try {
@SuppressWarnings("unchecked")
E e = (E) o;
} catch (ClassCastException ex) {
return false;
}
// Suppression simple (remplacement par successeur)
// (Pour les benchmarks, remove n'est pas essentiel)
E key = (E) o;
Noeud parent = null, cur = racine;
// Recherche du nœud à supprimer
while (cur != null && !cur.cle.equals(key)) {
parent = cur;
if (key.compareTo(cur.cle) < 0)
cur = cur.gauche;
else
cur = cur.droit;
}
if (cur == null)
return false;
// Cas où le nœud a deux enfants
if (cur.gauche != null && cur.droit != null) {
Noeud succParent = cur, succ = cur.droit;
// Successeur = minimum du sous-arbre droit
while (succ.gauche != null) {
succParent = succ;
succ = succ.gauche;
}
// Remplacer la clé
cur.cle = succ.cle;
parent = succParent;
cur = succ;
}
// Cas 0 ou 1 enfant
Noeud child = (cur.gauche != null) ? cur.gauche : cur.droit;
if (parent == null)
racine = child;
else if (parent.gauche == cur)
parent.gauche = child;
else
parent.droit = child;
size--;
return true;
try {
@SuppressWarnings("unchecked")
E e = (E) o;
return findNode(e) != null;
} catch (ClassCastException ex) {
return false;
}
}
@Override
public Iterator<E> iterator() {
// Parcours en ordre croissant (in-order)
return new Iterator<E>() {
private final Stack<Noeud> st = init(racine);
private Stack<Noeud> init(Noeud r) {
Stack<Noeud> s = new Stack<>();
Noeud c = r;
while (c != null) {
s.push(c);
c = c.gauche;
}
return s;
}
@Override
public boolean hasNext() {
return !st.isEmpty();
}
@Override
public E next() {
Noeud n = st.pop();
E res = n.cle;
Noeud c = n.droit;
while (c != null) {
st.push(c);
c = c.gauche;
}
return res;
}
};
@Override
public boolean remove(Object o) {
if (o == null) {
return false;
}
@Override
public int size() {
return size;
try {
@SuppressWarnings("unchecked")
E e = (E) o;
} catch (ClassCastException ex) {
return false;
}
@Override
public boolean isEmpty() {
return size == 0;
// Suppression simple (remplacement par successeur)
// (Pour les benchmarks, remove n'est pas essentiel)
E key = (E) o;
Noeud parent = null;
Noeud actuel = racine;
// Recherche du nœud à supprimer
while (actuel != null && !actuel.cle.equals(key)) {
parent = actuel;
if (key.compareTo(actuel.cle) < 0) {
actuel = actuel.gauche;
} else {
actuel = actuel.droit;
}
}
@Override
public void clear() {
racine = null;
size = 0;
if (actuel == null) {
return false;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[size];
int i = 0;
// Cas où le nœud a deux enfants
if (actuel.gauche != null && actuel.droit != null) {
Noeud succParent = actuel;
Noeud suivant = actuel.droit;
// Successeur = minimum du sous-arbre droit
while (suivant.gauche != null) {
succParent = suivant;
suivant = suivant.gauche;
}
// Remplacer la clé
actuel.cle = suivant.cle;
parent = succParent;
actuel = suivant;
}
for (E e : this)
arr[i++] = e;
// Cas 0 ou 1 enfant
Noeud child = (actuel.gauche != null) ? actuel.gauche : actuel.droit;
return arr;
if (parent == null) {
racine = child;
} else if (parent.gauche == actuel) {
parent.gauche = child;
} else {
parent.droit = child;
}
@Override
public <T> T[] toArray(T[] a) {
return null; // même logique que RBTree ; omis pour brièveté
taille--;
return true;
}
@Override
public Iterator<E> iterator() {
// Parcours en ordre croissant (in-order)
return new Iterator<E>() {
private final Stack<Noeud> st = init(racine);
private Stack<Noeud> init(Noeud r) {
Stack<Noeud> s = new Stack<>();
Noeud c = r;
while (c != null) {
s.push(c);
c = c.gauche;
}
return s;
}
@Override
public boolean hasNext() {
return !st.isEmpty();
}
@Override
public E next() {
Noeud n = st.pop();
E res = n.cle;
Noeud c = n.droit;
while (c != null) {
st.push(c);
c = c.gauche;
}
return res;
}
};
}
@Override
public int size() {
return taille;
}
@Override
public boolean isEmpty() {
return taille == 0;
}
@Override
public void clear() {
racine = null;
taille = 0;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[taille];
int i = 0;
for (E e : this) {
arr[i++] = e;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c)
if (!contains(o))
return false;
return arr;
}
@Override
public <T> T[] toArray(T[] a) {
return null; // même logique que RBTree ; omis pour brièveté
}
return true;
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean ch = false;
return true;
}
for (E e : c)
ch |= add(e);
@Override
public boolean addAll(Collection<? extends E> c) {
boolean ch = false;
return ch;
for (E e : c) {
ch |= add(e);
}
@Override
public boolean removeAll(Collection<?> c) {
boolean ch = false;
return ch;
}
for (Object o : c)
ch |= remove(o);
@Override
public boolean removeAll(Collection<?> c) {
boolean ch = false;
return ch;
for (Object o : c) {
ch |= remove(o);
}
@Override
public boolean retainAll(Collection<?> c) {
List<E> del = new ArrayList<>();
return ch;
}
// Construire la liste des éléments à supprimer
for (E e : this)
if (!c.contains(e))
del.add(e);
@Override
public boolean retainAll(Collection<?> c) {
List<E> del = new ArrayList<>();
// Supprimer les éléments
for (E e : del)
remove(e);
// Construire la liste des éléments à supprimer
for (E e : this) {
if (!c.contains(e)) {
del.add(e);
}
}
return !del.isEmpty();
// Supprimer les éléments
for (E e : del) {
remove(e);
}
return !del.isEmpty();
}
}
......@@ -11,128 +11,136 @@ import java.util.Random;
public class Benchmark {
private static final Random RNG = new Random(12345);
private static final Random RNG = new Random(12345);
public static void main(final String[] args) throws Exception {
public static void main(final String[] args) throws Exception {
final int[] N_LIST = { 1000, 2000, 5000, 10000, 20000, 50000 };
final int REP = 5;
final int[] nList = { 1000, 2000, 5000, 10000, 20000, 50000 };
final int repetition = 5;
// --- Create the 4 CSV writers ---
PrintWriter abrInsert = new PrintWriter(new FileWriter("ABR_insert.csv"));
PrintWriter abrSearch = new PrintWriter(new FileWriter("ABR_search.csv"));
PrintWriter arnInsert = new PrintWriter(new FileWriter("ARN_insert.csv"));
PrintWriter arnSearch = new PrintWriter(new FileWriter("ARN_search.csv"));
// --- Creation des 4 CSV avec leur enTete ---
PrintWriter abrInsert = new PrintWriter(new FileWriter("ABR_insert.csv"));
writeHeader(abrInsert, nList);
// Header line : n,1000,2000,...
writeHeader(abrInsert, N_LIST);
writeHeader(abrSearch, N_LIST);
writeHeader(arnInsert, N_LIST);
writeHeader(arnSearch, N_LIST);
PrintWriter abrSearch = new PrintWriter(new FileWriter("ABR_search.csv"));
writeHeader(abrSearch, nList);
// Scenarios
List<String> scenarios = Arrays.asList("ASCENDANT", "ALEATOIRE");
PrintWriter arnInsert = new PrintWriter(new FileWriter("ARN_insert.csv"));
writeHeader(arnInsert, nList);
// Process each structure
for (String structure : Arrays.asList("ABR", "ARN")) {
PrintWriter arnSearch = new PrintWriter(new FileWriter("ARN_search.csv"));
writeHeader(arnSearch, nList);
for (String scenario : scenarios) {
// Scenarios
List<String> scenarios = Arrays.asList("ASCENDANT", "ALEATOIRE");
// Buffers for one line of insert & search results
List<Double> insertLine = new ArrayList<>();
List<Double> searchLine = new ArrayList<>();
// Process each structure
for (String structure : Arrays.asList("ABR", "ARN")) {
for (int n : N_LIST) {
for (String scenario : scenarios) {
long insertSum = 0;
long searchSum = 0;
// Buffers for one line of insert & search results
List<Double> insertLine = new ArrayList<>();
List<Double> searchLine = new ArrayList<>();
for (int r = 0; r < REP; r++) {
for (int n : nList) {
List<Integer> keys = new ArrayList<>();
for (int i = 0; i < n; i++)
keys.add(i);
long insertSum = 0;
long searchSum = 0;
if (scenario.equals("ALEATOIRE")) {
Collections.shuffle(keys, new Random(RNG.nextLong()));
}
for (int r = 0; r < repetition; r++) {
long[] result = structure.equals("ABR") ? benchmarkABR(keys, n) : benchmarkARN(keys, n);
List<Integer> keys = new ArrayList<>();
for (int i = 0; i < n; i++) {
keys.add(i);
}
insertSum += result[0];
searchSum += result[1];
}
if (scenario.equals("ALEATOIRE")) {
Collections.shuffle(keys, new Random(RNG.nextLong()));
}
insertLine.add(insertSum / (double) REP);
searchLine.add(searchSum / (double) REP);
long[] result = structure.equals("ABR") ? benchmarkAbr(keys, n) : benchmarkArn(keys, n);
System.out.printf("Done %s %s n=%d\n", structure, scenario, n);
}
insertSum += result[0];
searchSum += result[1];
}
// Write to correct CSV
if (structure.equals("ABR")) {
writeScenarioLine(abrInsert, scenario, insertLine);
writeScenarioLine(abrSearch, scenario, searchLine);
} else {
writeScenarioLine(arnInsert, scenario, insertLine);
writeScenarioLine(arnSearch, scenario, searchLine);
}
}
}
insertLine.add(insertSum / (double) repetition);
searchLine.add(searchSum / (double) repetition);
abrInsert.close();
abrSearch.close();
arnInsert.close();
arnSearch.close();
System.out.printf("Done %s %s n=%d\n", structure, scenario, n);
}
System.out.println("4 CSV créés au format tableau.");
// Write to correct CSV
if (structure.equals("ABR")) {
writeScenarioLine(abrInsert, scenario, insertLine);
writeScenarioLine(abrSearch, scenario, searchLine);
} else {
writeScenarioLine(arnInsert, scenario, insertLine);
writeScenarioLine(arnSearch, scenario, searchLine);
}
}
}
// -------------------------------------------------------
// Helper: header line
// -------------------------------------------------------
private static void writeHeader(PrintWriter pw, int[] nList) {
pw.print("n");
for (int n : nList)
pw.print("," + n);
pw.println();
abrInsert.close();
abrSearch.close();
arnInsert.close();
arnSearch.close();
System.out.println("4 CSV créés au format tableau.");
}
// -------------------------------------------------------
// Helper: header line
// -------------------------------------------------------
private static void writeHeader(PrintWriter pw, int[] nlist) {
pw.print("n");
for (int n : nlist) {
pw.print("," + n);
}
// -------------------------------------------------------
// Helper: write one scenario line
// -------------------------------------------------------
private static void writeScenarioLine(PrintWriter pw, String scenario, List<Double> values) {
pw.print(scenario);
for (double v : values)
pw.printf(",%.2f", v);
pw.println();
pw.println();
}
// -------------------------------------------------------
// Helper: write one scenario line
// -------------------------------------------------------
private static void writeScenarioLine(PrintWriter pw, String scenario, List<Double> values) {
pw.print(scenario);
for (double v : values) {
pw.printf(",%.2f", v);
}
// -------------------------------------------------------
// Benchmark ABR / ARN
// -------------------------------------------------------
pw.println();
}
private static long[] benchmarkABR(List<Integer> keys, int n) {
Collection<Integer> tree = new ArbreBinaireRecherche<>();
return runBenchmark(tree, keys, n);
}
// -------------------------------------------------------
// Benchmark ABR / ARN
// -------------------------------------------------------
private static long[] benchmarkARN(List<Integer> keys, int n) {
Collection<Integer> tree = new RedBlackTree<>();
return runBenchmark(tree, keys, n);
}
private static long[] benchmarkAbr(List<Integer> keys, int n) {
Collection<Integer> tree = new ArbreBinaireRecherche<>();
return runBenchmark(tree, keys, n);
}
private static long[] runBenchmark(Collection<Integer> tree, List<Integer> keys, int n) {
long t0 = System.nanoTime();
for (Integer k : keys)
tree.add(k);
long t1 = System.nanoTime();
private static long[] benchmarkArn(List<Integer> keys, int n) {
Collection<Integer> tree = new RedBlackTree<>();
return runBenchmark(tree, keys, n);
}
long t2 = System.nanoTime();
for (int x = 0; x < 2 * n; x++)
tree.contains(x);
long t3 = System.nanoTime();
private static long[] runBenchmark(Collection<Integer> tree, List<Integer> keys, int n) {
long t0 = System.nanoTime();
for (Integer k : keys) {
tree.add(k);
}
long t1 = System.nanoTime();
return new long[] { t1 - t0, t3 - t2 };
long t2 = System.nanoTime();
for (int x = 0; x < 2 * n; x++) {
tree.contains(x);
}
long t3 = System.nanoTime();
return new long[] { t1 - t0, t3 - t2 };
}
}
......@@ -16,497 +16,543 @@ import java.util.Stack;
* contiennent le même nombre de nœuds noirs
*/
public class RedBlackTree<E extends Comparable<E>> implements Collection<E> {
private static final boolean RED = true;
private static final boolean BLACK = false;
/** Classe interne représentant un nœud de l'arbre */
private class Node {
E key;
Node left, right, parent;
boolean color = RED; // par défaut, un nouveau nœud est rouge
Node(E key, Node parent) {
this.key = key;
this.parent = parent;
}
private static final boolean ROUGE = true;
private static final boolean NOIR = false;
/** Classe interne représentant un nœud de l'arbre */
private class Noeud {
E cle;
Noeud gauche;
Noeud droit;
Noeud parent;
boolean color = ROUGE; // par défaut, un nouveau nœud est rouge
Noeud(E cle, Noeud parent) {
this.cle = cle;
this.parent = parent;
}
}
private Node root;
private int size = 0;
private Noeud racine;
private int taille = 0;
/** Rotation gauche standard */
private void rotateLeft(Node x) {
Node y = x.right;
x.right = y.left;
/** Rotation gauche standard */
private void rotateLeft(Noeud x) {
Noeud y = x.droit;
x.droit = y.gauche;
if (y.left != null)
y.left.parent = x;
y.parent = x.parent;
if (y.gauche != null) {
y.gauche.parent = x;
}
if (x.parent == null)
root = y;
else if (x == x.parent.left)
x.parent.left = y;
else
x.parent.right = y;
y.parent = x.parent;
y.left = x;
x.parent = y;
if (x.parent == null) {
racine = y;
} else if (x == x.parent.gauche) {
x.parent.gauche = y;
} else {
x.parent.droit = y;
}
/** Rotation droite standard */
private void rotateRight(Node x) {
Node y = x.left;
x.left = y.right;
y.gauche = x;
x.parent = y;
}
if (y.right != null)
y.right.parent = x;
/** Rotation droite standard */
private void rotateRight(Noeud x) {
Noeud y = x.gauche;
x.gauche = y.droit;
y.parent = x.parent;
if (y.droit != null) {
y.droit.parent = x;
}
if (x.parent == null)
root = y;
else if (x == x.parent.right)
x.parent.right = y;
else
x.parent.left = y;
y.parent = x.parent;
y.right = x;
x.parent = y;
if (x.parent == null) {
racine = y;
} else if (x == x.parent.droit) {
x.parent.droit = y;
} else {
x.parent.gauche = y;
}
/**
* Correction après insertion. Assure que les propriétés RB sont conservées.
*/
private void fixAfterInsert(Node z) {
while (z.parent != null && z.parent.color == RED) {
if (z.parent == z.parent.parent.left) {
Node y = z.parent.parent.right; // oncle
if (y != null && y.color == RED) {
// Cas 1 : parent rouge + oncle rouge -> recolorations
z.parent.color = BLACK;
y.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent;
} else {
if (z == z.parent.right) {
// Cas 2 : triangle -> rotation gauche
z = z.parent;
rotateLeft(z);
}
// Cas 3 : ligne -> rotation droite
z.parent.color = BLACK;
z.parent.parent.color = RED;
rotateRight(z.parent.parent);
}
} else {
// Symétrique : parent est enfant droit
Node y = z.parent.parent.left;
if (y != null && y.color == RED) {
z.parent.color = BLACK;
y.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent;
} else {
if (z == z.parent.left) {
z = z.parent;
rotateRight(z);
}
z.parent.color = BLACK;
z.parent.parent.color = RED;
rotateLeft(z.parent.parent);
}
}
}
root.color = BLACK;
y.droit = x;
x.parent = y;
}
/**
* Correction après insertion. Assure que les propriétés RB sont conservées.
*/
private void fixAfterInsert(Noeud z) {
while (z.parent != null && z.parent.color == ROUGE) {
if (z.parent == z.parent.parent.gauche) {
Noeud y = z.parent.parent.droit; // oncle
if (y != null && y.color == ROUGE) {
// Cas 1 : parent rouge + oncle rouge -> recolorations
z.parent.color = NOIR;
y.color = NOIR;
z.parent.parent.color = ROUGE;
z = z.parent.parent;
} else {
if (z == z.parent.droit) {
// Cas 2 : triangle -> rotation gauche
z = z.parent;
rotateLeft(z);
}
// Cas 3 : ligne -> rotation droite
z.parent.color = NOIR;
z.parent.parent.color = ROUGE;
rotateRight(z.parent.parent);
}
} else {
// Symétrique : parent est enfant droit
Noeud y = z.parent.parent.gauche;
if (y != null && y.color == ROUGE) {
z.parent.color = NOIR;
y.color = NOIR;
z.parent.parent.color = ROUGE;
z = z.parent.parent;
} else {
if (z == z.parent.gauche) {
z = z.parent;
rotateRight(z);
}
z.parent.color = NOIR;
z.parent.parent.color = ROUGE;
rotateLeft(z.parent.parent);
}
}
}
/** Remplace un nœud par un autre dans l’arbre */
private void transplant(Node u, Node v) {
if (u.parent == null)
root = v;
else if (u == u.parent.left)
u.parent.left = v;
else
u.parent.right = v;
if (v != null)
v.parent = u.parent;
racine.color = NOIR;
}
/** Remplace un nœud par un autre dans l’arbre */
private void transplant(Noeud u, Noeud v) {
if (u.parent == null) {
racine = v;
} else if (u == u.parent.gauche) {
u.parent.gauche = v;
} else {
u.parent.droit = v;
}
/** Renvoie le minimum du sous-arbre */
private Node minimum(Node x) {
while (x.left != null)
x = x.left;
return x;
if (v != null) {
v.parent = u.parent;
}
}
/**
* Correction après suppression. Restaure les propriétés Rouge-Noir.
*/
private void fixAfterDelete(Node x, Node parent) {
while ((x != root) && (x == null || x.color == BLACK)) {
if (parent != null && x == parent.left) {
Node w = parent.right;
if (w != null && w.color == RED) {
w.color = BLACK;
parent.color = RED;
rotateLeft(parent);
w = parent.right;
}
if ((w == null)
|| ((w.left == null || w.left.color == BLACK) && (w.right == null || w.right.color == BLACK))) {
if (w != null)
w.color = RED;
x = parent;
parent = x.parent;
} else {
if (w.right == null || w.right.color == BLACK) {
if (w.left != null)
w.left.color = BLACK;
w.color = RED;
rotateRight(w);
w = parent.right;
}
if (w != null)
w.color = parent.color;
parent.color = BLACK;
if (w != null && w.right != null)
w.right.color = BLACK;
rotateLeft(parent);
x = root;
break;
}
} else {
if (parent == null)
break;
Node w = parent.left;
if (w != null && w.color == RED) {
w.color = BLACK;
parent.color = RED;
rotateRight(parent);
w = parent.left;
}
if ((w == null)
|| ((w.right == null || w.right.color == BLACK) && (w.left == null || w.left.color == BLACK))) {
if (w != null)
w.color = RED;
x = parent;
parent = x.parent;
} else {
if (w.left == null || w.left.color == BLACK) {
if (w.right != null)
w.right.color = BLACK;
w.color = RED;
rotateLeft(w);
w = parent.left;
}
if (w != null)
w.color = parent.color;
parent.color = BLACK;
if (w != null && w.left != null)
w.left.color = BLACK;
rotateRight(parent);
x = root;
break;
}
}
}
if (x != null)
x.color = BLACK;
/** Renvoie le minimum du sous-arbre */
private Noeud minimum(Noeud x) {
while (x.gauche != null) {
x = x.gauche;
}
@Override
public boolean add(E e) {
Objects.requireNonNull(e);
// Arbre vide : nouvelle racine noire
if (root == null) {
root = new Node(e, null);
root.color = BLACK;
size = 1;
return true;
}
Node cur = root, parent = null;
int cmp = 0;
// Recherche de la position d'insertion
while (cur != null) {
parent = cur;
cmp = e.compareTo(cur.key);
if (cmp < 0)
cur = cur.left;
else if (cmp > 0)
cur = cur.right;
else
return false; // pas de doublons
}
Node node = new Node(e, parent);
if (cmp < 0)
parent.left = node;
else
parent.right = node;
// Correction RB après insertion
fixAfterInsert(node);
size++;
return true;
return x;
}
/**
* Correction après suppression. Restaure les propriétés Rouge-Noir.
*/
private void fixAfterDelete(Noeud x, Noeud parent) {
while ((x != racine) && (x == null || x.color == NOIR)) {
if (parent != null && x == parent.gauche) {
Noeud w = parent.droit;
if (w != null && w.color == ROUGE) {
w.color = NOIR;
parent.color = ROUGE;
rotateLeft(parent);
w = parent.droit;
}
if ((w == null) || ((w.gauche == null || w.gauche.color == NOIR)
&& (w.droit == null || w.droit.color == NOIR))) {
if (w != null) {
w.color = ROUGE;
}
x = parent;
parent = x.parent;
} else {
if (w.droit == null || w.droit.color == NOIR) {
if (w.gauche != null) {
w.gauche.color = NOIR;
}
w.color = ROUGE;
rotateRight(w);
w = parent.droit;
}
if (w != null) {
w.color = parent.color;
}
parent.color = NOIR;
if (w != null && w.droit != null) {
w.droit.color = NOIR;
}
rotateLeft(parent);
x = racine;
break;
}
} else {
if (parent == null) {
break;
}
Noeud w = parent.gauche;
if (w != null && w.color == ROUGE) {
w.color = NOIR;
parent.color = ROUGE;
rotateRight(parent);
w = parent.gauche;
}
if ((w == null) || ((w.droit == null || w.droit.color == NOIR)
&& (w.gauche == null || w.gauche.color == NOIR))) {
if (w != null) {
w.color = ROUGE;
}
x = parent;
parent = x.parent;
} else {
if (w.gauche == null || w.gauche.color == NOIR) {
if (w.droit != null) {
w.droit.color = NOIR;
}
w.color = ROUGE;
rotateLeft(w);
w = parent.gauche;
}
if (w != null) {
w.color = parent.color;
}
parent.color = NOIR;
if (w != null && w.gauche != null) {
w.gauche.color = NOIR;
}
rotateRight(parent);
x = racine;
break;
}
}
}
/** Recherche un nœud contenant la clé */
private Node findNode(E e) {
Node cur = root;
if (x != null) {
x.color = NOIR;
}
}
@Override
public boolean add(E e) {
Objects.requireNonNull(e);
// Arbre vide : nouvelle racine noire
if (racine == null) {
racine = new Noeud(e, null);
racine.color = NOIR;
taille = 1;
return true;
}
while (cur != null) {
int cmp = e.compareTo(cur.key);
Noeud cur = racine;
Noeud parent = null;
int cmp = 0;
// Recherche de la position d'insertion
while (cur != null) {
parent = cur;
cmp = e.compareTo(cur.cle);
if (cmp < 0) {
cur = cur.gauche;
} else if (cmp > 0) {
cur = cur.droit;
} else {
return false; // pas de doublons
}
}
if (cmp == 0)
return cur;
if (cmp < 0)
cur = cur.left;
else
cur = cur.right;
}
Noeud node = new Noeud(e, parent);
return null;
if (cmp < 0) {
parent.gauche = node;
} else {
parent.droit = node;
}
@Override
public boolean contains(Object o) {
if (o == null)
return false;
try {
@SuppressWarnings("unchecked")
E e = (E) o;
return findNode(e) != null;
} catch (ClassCastException ex) {
return false;
}
// Correction RB après insertion
fixAfterInsert(node);
taille++;
return true;
}
/** Recherche un nœud contenant la clé */
private Noeud findNode(E e) {
Noeud cur = racine;
while (cur != null) {
int cmp = e.compareTo(cur.cle);
if (cmp == 0) {
return cur;
}
if (cmp < 0) {
cur = cur.gauche;
} else {
cur = cur.droit;
}
}
@Override
public boolean remove(Object o) {
if (o == null)
return false;
E e;
try {
e = (E) o;
} catch (ClassCastException ex) {
return false;
}
Node z = findNode(e);
if (z == null)
return false;
Node y = z;
boolean yOriginalColor = y.color;
Node x;
Node xParent;
// Cas 1 : un seul enfant
if (z.left == null) {
x = z.right;
xParent = z.parent;
transplant(z, z.right);
} else if (z.right == null) {
x = z.left;
xParent = z.parent;
transplant(z, z.left);
}
// Cas 2 : deux enfants → successeur
else {
y = minimum(z.right);
yOriginalColor = y.color;
x = y.right;
if (y.parent == z) {
if (x != null)
x.parent = y;
xParent = y;
} else {
transplant(y, y.right);
y.right = z.right;
if (y.right != null)
y.right.parent = y;
xParent = y.parent;
}
transplant(z, y);
y.left = z.left;
if (y.left != null)
y.left.parent = y;
y.color = z.color;
}
if (yOriginalColor == BLACK)
fixAfterDelete(x, xParent);
size--;
return true;
return null;
}
@Override
public boolean contains(Object o) {
if (o == null) {
return false;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private final Stack<Node> stack = initStack(root);
private Stack<Node> initStack(Node r) {
Stack<Node> s = new Stack<>();
Node cur = r;
while (cur != null) {
s.push(cur);
cur = cur.left;
}
return s;
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public E next() {
if (!hasNext())
throw new NoSuchElementException();
Node n = stack.pop();
E res = n.key;
Node cur = n.right;
// Descente maximale à gauche
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
return res;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
try {
@SuppressWarnings("unchecked")
E e = (E) o;
return findNode(e) != null;
} catch (ClassCastException ex) {
return false;
}
}
@Override
public int size() {
return size;
@Override
public boolean remove(Object o) {
if (o == null) {
return false;
}
@Override
public boolean isEmpty() {
return size == 0;
E e;
try {
e = (E) o;
} catch (ClassCastException ex) {
return false;
}
@Override
public void clear() {
root = null;
size = 0;
Noeud z = findNode(e);
if (z == null) {
return false;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[size];
int i = 0;
Noeud y = z;
boolean yorigColor = y.color;
Noeud x;
Noeud xparent;
// Cas 1 : un seul enfant
if (z.gauche == null) {
x = z.droit;
xparent = z.parent;
transplant(z, z.droit);
} else if (z.droit == null) {
x = z.gauche;
xparent = z.parent;
transplant(z, z.gauche);
} else { // Cas 2 : deux enfants → successeur
y = minimum(z.droit);
yorigColor = y.color;
x = y.droit;
if (y.parent == z) {
if (x != null) {
x.parent = y;
}
xparent = y;
} else {
transplant(y, y.droit);
y.droit = z.droit;
if (y.droit != null) {
y.droit.parent = y;
}
xparent = y.parent;
}
transplant(z, y);
y.gauche = z.gauche;
if (y.gauche != null) {
y.gauche.parent = y;
}
y.color = z.color;
}
for (E e : this)
arr[i++] = e;
if (yorigColor == NOIR) {
fixAfterDelete(x, xparent);
}
return arr;
taille--;
return true;
}
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private final Stack<Noeud> stack = initStack(racine);
private Stack<Noeud> initStack(Noeud r) {
Stack<Noeud> s = new Stack<>();
Noeud cur = r;
while (cur != null) {
s.push(cur);
cur = cur.gauche;
}
return s;
}
@Override
public boolean hasNext() {
return !stack.isEmpty();
}
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Noeud n = stack.pop();
E res = n.cle;
Noeud cur = n.droit;
// Descente maximale à gauche
while (cur != null) {
stack.push(cur);
cur = cur.gauche;
}
return res;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return taille;
}
@Override
public boolean isEmpty() {
return taille == 0;
}
@Override
public void clear() {
racine = null;
taille = 0;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[taille];
int i = 0;
for (E e : this) {
arr[i++] = e;
}
@Override
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
return arr;
}
int i = 0;
@Override
public <T> T[] toArray(T[] a) {
if (a.length < taille) {
a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), taille);
}
for (E e : this)
a[i++] = (T) e;
int i = 0;
if (a.length > size)
a[size] = null;
for (E e : this) {
a[i++] = (T) e;
}
return a;
if (a.length > taille) {
a[taille] = null;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c)
if (!contains(o))
return false;
return true;
return a;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean changed = false;
for (E e : c)
changed |= add(e);
return changed;
return true;
}
@Override
public boolean addAll(Collection<? extends E> c) {
boolean changed = false;
for (E e : c) {
changed |= add(e);
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object o : c)
changed |= remove(o);
return changed;
return changed;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean changed = false;
for (Object o : c) {
changed |= remove(o);
}
@Override
public boolean retainAll(Collection<?> c) {
List<E> toRemove = new ArrayList<>();
return changed;
}
for (E e : this)
if (!c.contains(e))
toRemove.add(e);
@Override
public boolean retainAll(Collection<?> c) {
List<E> toRemove = new ArrayList<>();
for (E e : toRemove)
remove(e);
for (E e : this) {
if (!c.contains(e)) {
toRemove.add(e);
}
}
return !toRemove.isEmpty();
for (E e : toRemove) {
remove(e);
}
return !toRemove.isEmpty();
}
}
package collection;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class ArbreBinaireRechercheTest {
private ArbreBinaireRecherche<Integer> arbre;
@BeforeEach
void setUp() {
arbre = new ArbreBinaireRecherche<>();
}
@Test
void testAdd() {
assertTrue(arbre.add(5));
assertTrue(arbre.add(3));
assertTrue(arbre.add(7));
assertFalse(arbre.add(5)); // doublon
assertEquals(3, arbre.size());
}
@Test
void testContains() {
arbre.add(5);
arbre.add(2);
arbre.add(8);
assertTrue(arbre.contains(5));
assertTrue(arbre.contains(2));
assertFalse(arbre.contains(10));
assertFalse(arbre.contains(null));
}
@Test
void testRemoveFeuille() {
arbre.add(5);
arbre.add(3);
arbre.add(8);
assertTrue(arbre.remove(3));
assertFalse(arbre.contains(3));
assertEquals(2, arbre.size());
}
@Test
void testRemoveNoeudAvecUnEnfant() {
arbre.add(5);
arbre.add(3);
arbre.add(2); // enfant de 3
assertTrue(arbre.remove(3));
assertTrue(arbre.contains(2));
assertFalse(arbre.contains(3));
assertEquals(2, arbre.size());
}
@Test
void testRemoveNoeudDeuxEnfants() {
arbre.add(5);
arbre.add(3);
arbre.add(7);
arbre.add(6);
arbre.add(8);
assertTrue(arbre.remove(7));
assertFalse(arbre.contains(7));
assertEquals(4, arbre.size());
}
@Test
void testIteratorOrdreCroissant() {
arbre.add(5);
arbre.add(2);
arbre.add(8);
arbre.add(1);
arbre.add(3);
Iterator<Integer> it = arbre.iterator();
assertEquals(1, it.next());
assertEquals(2, it.next());
assertEquals(3, it.next());
assertEquals(5, it.next());
assertEquals(8, it.next());
assertFalse(it.hasNext());
}
@Test
void testClear() {
arbre.add(4);
arbre.add(1);
arbre.add(9);
arbre.clear();
assertTrue(arbre.isEmpty());
assertEquals(0, arbre.size());
assertFalse(arbre.contains(4));
}
@Test
void testToArray() {
arbre.add(3);
arbre.add(1);
arbre.add(2);
Object[] arr = arbre.toArray();
assertEquals(3, arr.length);
assertArrayEquals(new Object[] { 1, 2, 3 }, arr);
}
@Test
void testAddAll() {
List<Integer> list = Arrays.asList(5, 2, 8);
assertTrue(arbre.addAll(list));
assertEquals(3, arbre.size());
assertTrue(arbre.contains(8));
}
@Test
void testRemoveAll() {
arbre.add(5);
arbre.add(2);
arbre.add(8);
List<Integer> list = Arrays.asList(2, 8);
assertTrue(arbre.removeAll(list));
assertFalse(arbre.contains(2));
assertFalse(arbre.contains(8));
assertTrue(arbre.contains(5));
}
@Test
void testRetainAll() {
arbre.add(5);
arbre.add(2);
arbre.add(8);
List<Integer> list = Arrays.asList(5);
assertTrue(arbre.retainAll(list));
assertTrue(arbre.contains(5));
assertFalse(arbre.contains(2));
assertFalse(arbre.contains(8));
assertEquals(1, arbre.size());
}
@Test
void testContainsAvecTypeIncorrect() {
arbre.add(5);
arbre.add(3);
// Un type incompatible doit entraîner catch(ClassCastException) → false
assertFalse(arbre.contains("texte")); // String ≠ Integer
}
@Test
void testRemoveNull() {
arbre.add(5);
assertFalse(arbre.remove(null)); // ne doit jamais lancer d’exception
assertEquals(1, arbre.size());
assertTrue(arbre.contains(5));
}
@Test
void testRemoveRacineNull() {
// racine = null
assertTrue(arbre.isEmpty());
assertFalse(arbre.remove(10)); // retirer un élément dans un arbre vide => false
assertTrue(arbre.isEmpty());
}
@Test
void testRemoveSansException() {
arbre.add(10);
arbre.add(5);
arbre.add(15);
// Cas normaux
assertDoesNotThrow(() -> arbre.remove(5));
assertDoesNotThrow(() -> arbre.remove(15));
assertDoesNotThrow(() -> arbre.remove(10));
// Retrait sur arbre vide
assertDoesNotThrow(() -> arbre.remove(999));
// Retrait d’un null
assertDoesNotThrow(() -> arbre.remove(null));
}
@Test
void testRemoveAvecSuccesseurProfond() {
arbre.add(10);
arbre.add(5);
arbre.add(20);
arbre.add(15);
arbre.add(13); // successeur profond
// Suppression du nœud ayant deux enfants
assertTrue(arbre.remove(10));
// Le successeur (13) doit avoir remplacé la racine
Iterator<Integer> it = arbre.iterator();
assertEquals(5, it.next());
assertEquals(13, it.next()); // remplace 10
assertEquals(15, it.next());
assertEquals(20, it.next());
assertFalse(it.hasNext());
}
@Test
void testRemoveRacineAvecUnEnfant() {
arbre.add(10);
arbre.add(20); // enfant droit unique
assertTrue(arbre.remove(10));
// La racine doit être remplacée par l’enfant (20)
assertEquals(20, arbre.iterator().next());
assertEquals(1, arbre.size());
}
@Test
void testIsEmpty() {
ArbreBinaireRecherche<Integer> a = new ArbreBinaireRecherche<>();
// L'arbre vide
assertTrue(a.isEmpty());
// Après insertion
a.add(1);
assertFalse(a.isEmpty());
// Après suppression
a.remove(1);
assertTrue(a.isEmpty());
}
@Test
void testToArrayComplet() {
arbre.add(4);
arbre.add(1);
arbre.add(3);
arbre.add(2);
Object[] arr = arbre.toArray();
assertEquals(4, arr.length);
assertArrayEquals(new Object[] { 1, 2, 3, 4 }, arr);
// Vérification cohérence avec iterator()
int index = 0;
for (int value : arbre) {
assertEquals(value, arr[index++]);
}
}
@Test
void testContainsAll() {
arbre.add(5);
arbre.add(2);
arbre.add(8);
// Tous présents
assertTrue(arbre.containsAll(Arrays.asList(2, 5)));
// Un absent
assertFalse(arbre.containsAll(Arrays.asList(2, 10)));
// Collection vide → doit être true
assertTrue(arbre.containsAll(Arrays.asList()));
// Mauvais type → contains(o) doit retourner false
assertFalse(arbre.containsAll(Arrays.asList("test")));
}
@Test
void testToArrayGenericRetourNull() {
arbre.add(5);
arbre.add(2);
arbre.add(9);
Integer[] cible = new Integer[3];
// L’implémentation actuelle doit renvoyer null
assertNull(arbre.toArray(cible));
}
}
package collection;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class RedBlackTreeTest {
private RedBlackTree<Integer> tree;
@BeforeEach
void setUp() {
tree = new RedBlackTree<>();
}
@Test
void testAddAndContains() {
assertTrue(tree.add(10));
assertTrue(tree.contains(10));
assertFalse(tree.add(10)); // pas de doublons
}
@Test
void testRemove() {
tree.add(20);
tree.add(10);
tree.add(30);
assertTrue(tree.remove(20));
assertFalse(tree.contains(20));
assertFalse(tree.remove(40)); // élément inexistant
}
@Test
void testSizeAndIsEmpty() {
assertTrue(tree.isEmpty());
tree.add(1);
tree.add(2);
tree.add(3);
assertEquals(3, tree.size());
assertFalse(tree.isEmpty());
}
@Test
void testClear() {
tree.add(5);
tree.add(15);
tree.clear();
assertEquals(0, tree.size());
assertTrue(tree.isEmpty());
}
@Test
void testIterator() {
tree.add(3);
tree.add(1);
tree.add(4);
tree.add(2);
Iterator<Integer> it = tree.iterator();
int[] expectedOrder = { 1, 2, 3, 4 };
int i = 0;
while (it.hasNext()) {
assertEquals(expectedOrder[i++], it.next());
}
assertEquals(expectedOrder.length, i);
}
@Test
void testToArray() {
tree.add(7);
tree.add(3);
tree.add(9);
Object[] arr = tree.toArray();
Arrays.sort(arr); // pour comparaison
assertArrayEquals(new Object[] { 3, 7, 9 }, arr);
}
@Test
void testToArrayWithType() {
tree.add(5);
tree.add(1);
tree.add(8);
Integer[] arr = new Integer[3];
arr = tree.toArray(arr);
Arrays.sort(arr);
assertArrayEquals(new Integer[] { 1, 5, 8 }, arr);
}
@Test
void testAddAllAndContainsAll() {
List<Integer> list = Arrays.asList(10, 20, 30);
assertTrue(tree.addAll(list));
assertTrue(tree.containsAll(list));
}
@Test
void testRemoveAll() {
tree.add(1);
tree.add(2);
tree.add(3);
List<Integer> toRemove = Arrays.asList(2, 3, 4);
assertTrue(tree.removeAll(toRemove));
assertFalse(tree.contains(2));
assertFalse(tree.contains(3));
assertTrue(tree.contains(1));
}
@Test
void testRetainAll() {
tree.add(1);
tree.add(2);
tree.add(3);
List<Integer> toRetain = Arrays.asList(2, 3, 4);
assertTrue(tree.retainAll(toRetain));
assertFalse(tree.contains(1));
assertTrue(tree.contains(2));
assertTrue(tree.contains(3));
}
}