美文网首页贪婪的君子程序员
线性表、堆栈以及队列

线性表、堆栈以及队列

作者: 贪婪的君子 | 来源:发表于2017-07-09 22:56 被阅读19次

    线性表、链表以及队列是在coding中最为常见的数据结构,在平时编程时,我们会有意识或无意识的进行选择。

    1. 线性表


    线性表本质上是一种顺序存储结构,所有需要存储的内容是可以被索引的,说起来,在编程时常用的数组就是线性表的一种。
    线性表的数据结构表示:

    class LinearList 
    {
        private:
            int MaxSize;      //线性表的最大尺寸
            int length;     //线性表的实际长度
            int *element;     //存储线性表的数组
                      //一般使用模板来代替int定义,这里为了方便,使用int
        public:
            //线性表的构造函数,默认最大尺寸为10
            LinearList(int MaxSize = 10);
            ~LinearList() { delete[] element;  }
    
            bool IsEmpty()const { return length == 0; }
            bool IsFull()const { return length == MaxSize; }
        
            int Length()const { return length; }
        
            //线性表的存取函数,其中k表示索引号,item表示存取的数据值
            bool Find(int k, int &item)const;
            int Search(const int &item)const;
            void Delete(int k, int &item)const;
            void Insert(int k, const int &item);
    }
    

    不难看出,线性表的优势在于随机访问的效率高,速度快(因为可以进行索引),而且实现起来较为简单,但是由于先行表的最大长度固定,所以想要增加线性表的长度并不容易,如果需要进行线性表的长度变化,可以创建一个新的线性表,并且将原始的线性表中的内容完全复制到新的线性表中,删除原始线性表即可。

    2. 链表结构


    链表弥补了线性表的缺点,但是又增加了新的问题。看如下例程:

    class Node
    {
        private:
            int data;   //数据域,一般为模板类
            Node *next; //指向下一个结点
        public:
            Node(Node *nextNode = NULL) { next = nextNode; }
            Node(const int& item, Node *nextNode = NULL)
                { data = item;  next = nextNode; }
            void setData(int data);
            int Data() { return data; }
    };
    
    class LinkedList
    {
        private:
            Node *head;  //头指针
        public:
            LinkedList() { head = new Node(); }
            LinkedList(int &item);
            ~LinkedList();
    
            bool IsEmpty()const { return head->next == NULL; }
            int Length()const; //返回链表长度
    
            //这里的实现多种多样,此下几个函数为其中一种
            //通过遍历链表找到第k个元素,将该元素赋值给item
            bool Find(int k, int &item)const;
            int Search(int k, int &item)const;
            void Delete(int k, int &item)const;
            void Insert(int k, const int &item);
    }
    

    该程序结点只是链表的一种,单向链表。还有其他如,循环链表,双向链表,十字链表等等。
    循环链表中最后的一个元素的后继结点为表头结点,双向链表中每个元素有两个指针,分别指向其前驱结点与后继结点。

    Tip:
    前驱结点:对于某一个结点来说,所有指向该结点的结点,就是该结点的前驱结点。
    后继结点:对于某一个结点来说,该结点所指向的下一个结点,就是该节点的后继结点。

    3. 堆栈


    堆栈这一概念应该是分开的,堆是堆,栈是栈,两种不同的东西。
    栈的基本组成为栈顶指针,压栈,弹栈动作,并且只能对栈顶元素进行读写操作,。其逻辑结构是先进栈的元素后读出,后进入的元素先读出,我们可以称之为后来居上的原则(LIFO)。栈也仅仅是一种概念,其实现是由其他基本数据结构完成的。

    在这里只讲栈,不讲堆。

    3.1 顺序栈

    顺序栈,其实就是用线性表来实现的栈。
    顺序栈的主要思想是:创建一个线性表,栈顶指针为索引号,其值为当前栈顶的索引号,压栈和弹栈的动作仅仅是对栈顶指针的值进行赋值和清除。

    class AStack
    {
        private:
            int size;     //线性表的长度
            int *stackArray;    //存放栈顶元素的数组
            int top;    //栈顶指针
        public:
            AStack(int MaxStackSize = 10)
            {
                size = MaxStackSize;
                stackArray = new int[MaxStackSize];
                top = -1;
            }
            ~AStack()
            { delete[] stackArray; }
            
            bool Push(const int &item);   //压栈
            bool Pop(int &item);     //弹栈
            bool Peek(int &item)const;      //读取栈顶元素
            int IsEmpty()const { return top == -1; }
            int IsFull()const { return top == size - 1; }
            void clear() { top = -1; }
    

    顺序栈的优势与线性表的优势相同,都是实现简单,效率高,缺点也是不易对栈进行扩充。

    3.2 链式栈

    听名字就知道了,这种栈是以链表存储结构实现的。

    class LStack
    {
        private:
            Node *top;   //指向栈顶指针
        public:
            LStack() { top = NULL; }
            ~LStack() { clear(); }
            void clear();   //清空栈
    
            bool Push(const int &item);
            bool Pop(int &item);
            bool Peek(int &item);
            int IsEmpty()const { return top == NULL; }
    }
    

    链式栈的优缺点可以与链表结构相似。

    4. 队列


    队列和堆栈的性质较为类似,只是队列采取的原则是,先进先出原则(FIFO)。在队列中所有的操作均只能对队首与队尾进行,而在实现上,也分为顺序队列以及链式队列。

    这种原则和日常排队相像,先到先得。

    4.1 顺序队列

    使用顺序存储的方式实现,其主要思想为:
    创建一个线性表,增加两个指针,分别命名为队首指针,指向队列最开始的位置,并且元素由此出队,队尾指针,指向当前插入队列的位置,并且只能向后插入。

    其实队列还可以增加一个指针,用于遍历队列中的元素,但是这样做就破坏了队列操作的原则,使队列退化成了一个简单的线性表。

    class AQueue
    {
        private:
            int front;     //队首
            int rear;     //队尾
            int count;    //队列元素个数
            int *QArray;   //队列数组
            int size;    //队列大小
        public:
            AQueue(int MaxQueueSize = 10);
            ~AQueue() { delete[] QArray; }
    
            bool QInsert(const int &item);    //向队尾插入元素
            bool QDelete(int &item);     //删除队首元素
            
            void QClear() { front = rear = count = 0; }
            int QFront()const;    //读取队首元素
            bool IsEmpty()const { return count == 0; }
            bool IsFull()const { return count == size; }
    }
    

    4.2 链式队列

    思想和顺序队列类似,实现如下:

    class LQueue
    {
        private:
            Node *front, *rear;
        public:
            LQueue() { front = rear = NULL; }
            ~LQueue() { QClear(); }
    
            void QInsert(const int &item);
            bool QDelete(int &item);
            bool QFront(int &item);
    
            int IsEmpty()const { return front == NULL; }
            void QClear();
    

    无论是堆栈还是队列,上述几种都是较为基础的形式,关于堆栈和队列,还有其相应的扩展,如双端队列,双栈,超队列,超栈等等。

    5. 总结


    数据结构是计算机开发中最为重要的一门知识,可以说,只要是编程,就会用到数据结构。
    本文仅仅是数据结构的冰山一角,但是随着编程的深入,你会发现,这冰山一角却是冰山最重要的基础。

    数据结构的定义很广,包括使用C语言或是其他语言编写一个小的struct结构体,都可以被视为一中数据结构。
    还是那句话,所有复杂的事物,都是由简单的符号所组成的。

    相关文章

      网友评论

        本文标题:线性表、堆栈以及队列

        本文链接:https://www.haomeiwen.com/subject/hkiehxtx.html