3
votes

Comment trier la carte sans utiliser l'interface comparable et Comparator? Comment écrire un tri personnalisé?

Problème - J'ai une classe d'étudiants, elle contient le nom, le numéro de rouleau, trois notes de sujet m1, m2, m3 et le total des notes. Je dois trier l'objet étudiant en fonction de leurs notes totales si deux ou plusieurs notes d'élèves sont égales, puis triez-le en fonction de leur nom. Remarque - Je dois le rechercher sur Google, mais je n'obtiens pas la solution attendue dans la question de stackoverflow à l'aide de l'interface Comparable et Comparator.

J'ai créé la classe Studnt

public class StudentData {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enetr the number of Student");
        int totalStudent = sc.nextInt();
        Map<Integer,Student> map = new TreeMap<Integer,Student>();
        for(int i =0;i<totalStudent;i++) {
            Student ss = new Student();
            System.out.println("Enter the Student roll number");
            ss.setRollNumber(sc.nextInt());
            System.out.println("Enter the Student Name");
            ss.setName(sc.next());
            System.out.println("Enter the m1 marks ");
            ss.setM1(sc.nextInt());
            System.out.println("Enetr the m2 marks ");
            ss.setM2(sc.nextInt());
            System.out.println("Enter the m3 marks ");
            ss.setM3(sc.nextInt());
            ss.setTotMarks(ss.getM1()+ss.getM2()+ss.getM3());

            map.put(ss.getTotMarks(),ss);
            ss=null;
        }   
        //stdList.forEach(System.out::print);
        for(Map.Entry<Integer,Student> m :map.entrySet()) {
            System.out.println(m);
        }

    }
}

Classe principale

public class Student {
    private String name;
    private Integer rollNumber;
    private int m1;
    private int m2;
    private int m3;
    private int totMarks;
    //Getter setter
}

En fait, j'utilise TreeMap pour trier la valeur par clé (le total des marques est la clé dans mon TreeMap). mais deux élèves ou plus ont des notes égales. Ensuite, l'ancien objet étudiant (valeur) remplacé par le nouvel étudiant en raison de la clé non autorisée en double

output

6 = Élève [name = ved, rollNumber = 12, m1 = 2, m2 = 2, m3 = 2, totMarks = 6]

9 = Élève [name = prakash, rollNumber = 56, m1 = 3, m2 = 3, m3 = 3, totMarks = 9]

les seuls totMarks uniques stockés dans la carte


18 commentaires

@JonSkeet: Selon votre suggestion, Une nouvelle question posée par moi, discussion précédente sur ce lien stackoverflow.com/questions/8119366/sorting-hashmap-by-value‌ s


Pourquoi n'utilisez-vous pas Comparable et Comparator? Votre question n'a pas vraiment de sens.


@BasilBourque: En fait, c'est ma restriction d'enseignant. Je dois le faire sans Comparable et Comparator


Si Comparable et Comparator ne sont pas disponibles, vous ne pouvez pas utiliser les routines de tri pratiques Arrays.sort et Collections fournies par JDK. trier . Vous devrez rédiger votre propre procédure de tri. Votre question se résume donc à "Comment écrire une procédure de tri personnalisée?"


@BasilBourque: pourquoi ma question n'a pas vraiment de sens? le tri n'est pas possible sans utiliser Comparable et Comparator?


@VedPrakash Vous devez modifier votre question pour expliquer qu'il s'agit d'un devoir, sinon cela n'a aucun sens. Le tri est le but même de Comparable et Comparator. Demander à trier sans eux, c'est comme demander comment conduire une voiture sans moteur ni roues.


@KevinAnderson: oui. Question mise à jour avec un tri personnalisé


@BasilBourque: En fait, j'essaye de conduire une voiture avec mon moteur personnalisé (* _ *)


Ce dont vous avez besoin pour rechercher sur Google, c'est donc des "algorithmes de tri". Ou recherchez des algorithmes spécifiques tels que "tri par bulles" (mauvais), "tri par insertion" ou "tri par sélection", (légèrement meilleur), "tri par shell" (bien meilleur), "tri rapide" (meilleur), etc.


@KevinAnderson: J'essaye toujours d'écrire mon tri personnalisé mais je n'arrive pas à résoudre mon problème. C'est trop facile mais je ne sais pas pourquoi je reste coincé à chaque essai (- _ -)


Quel problème cela poserait-il?


@KevinAnderson: je vous mettrai à jour dès que possible


Je pense que la bonne réponse est que cela dépend du sujet que vous étudiez actuellement. Par exemple. s le sujet comment écrire votre propre arbre qui accepte les algorithmes de tri définis par l'utilisateur? Ou comment résoudre les problèmes de manière créative? Par exemple, vous pouvez dériver une clé composite - constituée du total des marques et du nom. Par exemple "6-ved" ou "9-prakash". Je ne vais pas vous donner la réponse complète, mais je vais vous donner un indice. Faites tester les personnes avec des scores> = 10 (par exemple 65) et des scores à un chiffre (par exemple 5 ou 7) et assurez-vous que vous obtenez le bon résultat. Vous pouvez avec ce schéma, mais vous devez réfléchir à la façon dont.


Je viens de réaliser que vous demandez comment effectuer un tri personnalisé sans utiliser les interfaces Comparator ou Comparable . C'est un peu étrange, car les objets de vos élèves ne sont pas comparables et les cartes ne sont généralement pas ordonnées


@GMc selon votre indice, je vais essayer de résoudre


Pourquoi n'implémentez-vous pas une méthode statique dans la classe Student: lessOrEqual qui accepte 2 instances Student et les compare selon vos critères? Ensuite, vous pouvez utiliser cette méthode pour trier une liste d'étudiants en fonction de n'importe quel algorithme de tri (le tri rapide est préférable).


@Everyone: OK, j'essaierai de faire selon votre suggestion> (+ _ +) <


@VedPrakash J'ai fait une démo expliquant ce que je voulais dire par mon commentaire initial.


3 Réponses :


0
votes

Au lieu de stocker les valeurs en tant qu'élève, stockez-les sous forme de carte de (nom, élève), de sorte que lorsqu'un élève avec les mêmes notes est rencontré, il est ajouté à la carte.

public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enetr the number of Student");
    int totalStudent = sc.nextInt();
    Map<Integer, Map<String, Student>> map = new TreeMap<>();
    for(int i =0;i<totalStudent;i++) {
        Student ss = new Student();
        System.out.println("Enter the Student roll number");
        ss.setRollNumber(sc.nextInt());
        System.out.println("Enter the Student Name");
        ss.setName(sc.next());
        System.out.println("Enter the m1 marks ");
        ss.setM1(sc.nextInt());
        System.out.println("Enetr the m2 marks ");
        ss.setM2(sc.nextInt());
        System.out.println("Enter the m3 marks ");
        ss.setM3(sc.nextInt());
        ss.setTotMarks(ss.getM1()+ss.getM2()+ss.getM3());

        Integer key = ss.getTotMarks();
        if (map.get(key) == null){  // if this is a new key in the map, then create a new TreeMap and put it
            final TreeMap<String, Student> nameAndStudentMap = new TreeMap<>();
            nameAndStudentMap.put(ss.getName(), ss);
            map.put(key, nameAndStudentMap);
        }else { // if the key already existed, then get the map stored and add to it.
            map.get(key).put(ss.getName(), ss);
        }

    }
    //stdList.forEach(System.out::print);
    for(Map.Entry<Integer,Map<String, Student>> m :map.entrySet()) {
        for (Map.Entry<String, Student> nameAndStudent : m.getValue().entrySet()) {
            System.out.println(m.getKey() + " = " + nameAndStudent.getValue());
        }
    }

}


6 commentaires

Je pense que cela ne répond pas tout à fait à l'exigence de trier les traverses par nom.


@GMc Selon votre commentaire, je l'ai modifié pour inclure le tri par nom également en utilisant la carte d'arbre comme valeur de la carte externe.


@PariNgang ok je vais vérifier


Ce code peut être hors de portée / excessif à cette fin. Les suggestions de @GMc ou de Tout le monde dans votre commentaire sont la meilleure façon de procéder.


Cela trie par marques, et stocke les entrées dans map à la fois par marques et par nom. Confusion totale.


@ user207421 la carte externe utilise les marques comme clé, tandis que la carte interne utilise le nom comme clé. Puisque le PO voulait une priorité des marques suivies du nom. Dites que 2 élèves 'x' et 'b' obtiennent 11 comme notes totales, 2 autres 'p' et 'q' obtiennent 12 et 'a' obtiennent 13. Le json ressemblerait à ceci -> {"11" : {"b": {"name": "b", "rollNumber": 12, "totMarks": 11}, "x": {"‌ name": "x", "rollNumbe‌ r": 13 , "totMarks": 11} ‌}, "12": {"p": {"name": ‌ "p", "rollNumber": 15, ‌ "totMarks": 12}, "q": {‌ "name": "q", "rollNumb‌ er": 14, "totMarks": 12‌}}, "13": {"a": {"name" ‌: "a", "rollNumber ": 22‌," totMarks ": 13}}}. Cela aide-t-il à clarifier?



1
votes

Puisque vous ne pouvez pas utiliser le comparateur ou les algorithmes de tri existants, vous devez le faire vous-même. J'ai implémenté une fonction statique lessOrEqual qui accepte 2 instances Student , les compare et renvoie si oui ou non s1 code> est inférieur ou égal à s2 . plus grand (étudiant s1, étudiant s2) qui renvoie vrai SEULEMENT SI s1 est plus grand que s2 . Il peut y avoir de nombreuses façons de faire cela, c'est vraiment à vous car c'est juste une compréhension. La fonction vérifie d'abord les notes, si les notes correspondent, elle vérifiera ensuite le nom et retournera en conséquence.

EDIT: Comme vous pouvez le voir, j'ai remplacé lessOrEqual par plus grand car le tri de sélection que j'utilise doit trouver plus grand . C'est le même effet, je ne l'ai fait que pour une meilleure lisibilité du genre.

Ensuite, j'ai implémenté une autre fonction statique qui accepte ArrayList , la trie et la renvoie triée. L'algorithme de tri utilisé est très basique: le tri par sélection. C'est O (N ^ 2) qui n'est pas efficace, mais je l'ai fait par souci de simplicité dans la démo ci-dessous.

Code:

    public boolean larger(Student other){
        if(totMarks < other.totMarks) return false; 
        else if (totMarks > other.totMarks) return true;
        // compare names
        else return name.compareTo(other.name) > 0;
    }

    public static ArrayList<Student> sortSelection(ArrayList<Student> list){
        for(int i=0; i<list.size(); i++){
            for(int j=i+1; j< list.size(); j++){
                // comparison way changes accordingly
                if(list.get(i).larger(list.get(j))){ // swap
                    Student temp = list.get(i); 
                    list.set(i, list.get(j));
                    list.set(j, temp);
                }
            }
        }
        return list;
    }

Sortie:

Array before sorting:
Name:          Ved Parkash - Total Marks:  99
Name:            Adam Noob - Total Marks: 100
Name:           John Smith - Total Marks:  98
Name:           Jack Smith - Total Marks:  98
Array after sorting:
Name:           Jack Smith - Total Marks:  98
Name:           John Smith - Total Marks:  98
Name:          Ved Parkash - Total Marks:  99
Name:            Adam Noob - Total Marks: 100

Remarques:

1) Voir l'ordre d'ajout des étudiants dans la liste, c'est 4,3, 1 puis 2. C'est pour prouver qu'il trie selon le nom lorsque les notes correspondent (Jack Smith vs John Smith).

2) J'ai codé en dur les étudiants pour faire une meilleure démo.

3) Comme vous le remarquerez peut-être, je ne règle aucune des autres variables car la question concerne uniquement le tri, et les seules variables contribuant au tri sont: name et totMarks . Vous devrez faire le reste.

4) J'utilise ArrayList , mais ce n'est pas limité à cela, avec de simples changements, vous pouvez l'utiliser sur un Student [] normal tableau.

5) La fonction plus grande n'a pas besoin d'être statique , vous pouvez en faire une fonction membre et l'utiliser différemment. Par exemple, le code ci-dessus deviendrait:

import java.util.ArrayList; 

public class Student {
    private String name;
    private Integer rollNumber;
    private int m1;
    private int m2;
    private int m3;
    private int totMarks;

    public static boolean larger(Student s1, Student s2){
        if(s1.totMarks < s2.totMarks) return false; 
        else if (s1.totMarks > s2.totMarks) return true;
        // compare names
        else return s1.name.compareTo(s2.name) > 0;
    }

    public static ArrayList<Student> sortSelection(ArrayList<Student> list){
        for(int i=0; i<list.size(); i++){
            for(int j=i+1; j< list.size(); j++){
                if(larger(list.get(i), list.get(j))){ // swap
                    Student temp = list.get(i); 
                    list.set(i, list.get(j));
                    list.set(j, temp);
                }
            }
        }
        return list;
    }
    //Getter setter
    public String getName(){
        return name; 
    }

    public void setName(String name){
        this.name = name; 
    }

    public int getTotMarks(){
        return totMarks;
    }

    public void setTotMarks(int totMarks){
        this.totMarks = totMarks; 
    }

    @Override
    public String toString(){
        return String.format("Name: %20s - Total Marks: %3d", name, totMarks);
    }

    public static void main(String[] args){
        Student s1 = new Student(); 
        Student s2 = new Student();
        Student s3 = new Student();
        Student s4 = new Student();

        s1.setName("John Smith");
        s1.setTotMarks(98);
        s2.setName("Jack Smith");
        s2.setTotMarks(98);
        s3.setName("Adam Noob");
        s3.setTotMarks(100);
        s4.setName("Ved Parkash");
        s4.setTotMarks(99);

        ArrayList<Student> list = new ArrayList<>(); 
        list.add(s4);
        list.add(s3);
        list.add(s1);
        list.add(s2);

        System.out.println("Array before sorting:");
        for(int i=0; i<list.size(); i++){
            System.out.println(list.get(i).toString());
        }

        Student.sortSelection(list);

        System.out.println("Array after sorting:");
        for(int i=0; i<list.size(); i++){
            System.out.println(list.get(i).toString());
        }
    }
}


0 commentaires

1
votes

Dans l'intérêt de garder les choses simples (c'est-à-dire le principe KISS) et d'expliquer mon "indice" relatif à une clé composée, voici l'exemple travaillé.

La "clé" de la solution est de laisser l'arbre trier les données naturellement (pas, à mon humble avis, à ajouter au code le rendant plus complexe en fournissant un tri manuel). Ainsi, la classe d'élève doit renvoyer une clé que l'arborescence peut naturellement trier.

Pour produire le résultat de tri souhaité, la clé de l'arbre est (total des notes, nom de l'élève).

Voici la classe Student révisée (moins les getters et les setters, mais j'ai ajouté un nouveau constructeur pour me faciliter la vie):

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class StudentData {
    public static void main(String[] args) {
        // Initialise a list of students (as I don't want to rekey all the details each
        // time I run the program).
        List<Student> studentList = Arrays.asList(
                    new Student("Fred", 1, 2, 2, 2),      /* Score of 6 */
                    new Student("John", 2, 2, 3, 3),      /* Score of 8 */
                    new Student("Jane", 3, 20, 25, 30),      /* Score of 75 */
                    new Student("Julie", 4, 20, 15, 10)      /* Score of 45 */
                    // add as many new students as you like, and reorder them
                    // as much as you like to see if there is any difference in the
                    // result (there shouldn't be).
        );

            // Note the key of the tree is of type String - not Integer.
            // This is the heart of the algorithm, the tree will be "sorted"
            // on the natural key provided. This "natural key" is in fact
            // a compound key that is generated by combining the total score
            // and the student name.
        Map<String,Student> map = new TreeMap<String,Student>();
        for (Student ss : studentList) {
            map.put(ss.getKey(),ss);
        }   
        //stdList.forEach(System.out::print);
        for(Map.Entry<String,Student> m :map.entrySet()) {
            System.out.println(m);
        }
    }
}

Remarque strong > il y a une ligne de code commentée dans la méthode getKey avec le commentaire WRONG . Cela se rapporte à mon indice de test avec des scores à un chiffre. Essayez d'échanger les deux lignes de code pour voir le résultat correct et incorrect.

Voici le principal, j'ai supprimé tous les éléments du Scanner - encore une fois pour me faciliter la vie. J'espère que vous pourrez le suivre et le rajouter dans votre boucle d'analyse.

public class Student {
    private String name;
    private Integer rollNumber;
    private int m1;
    private int m2;
    private int m3;
    private int totMarks;
    //Getter setter

    public Student() {

    }

    public Student(String name, Integer rollNumber, int m1, int m2, int m3) {
        this.name = name;
        this.rollNumber = rollNumber;
        this.m1 = m1;
        this.m2 = m2;
        this.m3 = m3;
        this.totMarks = m1 + m2 + m3;
    }

    public String getKey() {
//      return String.format("%d-%s", totMarks, name);          // WRONG!
        return String.format("%04d-%s", totMarks, name);        // Right
    }

    public String toString() {
        return String.format("%06d: %s - %d", rollNumber, name, totMarks);
    }

}

J'espère que vous conviendrez que c'est une solution plus simple. Il y a aussi un avantage potentiel en termes de performance car les élèves sont triés au fur et à mesure qu'ils sont chargés dans l'arbre (c'est-à-dire une fois). La performance de ce tri est, je pense, log (n). Le tri lors de la récupération sera probablement n log (n) ou pire.


1 commentaires

Très intéressant votre principe KISS et aussi votre explication de mon problème de manière simple.