Structure de données Java [Introduction]

définition

La structure de données fait référence à un ensemble d'éléments de données qui ont une ou plusieurs relations les uns avec les autres .

Structure de données commune

Les structures courantes pour le stockage de données incluent les tableaux, les piles, les files d'attente, les listes chaînées, les arbres, les graphiques, les tas, les tables de hachage, etc., comme indiqué dans la figure ci-dessous.
Insérez la description de l'image ici

Déployer

Un tableau est une séquence qui stocke plusieurs éléments en continu, et l'allocation en mémoire est également continue, et les éléments du tableau sont accessibles via des indices.

        String[] data = new String[]{
    
    "Tom","Jack","Jessica","Katherine","Layman"};
        System.out.println(data[3]);

Insérez la description de l'image ici

Avantages:
1. Recherche rapide d'éléments: grâce à l'indexation, vous pouvez accéder rapidement aux éléments à une position spécifiée
2. Traversée rapide du tableau: grâce à l'indexation, vous pouvez rapidement parcourir le tableau

Inconvénients:
1. Une fois la taille du tableau déterminée, elle ne peut pas être étendue
2. Le tableau ne peut stocker qu'un seul type de données
3. L'opération d'ajout et de suppression est lente car d'autres éléments doivent être déplacés.

  • Ajout d'éléments: Il est nécessaire de créer un nouveau tableau, de stocker le nouvel élément spécifié à la position d'index spécifiée, puis de copier l'élément de tableau d'origine à la position correspondant à l'index du nouveau tableau en fonction de l'index.

  • Supprimer l'élément: vous devez créer un nouveau tableau, copier l'élément du tableau d'origine à la position correspondant à l'index du nouveau tableau en fonction de l'index, et supprimer l'élément à la position d'index spécifiée dans le tableau d'origine sans copier.

Scène applicable:

Requêtes fréquentes, peu d'espace de stockage requis, augmentez et supprimez rarement les situations.

Empiler

La pile, également connue sous le nom de pile, est une table linéaire spéciale (à opération limitée) dont la restriction est de n'autoriser les opérations qu'en haut de la pile (semblable à un magasin).
L'opération consistant à placer des éléments à partir du haut de la pile est appelée poussée dans la pile, également appelée poussée de la pile, et la suppression d'éléments est appelée popping la pile, également appelée popping.
Insérez la description de l'image ici

Fonctionnalités

  • Premier entré, dernier sorti, dernier entré, premier sorti (analogue aux balles d'un magazine)

Scène applicable:

Récursivité , comme la séquence de Fibonacci.

File d'attente

La file d'attente, comme la pile, est également une table linéaire avec des opérations limitées. La limitation est que seules les insertions sont autorisées à une extrémité de la table et les suppressions sont effectuées à l'autre extrémité de la table.

  • Vous pouvez considérer la file d'attente comme une pile avec des extrémités ouvertes

Insérez la description de l'image ici
Fonctionnalités

  • Premier entré, premier sorti.

Scénario d'essai

Étant donné que la file d'attente a les caractéristiques du premier entré, premier sorti, elle est très appropriée pour la gestion de file d'attente de blocage multi-thread.

Liste liée

La liste liée est composée d'une série de nœuds (chaque élément de la liste liée est appelé un nœud). Chaque nœud comporte au moins deux champs, l'un est le champ de données pour stocker les éléments de données et l'autre pour stocker l'adresse de le nœud suivant. Champ de pointeur.

Le premier nœud et le dernier nœud de la liste chaînée sont appelés respectivement le nœud principal et le nœud de queue.

La caractéristique du nœud de queue est que sa prochaine référence est nulle.

Liste liée individuellement

Dans la liste liée individuellement, le champ de données de chaque nœud pointe vers l'élément de données via une référence d'objet de la classe Object.

Semblable à un tableau, les nœuds de la liste liée individuellement ont également un ordre linéaire, c'est-à-dire que si la référence suivante du nœud a1 pointe vers le nœud a2, alors a1 est le nœud prédécesseur direct de a2 et a2 est le nœud successeur direct de a1.

Le nœud suivant ne peut être trouvé que via le nœud prédécesseur, mais le nœud prédécesseur est introuvable à partir du nœud suivant.
Insérez la description de l'image ici

Fonctionnalités

  • Requête lente: l'adresse de stockage n'est pas continue, chaque élément de requête doit commencer depuis le début
  • Ajout et suppression rapides: vous n'avez pas besoin de déplacer le nœud, changez simplement le pointeur dans le nœud, mais vous devez d'abord localiser l'élément.
  • L'ordre des éléments n'est pas garanti: l'ordre de stockage et de récupération des éléments peut être incohérent.
  • Le nœud suivant ne peut être trouvé que via le nœud prédécesseur, mais le nœud prédécesseur est introuvable à partir du nœud suivant.

Liste doublement liée

Pour trouver le nœud prédécesseur d'un certain nœud dans la liste liée à un seul lien, il doit commencer à partir du nœud principal de la liste liée pour effectuer une recherche en arrière à son tour, ce qui nécessite Ο (n) temps.

Par conséquent, nous pouvons étendre la structure des nœuds de la liste liée individuellement, de sorte que, via la référence d'un nœud, non seulement puisse accéder à ses nœuds suivants, mais également à ses nœuds prédécesseurs. Il s'agit d'une liste doublement liée.

Une liste doublement liée est un nouveau domaine dans la structure de nœud de liste à lien unique, qui stocke le nœud prédécesseur qui pointe vers le nœud.

Une liste liée à une seule liaison ne peut être parcourue que dans une direction, et une liste à double liaison peut être parcourue à partir de deux directions.

Le nœud prédécesseur (pré) du nœud principal de la liste à double liaison est vide et le nœud suivant (Next) du nœud de queue est vide
Insérez la description de l'image ici
. Insérez et supprimez des nœuds dans la liste à double liaison, quel que soit l'endroit où les nœuds sont insérés et supprimé. Du fait de l'existence des premier et dernier nœuds, les opérations d'insertion et de suppression peuvent être attribuées à l'insertion et à la suppression d'un nœud intermédiaire.

En raison de l'existence des premier et dernier nœuds, la liste doublement liée n'est jamais vide.

Utilisez simplement une liste bidirectionnelle pour simuler LinkedList, le code est le suivant:

import lombok.Data;

/**
 * 简单用双向列表模拟LinkedList
 * @author layman
 * @date 2021/2/24
 */
public class MyLinkedList {
    
    
    //首节点
    private Node first;
    //尾节点
    private Node last;

    public void add(Object obj){
    
    
        if(first==null){
    
    
            //说明该元素存于首节点上
            Node firstNode = new Node();
            firstNode.setPrevious(null);
            firstNode.setObj(obj);
            firstNode.setNext(null);

            //此时该LinkedList只存了一个元素,所以首尾节点都设置为首节点
            first = firstNode;
            last = firstNode;
        }else{
    
    
            //存入的不是首节点,则该元素存于首节点之后,指向前一个节点的last
            Node node=new Node();
            node.setPrevious(last);
            node.setObj(obj);
            node.setNext(null);
            
            //将链表的last指针指向node
            last.setNext(node);
            //将链表的last改为node
            last = node;
        } 
    }
}
// 节点类
@Data
class Node {
    
    
    //前驱节点
    private Object previous;
    //存储的数据
    private Object obj;
    //后续节点
    private Object next;
}

Liste liée circulaire

La liste chaînée dans laquelle le nœud principal et le nœud de queue sont connectés ensemble est appelée une liste chaînée circulaire, et cette méthode peut être implémentée dans des listes chaînées unidirectionnelles et bidirectionnelles.

Avant le premier nœud de la liste liée circulaire se trouve le dernier nœud, et vice versa.

Insérez la description de l'image ici

Pour résumer

Avantages des listes chaînées:

  • La liste chaînée n'a pas besoin d'initialiser la capacité, vous pouvez ajouter ou soustraire des éléments à volonté;
  • Requête lente: l'adresse de stockage n'est pas continue et chaque élément de requête doit commencer depuis le début, ce qui prend beaucoup de temps
  • Ajout et suppression rapides: vous n'avez pas besoin de déplacer le nœud, changez simplement le pointeur dans le nœud, mais vous devez d'abord localiser l'élément.
  • Parce qu'il contient un grand nombre de champs de pointeur, il prend beaucoup d'espace

Scène applicable:

Scénarios où la quantité de données est faible et doit être fréquemment augmentée et supprimée

Arbre

Un arbre est une structure de données, qui est une collection hiérarchique composée de n (n> = 1) nœuds finis.

On l'appelle un «arbre» ​​parce qu'il ressemble à un arbre à l'envers (les racines sont tournées vers le haut et les feuilles sont tournées vers le bas).

Fonctionnalités

  • Chaque nœud a zéro ou plusieurs nœuds enfants.
  • Un nœud sans nœud parent est appelé le nœud racine.
  • Chaque nœud non racine a un et un seul nœud parent.
  • À l'exception du nœud racine, chaque nœud enfant peut être divisé en plusieurs sous-arbres disjoints.

L'arbre est divisé en arbre binaire, arbre équilibré, arbre rouge-noir et ainsi de suite.

Dans le langage Java, l'arbre binaire le plus discuté et utilisé est l'arbre binaire.

Un arbre binaire est un arbre ordonné ne comportant pas plus de 2 nœuds. Il présente les caractéristiques suivantes:

  • Chaque nœud a au plus deux sous-arbres et le degré maximum du nœud est de 2.
  • Le sous-arbre gauche et le sous-arbre droit sont dans l'ordre et l'ordre ne peut pas être inversé.
  • Même si un nœud n'a qu'un seul sous-arbre, les sous-arbres gauche et droit doivent être distingués.

Scène applicable:

Les arbres binaires ajoutent et suppriment des éléments très rapidement, et il existe de nombreuses optimisations d'algorithmes dans la recherche. Par conséquent, les arbres binaires présentent à la fois les avantages des listes chaînées et les avantages des tableaux. Ce sont les schémas d'optimisation des deux, et ils sont uniques dans leur gestion. grandes quantités de données dynamiques Les avantages.

Expansion:

Les arbres binaires ont de nombreuses structures de données étendues, telles que des arbres binaires équilibrés, des arbres rouge-noir et des arbres B +.

Ces structures de données dérivent de nombreuses fonctions sur la base d'arbres binaires et sont largement utilisées dans des applications pratiques.

Par exemple, la structure d'index de la base de données MySQL utilise des arbres B + et le code source sous-jacent de HashMap utilise des arbres rouge-noir. Ces arbres binaires sont puissants, mais l'algorithme est plus compliqué. Si vous voulez apprendre, vous devez passer du temps et de la profondeur. Cet article est limité dans l'espace et ne sera pas développé un par un.
Insérez la description de l'image ici

Graphique

(Comprends juste)

Le graphe est composé d'un ensemble fini V de nœuds et d'un ensemble E d'arêtes. Parmi eux, afin de le distinguer de la structure arborescente, les nœuds sont souvent appelés sommets dans la structure du graphe, et les arêtes sont des paires ordonnées de sommets. S'il y a une arête entre deux sommets, cela signifie que les deux sommets sont adjacents relation.

Selon la direction dans laquelle les sommets pointent, il peut être divisé en graphes non orientés et graphes orientés.
Insérez la description de l'image ici
Insérez la description de l'image ici
Le graphique est une structure de données relativement complexe avec un algorithme très complexe et efficace. Il ne sera pas développé ici. Les lecteurs intéressés peuvent en apprendre davantage par eux-mêmes.

Tas

Heap est une structure de données spéciale, qui peut être considérée comme un objet tableau d'une arborescence. Elle possède les propriétés suivantes:

  • La valeur d'un nœud dans le tas n'est toujours pas supérieure ou non inférieure à la valeur de son nœud parent;

  • Heap est toujours un arbre binaire complet.

Le tas avec le nœud racine le plus grand est appelé le plus grand tas ou le grand tas racine, et le tas avec le plus petit nœud racine est appelé le plus petit tas ou petit tas racine.

Les piles courantes comprennent les piles binaires, les piles de Fibonacci, etc.

La définition d'un tas est la suivante: une séquence de n éléments {k1, k2, ki, ..., kn} est appelée tas si et seulement si la relation suivante est satisfaite.
(ki <= k2i, ki <= k2i + 1) ou (ki> = k2i, ki> = k2i + 1), (i = 1,2,3,4 ... n / 2), ce qui satisfait le premier expression Il devient une petite pile supérieure, et celle qui satisfait cette dernière expression est une grande pile supérieure. Les diagrammes de structure des deux peuvent être disposés dans un arbre binaire complet. L'exemple de diagramme est le suivant:
Insérez la description de l'image ici
En raison des caractéristiques ordonnées de la pile, il est généralement utilisé pour trier dans le tableau, appelé Trier le tas.

Hacher

Une table de hachage, également appelée table de hachage, est une structure de données accessible directement en fonction des codes clés et des valeurs (clé et valeur).

HashCode: est un entier décimal, donné aléatoirement par le système (c'est-à-dire la valeur d'adresse de l'objet, c'est une adresse logique, pas l'adresse de stockage réelle des données .)

public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        Layman layman = new Layman("layman",18);
        /**
         * public native int hashCode() : Object类中的方法,可以返回对象的哈希值(一个十进制整数)。
         */
        System.out.println(layman.hashCode()); //-1109720331
    }
    //Object类中toString()的源码
    public String toString() {
    
    
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
}
@Data
@AllArgsConstructor
class Layman{
    
    
    private String name;
    private Integer age;
}

Emplacement de stockage d'enregistrement = F (clé)

La table de hachage consiste à convertir la clé en un entier décimal via la fonction d'algorithme F (fonction de hachage), puis à prendre le reste de l'entier à la longueur du tableau, à prendre le reste comme indice du tableau et à stocker la valeur sous le nombre L'espace de la matrice cible.

Cet espace de stockage utilise pleinement l'avantage de recherche du tableau pour trouver des éléments, de sorte que la vitesse de recherche est rapide .

Les tables de hachage sont largement utilisées. Par exemple, certaines classes de collection (HashMap, HashTable) en Java sont construites sur le principe du hachage, et il est très pratique de trouver des éléments.

Cependant, étant donné que la table de hachage est basée sur une structure de données dérivée d'un tableau, l' ajout et la suppression d'éléments est lent . Afin de résoudre le problème de l'ajout et de la suppression lents d'éléments, la couche inférieure de la table de hachage adopte un tableau + liste liée (la requête de tableau est rapide et la liste liée est ajoutée et supprimée rapidement ).

  • Avant JDK1.8, la couche inférieure de la table de hachage était implémentée par tableau + liste liée, c'est-à-dire que la liste liée est utilisée pour gérer les conflits et que les données de la même valeur de hachage sont stockées dans une liste liée. Cependant, lorsqu'il existe de nombreux éléments avec des valeurs de hachage égales, l'efficacité de la recherche séquentielle par valeur de clé est faible.
  • Dans JDK1.8, le stockage de la table de hachage est implémenté par tableau + liste liée + arbre rouge-noir. Lorsque la longueur de la liste liée dépasse le seuil (8), la liste liée est convertie en un arbre rouge-noir, ce qui réduit le temps de recherche.

Je suppose que tu aimes

Origine blog.csdn.net/single_0910/article/details/114004426
conseillé
Classement