C# [Datenstruktur] Heap (Heap) Stapel (Stack)

Inhaltsverzeichnis

1. Einleitung

2. Das Konzept von Heap und Stack:

    2.1. Das Konzept des Heaps:

    2.2 Das Konzept des Stapels:

3. Der Unterschied zwischen Heap und Stack:

    3.1 Der Unterschied zwischen Heap- und Stack-Speicherplatzzuweisung:

    3.2 Unterschiede in den Stack-Cache-Methoden:

    3.3 Unterschiede in der Speichernutzung:

    3.4 Unterschiede in der Stack-Datenstruktur:

4. Implementieren Sie die Stapeldatenstruktur:

5. Anwendungsszenarien und Algorithmen des Stapels:

6. Vergleich von Stapel und Warteschlange:


1. Einleitung

        Wenn ein C#-Programm auf der CLR (Common Language Runtime) ausgeführt wird, wird der Speicher logisch in zwei Hauptteile unterteilt: den Stapel und den Heap. Neben Stapel und Heap verwaltet CLR auch einige andere Speicherbereiche, z. B. den statischen Speicherbereich (Static Storage Area), den konstanten Speicherbereich (Constant Storage Area) usw. Diese Speicherbereiche haben ihre eigenen Eigenschaften und Verwendungszwecke, die uns dabei helfen können, die Nutzung von Programmspeicher und -ressourcen besser zu verwalten.

       Wenn ein C#-Programm ausgeführt wird, stellen der Stapel und der Heap seine Grundelemente dar. Sie bilden die Laufumgebung des Programms und haben einen wichtigen Einfluss auf die Leistung und Stabilität des Programms. Das Verständnis und die Beherrschung des Konzepts und der Verwendung von Stack und Heap ist der Schlüssel zur Entwicklung hochwertiger C#-Programme.

2. Das Konzept von Heap und Stack:

    2.1. Das Konzept des Heaps:

        Der Heap ist ein dynamisch zugewiesener Speicherbereich, der zum Speichern von Daten verwendet wird, die das Programm zur Laufzeit benötigt. Wenn ein Objekt oder eine Variable deklariert wird, werden sie auf dem Heap zugewiesen und ihre Referenz (dh ein Zeiger darauf, wo das Objekt oder die Variable auf dem Heap gespeichert ist) wird zurückgegeben. Auf Objekte oder Variablen im Heap kann über ihre Referenzen zugegriffen und diese geändert werden.

    2.2 Das Konzept des Stapels:

       Der Stack ist ein Speicherbereich nach dem Last In First Out (LIFO)-Prinzip. Der Stapel speichert mehrere Arten von Daten: die Werte von Variablen bestimmter Typen, die aktuelle Ausführungsumgebung des Programms und die an Methoden übergebenen Parameter. Wenn ein Programm eine Methode aufruft, werden Daten wie die Parameter der Methode, die Rücksprungadresse und lokale Variablen auf den Stapel verschoben. Wenn die Methodenausführung endet, werden diese Daten vom Stapel entfernt.

       Die Eigenschaften des Stapels: (1) Daten können nur von der Oberseite des Stapels eingefügt und gelöscht werden. (2) Das Platzieren von Daten oben auf dem Stapel wird als Pushen bezeichnet. (3) Das Löschen von Daten oben im Stapel wird als „Popping“ bezeichnet.

3. Der Unterschied zwischen Heap und Stack:

    3.1 Der Unterschied zwischen Heap- und Stack-Speicherplatzzuweisung:

         Stapel (Betriebssystem): Wird vom Betriebssystem automatisch zugewiesen und freigegeben und speichert Funktionsparameterwerte, lokale Variablenwerte usw. Sein Betriebsmodus ähnelt dem Stapel in der Datenstruktur.

         Heap (Betriebssystem): Im Allgemeinen wird er vom Programmierer zugewiesen und freigegeben . Wenn der Programmierer ihn nicht freigibt, kann er am Ende des Programms vom Betriebssystem zurückgefordert werden . Die Zuweisungsmethode ähnelt einer verknüpften Liste.

    3.2 Unterschiede in den Stack-Cache-Methoden:

         Heap (Datenstruktur): Wird im Cache der zweiten Ebene gespeichert und der Lebenszyklus wird durch den Garbage-Collection -Algorithmus der virtuellen Maschine bestimmt (er kann nicht mehr recycelt werden, sobald er zu einem verwaisten Objekt wird). Daher ist die Geschwindigkeit beim Aufrufen dieser Objekte relativ gering.

         Stapel (Datenstruktur): Der Cache der ersten Ebene wird verwendet. Sie befinden sich normalerweise beim Aufruf im Speicherplatz und werden sofort nach dem Aufruf freigegeben .

    3.3 Unterschiede in der Speichernutzung:

         Stapel (Datenstruktur): Die Größe ist normalerweise begrenzt und es können nicht zu viele Daten auf dem Stapel gespeichert werden

        Heap (Datenstruktur): Die Größe kann je nach tatsächlichem Bedarf dynamisch erweitert werden . Eine übermäßige Nutzung des Heaps kann jedoch die Programmleistung und -stabilität beeinträchtigen.

    3.4 Unterschiede in der Stack-Datenstruktur:

         Heap (Datenstruktur): Referenztypdaten werden normalerweise auf dem Heap gespeichert. Referenztypen erfordern zwei Speichersegmente. Das erste Segment speichert die eigentlichen Daten, die sich immer im Heap befinden. Das zweite Segment ist ein Verweis darauf, wo die Daten auf dem Heap gespeichert sind. Wenn wir die Referenztypzuweisung verwenden, handelt es sich tatsächlich um eine Referenz des zugewiesenen Referenztyps. Wenn das Array ein Array vom Typ „Wert“ ist, wird der Wert direkt im Array gespeichert. Wenn es sich um ein Array vom Typ „Referenz“ handelt (das Array speichert den Referenztyp), speichert das Array die Referenz (Speicheradresse).

         Stapel (Datenstruktur): Daten vom Werttyp werden normalerweise auf dem Stapel gespeichert , z. B. Ganzzahlen, Gleitkommazahlen, Aufzählungen usw. Werttypen benötigen nur einen separaten Speicher zum Speichern der tatsächlichen Daten.

      Die in C# häufig verwendeten Datentypen sind in der folgenden Abbildung dargestellt:

4. Implementieren Sie die Stapeldatenstruktur:

Es gibt zwei gängige Methoden zum Implementieren einer Stapeldatenstruktur:

     4.1 Array-Implementierungsstapel:

     Arrays sind eine der einfachsten Möglichkeiten, einen Stack zu implementieren. Sie können ein Array verwenden, um die Elemente im Stapel zu speichern, und dann Zeiger verwenden, um den Überblick über die Oberseite des Stapels zu behalten. Beim Pushen auf den Stapel fügen Sie das Element einfach am Ende des Arrays hinzu und bewegen den Zeiger um ein Bit nach oben. Beim Popup entfernen Sie einfach das Element vom Ende des Arrays und bewegen den Zeiger um ein Bit nach unten.

 public class SeqStack<T> : IStack<T>
    {
        /// <summary>
        /// 顺序栈容量
        /// </summary>
        private int maxsize;
        /// <summary>
        /// 数组 用于存储顺序栈中的数据元素
        /// </summary>
        public T[] data;
        /// <summary>
        /// 指示顺序栈的栈顶
        /// </summary>
        private int top;
       
        /// <summary>
        /// 容量属性
        /// </summary>
        public int Maxsize
        {
            get { return maxsize; }
            set { maxsize = value; }
        }
        /// <summary>
        /// 栈顶属性
        /// </summary>
        public int Top
        {
            get { return top; }
        }

        /// <summary>
        /// 初始化栈
        /// </summary>
        /// <param name="size"></param>
        public SeqStack(int size)
        {
            data = new T[size];
            maxsize = size;
            top = -1;
        }

        /// <summary>
        /// 入栈操作
        /// </summary>
        /// <param name="item"></param>
        public void Push(T item)
        {
            if (IsFull())
            {
                Console.WriteLine("Stack is full");
                return;
            }
            data[++top] = item;
        }

        /// <summary>
        /// 出栈操作
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            T tmp = default(T);
            if (IsEmpty())
            {
                Console.WriteLine("Stack is empty");
                return tmp;
            }
            tmp = data[top];
            --top;
            return tmp;
        }

        /// <summary>
        /// 获取栈顶数据元素,但不弹出元素
        /// </summary>
        /// <returns></returns>
        public T GetTop()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Stack is empty");
                return default(T);
            }
            return data[top];
        }

        /// <summary>
        /// 获取栈的长度
        /// </summary>
        /// <returns></returns>
        public int GetLength()
        {
            return top+1;
        }

        /// <summary>
        /// 清空顺序栈
        /// </summary>
        public void Clear()
        {
            top = -1;
        }      
       
        /// <summary>
        /// 判断顺序栈是否为空
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            if(top == -1)
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 判断顺序栈是否已满
        /// </summary>
        /// <returns></returns>
        public bool IsFull()
        {
            if(top == maxsize - 1)
            {
                return true;
            }
            return false;
        }
    }

     4.2 Implementierungsstapel für verknüpfte Listen:

     Eine verknüpfte Listenimplementierung eines Stapels ist ein weiterer gängiger Ansatz. In einer Implementierung einer verknüpften Liste enthält jeder Knoten ein Element und einen Zeiger auf den nächsten Knoten. Verwenden Sie einen Zeiger, um den oberen Rand des Stapels zu verfolgen und das neue Element als neuen Knoten am Anfang der verknüpften Liste einzufügen. Wenn Sie den Stapel öffnen, löschen Sie einfach den ersten Knoten der verknüpften Liste und zeigen Sie mit dem Zeiger auf den nächsten Knoten.

 public class LinkStack<T> : IStack<T>
    {
        /// <summary>
        /// 栈顶指示器
        /// </summary>
        private StackNode<T> top;
        /// <summary>
        /// 栈中元素的个数
        /// </summary>
        private int size;
        /// <summary>
        /// 栈顶指示器属性
        /// </summary>
        public StackNode<T> Top
        {
            get { return top; }
            set { top = value; }
        }

        /// <summary>
        /// 元素个数属性
        /// </summary>
        public int Size
        {
            get
            {
                return size;
            }
            set
            {
                size = value;
            }
        }
        /// <summary>
        /// 初始化链栈
        /// </summary>
        public LinkStack()
        {
            top = null;
            size = 0;
        }
        /// <summary>
        /// 入栈操作
        /// </summary>
        /// <param name="item"></param>
        public void Push(T item)
        {
            StackNode<T> q = new StackNode<T>(item);
            if (top == null)
            {
                top = q;
            }
            else
            {
                q.Next = top;
                top = q;
            }
            ++Size;
        }

        /// <summary>
        /// 出栈操作
        /// </summary>
        /// <returns></returns>
        public T Pop()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Stack is empty");
                return default(T);
            }
            StackNode<T> p = top;
            top = top.Next;
            --size;
            return p.Data;
        }

        /// <summary>
        /// 获取栈顶节点的值
        /// </summary>
        /// <returns></returns>
        public T GetTop()
        {
            if (IsEmpty())
            {
                Console.WriteLine("Stack is empty");
                return default(T);
            }
            return top.Data;
        }

        /// <summary>
        /// 获取链栈的长度
        /// </summary>
        /// <returns></returns>
        public int GetLength()
        {
            return size;
        }
        /// <summary>
        /// 清空链栈
        /// </summary>
        public void Clear()
        {
            top = null;
            size = 0;
        }

        /// <summary>
        /// 判断链栈是否为空
        /// </summary>
        /// <returns></returns>
        public bool IsEmpty()
        {
            if(top == null && size == 0)
            {
                return true;
            }
            return false;
        }     
    }

Unabhängig davon, ob es sich um eine Array-Implementierung oder eine Implementierung einer verknüpften Liste handelt, folgt der Stapel dem Prinzip „Last In, First Out“ (LIFO), d. h. das zuletzt hinzugefügte Element ist das erste Element, das gelöscht wird.

5. Anwendungsszenarien und Algorithmen des Stapels:

Stack ist eine sehr grundlegende und wichtige Datenstruktur mit einer Vielzahl von Anwendungsszenarien und Algorithmen in der Informatik. Im Folgenden sind einige gängige Anwendungsszenarien und Algorithmen aufgeführt:

  1. Ausdrucksauswertung: Der Stack kann zum Parsen und Auswerten mathematischer Ausdrücke verwendet werden. Nach der Konvertierung eines Ausdrucks in einen Postfix-Ausdruck kann dieser mithilfe des Stacks ausgewertet werden und unterstützt verschiedene Operatorprioritäten.

  2. Compiler-Implementierung: Viele Compiler oder Interpreter verwenden einen Stack, um die Phasen der Syntaxanalyse und Codegenerierung zu implementieren.

  3. Rekursive Algorithmen: Rekursive Algorithmen können mithilfe von Funktionsaufrufstapeln implementiert werden. Jeder rekursive Aufruf erstellt einen neuen Stapelrahmen, der geöffnet wird, wenn die Rekursion zurückkehrt.

  4. Webbrowser-Verlauf: Browser verwenden häufig Stapel, um den Verlauf der Webseiten zu verfolgen, die ein Benutzer besucht hat. Jede neue Seite wird auf den Stapel geschoben und die vorherige Seite kann über die „Zurück“-Funktion vom Stapel entfernt werden.

  5. Klammerübereinstimmung: Der Stapel kann verwendet werden, um zu überprüfen, ob Klammern übereinstimmen. Immer wenn eine öffnende Klammer angetroffen wird, wird sie auf den Stapel verschoben; immer wenn eine schließende Klammer angetroffen wird, wird sie mit der öffnenden Klammer oben im Stapel abgeglichen.

  6. Labyrinthprobleme: Stapel können zur Lösung von Labyrinthproblemen verwendet werden, indem der Status der Suche während der Suche hin und her verfolgt wird, indem die aktuelle Position und der Suchpfad auf den Stapel verschoben werden.

In Bezug auf Algorithmen umfassen gängige Stapelalgorithmen Folgendes:

  1. Sortieralgorithmus: Stapel können verwendet werden, um Sortieralgorithmen auf der Grundlage des Teilens und Herrschens wie Schnellsortierung und Zusammenführungssortierung zu implementieren.

  2. DFS (Depth First Search): Die Tiefensuche kann mithilfe eines Stapels implementiert werden. Jedes Mal, wenn ein Knoten besucht wird, wird er auf den Stapel verschoben und wieder entfernt, nachdem alle Nachbarknoten verarbeitet wurden.

  3. DP (Dynamische Programmierung): In einigen Fällen kann ein Stapel verwendet werden, um den Memento-Mechanismus des dynamischen Programmieralgorithmus zu implementieren und so Doppelberechnungen zu vermeiden.

Kurz gesagt, der Stapel ist eine sehr häufig verwendete Datenstruktur, die in verschiedenen Programmierszenarien häufig verwendet wird.

6. Vergleich von Stapel und Warteschlange:

Stack (Stack) und Queue (Queue) sind eine der beiden grundlegendsten Datenstrukturen. Sie werden beide zum Speichern einer Reihe von Elementen verwendet, weisen jedoch unterschiedliche Verhaltensweisen hinsichtlich des Hinzufügens, Entfernens und Zugreifens auf Elemente auf.

  1. Die Reihenfolge, in der Elemente hinzugefügt werden: Der Stapel folgt dem „Last In First Out“ (LIFO)-Prinzip, d. h. das zuletzt hinzugefügte Element ist das erste entfernte Element. Die Warteschlange folgt dem Prinzip „First In, First Out“ (FIFO), d. h. das erste hinzugefügte Element ist das erste Element, das entfernt wird.

  2. So werden Elemente gelöscht: In einem Stapel kann nur das oberste Element gelöscht oder darauf zugegriffen werden. In der Warteschlange können neben dem Kopfelement auch andere Elemente gelöscht oder darauf zugegriffen werden.

  3. Auf Elemente an einer bestimmten Position zugreifen: Im Stapel gibt es keine Methode, auf Elemente an einer bestimmten Position zuzugreifen, und auf alle Elemente im Stapel kann nur durch Herausnehmen von Elementen zugegriffen werden. In einer Warteschlange kann jedoch über einen Index auf ein Element an einer bestimmten Position zugegriffen werden.

  4. Anwendungsszenarien: Stapel werden häufig in Szenarien verwendet, die ein Backtracking erfordern, beispielsweise in Algorithmen wie dem Parsen von Ausdrücken und Speichersuchen; während Warteschlangen häufig zum Implementieren von Puffern und Planern verwendet werden.

Kurz gesagt, Stapel und Warteschlangen haben unterschiedliche Eigenschaften und Anwendungsszenarien, und Entwickler müssen die geeignete Datenstruktur entsprechend den spezifischen Anforderungen auswählen.

Supongo que te gusta

Origin blog.csdn.net/beenles/article/details/130710732
Recomendado
Clasificación