C# [data structure] heap (Heap) stack (Stack)

Table of contents

1 Introduction

2. The concept of heap and stack:

    2.1. The concept of heap:

    2.2 The concept of the stack:

3. The difference between the heap and the stack:

    3.1 The difference between heap and stack space allocation:

    3.2 Differences in stack cache methods:

    3.3 Differences in memory usage:

    3.4 Differences in stack data structure:

4. Implement the stack data structure:

5. Application scenarios and algorithms of the stack:

6. Comparison of stack and queue:


1 Introduction

        When a C# program runs on the CLR (Common Language Runtime), the memory is logically divided into two main parts: the stack and the heap. In addition to stack and heap, CLR also maintains some other memory areas, such as static storage area (Static Storage Area), constant storage area (Constant Storage Area), etc. These memory areas have their own characteristics and uses, which can help us better manage the use of program memory and resources.

       When a C# program is running, the stack and the heap are its basic elements. They constitute the running environment of the program and have an important impact on the performance and stability of the program. Understanding and mastering the concept and usage of stack and heap is the key to developing high-quality C# programs.

2. The concept of heap and stack:

    2.1. The concept of heap:

        The heap is a dynamically allocated memory area used to store data needed by the program at runtime. When an object or variable is declared, they are allocated on the heap, and their reference (ie, a pointer to where the object or variable is stored on the heap) is returned. Objects or variables in the heap can be accessed and modified by their references.

    2.2 The concept of the stack:

       The stack is a memory area based on the Last In First Out (LIFO) principle. The stack stores several types of data: the values ​​of variables of certain types, the current execution environment of the program, and the parameters passed to methods. When a program calls a method, data such as the method's parameters, return address, and local variables are pushed onto the stack. When the method execution ends, these data are popped from the stack.

       The characteristics of the stack: (1) Data can only be inserted and deleted from the top of the stack. (2) Putting data on the top of the stack is called pushing. (3) Deleting data from the top of the stack is called popping.

3. The difference between the heap and the stack:

    3.1 The difference between heap and stack space allocation:

         Stack (operating system): automatically allocated and released by the operating system , storing function parameter values, local variable values, etc. Its operation mode is similar to the stack in the data structure;

         Heap (operating system): Generally, it is allocated and released by the programmer . If the programmer does not release it, it may be reclaimed by the OS at the end of the program . The allocation method is similar to a linked list.

    3.2 Differences in stack cache methods:

         Heap (data structure): It is stored in the second-level cache, and the life cycle is determined by the garbage collection algorithm of the virtual machine (it cannot be recycled once it becomes an orphan object). So the speed of calling these objects is relatively low.

         Stack (data structure): The first-level cache is used. They are usually in the storage space when they are called, and they are released immediately after the call ;

    3.3 Differences in memory usage:

         Stack (data structure): the size is usually limited , and too much data cannot be stored on the stack

        Heap (data structure): The size can be dynamically expanded according to actual needs , but excessive use of the heap may affect program performance and stability.

    3.4 Differences in stack data structure:

         Heap (data structure): Reference type data is usually stored on the heap. Reference types require two segments of memory. The first segment stores the actual data, which is always located in the heap. The second segment is a reference to where the data is stored on the heap. When we use reference type assignment, it is actually a reference of the assigned reference type. If the array is an array of value type, then the value is directly stored in the array, and if it is an array of reference type (the array stores the reference type), then the array stores the reference (memory address).

         Stack (data structure): Value type data is usually stored on the stack , such as integers, floating point numbers, enumerations, etc. Value types require only a separate piece of memory to store the actual data.

      The commonly used data types in C# are shown in the figure below:

4. Implement the stack data structure:

There are two common ways to implement a stack data structure:

     4.1 Array implementation stack:

     Arrays are one of the easiest ways to implement a stack. You can use an array to store the elements in the stack, and then use pointers to keep track of the top of the stack. When pushing to the stack, simply add the element to the end of the array and move the pointer up one bit. When popping, simply remove the element from the end of the array and move the pointer down one bit.

 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 Linked list implementation stack:

     A linked list implementation of a stack is another common approach. In a linked list implementation, each node contains an element and a pointer to the next node. Use a pointer to keep track of the top of the stack and insert the new element as a new node at the beginning of the linked list. When popping the stack, just delete the first node of the linked list and point its pointer to the next node.

 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;
        }     
    }

Whether it is an array implementation or a linked list implementation, the stack follows the "last in, first out" (LIFO) principle, that is, the last element added is the first element to be deleted.

5. Application scenarios and algorithms of the stack:

Stack is a very basic and important data structure, which has a wide range of application scenarios and algorithms in computer science. The following are several common application scenarios and algorithms:

  1. Expression evaluation: The stack can be used to parse and evaluate mathematical expressions. After converting an expression to a postfix expression, it can be evaluated using the stack and supports various operator precedence.

  2. Compiler Implementation: Many compilers or interpreters use a stack to implement the syntax analysis and code generation phases.

  3. Recursive Algorithms: Recursive algorithms can be implemented using function call stacks. Each recursive call creates a new stack frame, which is popped when the recursion returns.

  4. Web browsing history: Browsers often use stacks to keep track of the history of web pages a user has visited. Each new page is pushed onto the stack, and the previous page can be popped from the stack via the "back" function.

  5. Bracket Matching: The stack can be used to check if brackets match. Whenever an opening parenthesis is encountered, it is pushed onto the stack; whenever a closing parenthesis is encountered, it is matched against the opening parenthesis at the top of the stack.

  6. Maze Problems: Stacks can be used to solve maze problems by tracking the state of the search back and forth during the search by pushing the current position and search path onto the stack.

In terms of algorithms, common stack algorithms include the following:

  1. Sorting algorithm: Stacks can be used to implement divide-and-conquer-based sorting algorithms such as quick sort and merge sort.

  2. DFS (Depth First Search): Depth First Search can be implemented using a stack. Each time a node is visited, it is pushed onto the stack and popped after all neighbor nodes have been processed.

  3. DP (Dynamic Programming): In some cases, a stack can be used to implement the memento mechanism of the dynamic programming algorithm to avoid double computation.

In short, the stack is a very commonly used data structure, which is widely used in various programming scenarios.

6. Comparison of stack and queue:

Stack (Stack) and Queue (Queue) are one of the two most basic data structures. They are both used to store a set of elements, but have different behaviors in terms of adding, removing, and accessing elements.

  1. The order in which elements are added: The stack follows the "Last In First Out" (LIFO) principle, i.e. the last element added is the first element removed. The queue follows the principle of "first in, first out" (FIFO), that is, the first element added is the first element to be removed.

  2. How elements are deleted: In a stack, only the top element can be deleted or accessed. In the queue, in addition to the head element, other elements can also be deleted or accessed.

  3. Accessing elements at a specific position: In the stack, there is no method to access elements at a specific position, and all elements in the stack can only be accessed by popping elements. In a queue, however, an element at a specific position can be accessed through an index.

  4. Application scenarios: Stacks are often used in scenarios that require backtracking, such as in algorithms such as parsing expressions and memory searches; while queues are often used to implement buffers and schedulers.

In short, stacks and queues have different characteristics and application scenarios, and developers need to choose the appropriate data structure according to specific needs.

Guess you like

Origin blog.csdn.net/beenles/article/details/130710732