C++单链表简单实现栈和队列(继承和派生)

先上题目:
设计单链表类,并基于单链表类实现栈类和队列类:
(1)设计学生信息类StudentRecord,要求包含公有数据成员:string stuName和int stuNo,设计用于输出学生信息的公有成员函数:void print(),输出格式为:Name: stuName, Number: stuNo。
(2)设计学生链表的结点类StudentNode,要求包含公有数据成员:StudentRecord data和StudentNode *next。
(3)设计学生链表类LinkedList,要求定义头插入、头删除、尾插入、遍历等公有成员函数。
(4)由LinkedList派生LinkedStack类,基于单链表类的功能实现压栈和出栈的成员函数:void Push(StudentRecord record)和bool Pop(StudentRecord &record)。
(5)由LinkedList派生LinkedQueue类,基于单链表类的功能实现入队和出队的成员函数:void EnQueue(StudentRecord record)和bool DeQueue(StudentRecord &record)。
在main函数中:
定义一个LinkedQueue类的对象queue和一个LinkedStack类的对象stack,并根据用户的输入
分别对queue和stack作出相应的操作。若为"Push",则压栈;若为"EnQueue",则入队;若
为"Pop",则出栈;若为"DeQueue",则出队;若为"Exit",则退出;若为其它,则给出提示信
息"Input error!"。入栈和入队时,输入学生姓名和学号。出栈和出队时,若非空,则输出
被删除的学生信息;若栈空,则输出Stack is empty!";若队空,则输出"Queue is empty!"。

接下来附上解题思路。

我在单链表中引入一个头结点作为表头,以避免对空表的处理,从而避免对边界情况做特别考虑。作为表中的一个虚结点,这个头结点的值被忽略,不被看作表中的实际元素。以下是带有头结点的空表示意图:
在这里插入图片描述空表的临界条件为头指针head和尾指针tail值相等。

用单链表实现入栈,即是让单链表的插入结点方式为头插入。以下是示意图:
在这里插入图片描述

用单链表实现入队,即是让单链表的插入结点方式为尾插入。以下是示意图:
在这里插入图片描述

出栈和出队都统一成删除单链表里第一个结点(不包括头结点)。
出栈操作如下所示:
在这里插入图片描述
出队操作时要特殊考虑队中只有一个结点的情况:
在这里插入图片描述出队时队中不止一个结点:
在这里插入图片描述
接下来给出具体实现的代码:

class StudentRecord{//学生数据类
private:    
    string stuName;
    int stuNo;
public:
    void print()
    {
        cout<<"Name: "<<stuName<<", Number: "<<stuNo<<endl;
    }
    StudentRecord& operator=(const StudentRecord &stu)//重载等号
    {
        stuName=stu.stuName;
        stuNo=stu.stuNo;
        return *this;
    }
    void setData(string str,int no)//赋值操作
    {
        stuName=str;
        stuNo=no;
    }
    friend class LinkedList;
};

class StudentNode{//结点类
public:
    StudentRecord data;
    StudentNode *next;
    StudentNode()
    {
        next=NULL;
    }
};

在链表类中把出栈、出队统一为函数bool deleteNode(),把获取栈中、队中的一个结点数据统一为函数bool getData(StudentRecord &record)。

class LinkedList{//单链表类
protected:
    StudentNode *head,*tail;
public:
    LinkedList()//创建链表
    {
        head=tail=new StudentNode;
        head->next=NULL;
        (head->data).setData("head_node",0);
    }
    ~LinkedList()//删除链表
    {
        StudentNode *temp;
        while(head!=tail)
        {
            temp=head;
            head=head->next;
            delete temp;     
        }
        delete head;
    }
    void headAdd(StudentNode &node)//头插入结点
    {
        node.next=head;
        head=&node;
    }
    bool deleteNode()//从头删除一个结点
    {
        if(empty()){
            return false;//删除失败
        }
        StudentNode *temp;
        if((head->data).stuName=="head_node"){//如果是尾插
            if((head->next)->next==NULL){//如果只有一个结点(除头结点)
              tail=head;
              temp=head->next;
              head->next=NULL;
              delete temp;
              return true;//删除成功
            }
            else{//如果多于1个结点(除头结点)
                temp=head->next;
                head->next=(head->next)->next;
                delete temp;
                return true;//删除成功
            }
        }
        else{//如果是头插
            temp=head;
            head=head->next;
            delete temp;
            return true;//删除成功
        }
    }
    void tailAdd(StudentNode &node)//尾插入结点
    {
        tail->next=&node;
        tail=&node;
    }
    bool empty()//判断链表是否为空
    {
        if(head==tail){
            return true;
        }
        else return false;
    }
    bool getData(StudentRecord &record)//获取链表中第一个数据
    {
        if(empty()){
            return false;//获取失败
        }
        if((head->data).stuName=="head_node"){
            record=(head->next)->data;
            return true;//获取成功
        }
        else{
            record=head->data;
            return true;//获取成功
        }
    }
};

实际使用时出栈操作还伴随着获取结点数据,故bool Pop()函数中结点出栈前要先启用函数bool getData(StudentRecord &record)来保存即将出栈的结点。

class LinkedStack:protected LinkedList{//单链表派生栈类
public: 
    void Push(StudentRecord record)
    {
        StudentNode *temp=new StudentNode;
        temp->data=record;
        headAdd(*temp);//入栈即头插入
    }
    bool Pop(StudentRecord &record)
    {
        if(!getData(record)){//保存将要出栈结点数据
            return false;//栈空,无法进行出栈操作
        }
        else{//栈非空
            deleteNode();//出栈
            return true;//出栈成功
        } 
    }
};

实际使用时出队操作还伴随着获取结点数据,故bool DeQueue()函数中结点出队前要先启用函数bool getData(StudentRecord &record)来保存即将出队的结点数据。

class LinkedQueue:protected LinkedList{//单链表派生队类
public:
    void EnQueue(StudentRecord record)
    {
        StudentNode *temp=new StudentNode;
        temp->data=record;
        tailAdd(*temp);//入队即尾插
    }
    bool DeQueue(StudentRecord &record)
    {
        if(!getData(record)){//接收将要出队结点数据
            return false;//队空,无法进行出队操作
        }
        else{//队非空
            deleteNode();//出队
            return true;//出队成功
        }
    }
};

最后是主函数main()

#include <iostream>
#include <string>
using namespace std;

int main()
{
    LinkedQueue queue;
    LinkedStack stack;
    //signal用来获取操作,name用来接受学生姓名
    string signal,name;
    //number用来接收学生学号
    int number;
    //save用来保存结点数据        
    StudentRecord save;
    
    cin>>signal;
    while(signal!="Exit")
    {
        //操作为入栈或入队
        if(signal=="Push"||signal=="EnQueue"){
            cin>>name>>number;//接收数据
            save.setData(name,number);//获得结点数据
            if(signal=="Push"){//入栈操作
                stack.Push(save);//结点入栈
            }
            else queue.EnQueue(save);//入队操作,结点入队
            cin>>signal;//获取下一个操作
            continue;
        }
        //操作为出栈或出队
        if(signal=="Pop"||signal=="DeQueue"){
            if(signal=="Pop"){//出栈操作
                if(stack.Pop(save)){//成功出栈
                    save.print();//打印出栈结点数据
                }
                else cout<<"Stack is empty!"<<endl;
            }
            else{//出队操作
                if(queue.DeQueue(save)){//成功出队
                    save.print();//打印出队结点数据
                }
                else cout<<"Queue is empty!"<<endl;
            }
            cin>>signal;//获取下一个操作
            continue;
        }
        //不是入栈、入队,不是出栈、出队,也不是退出程序
        cout<<"Input error!"<<endl;
        cin>>signal;
    }
    return 0;
}

总结:
使用派生类时,三种继承方式的用途:
1.希望派生类仅能在类内访问父类protected成员,并能用对象来访问父类public成员,用public继承方式;
2.希望派生类仅能在类内访问父类public、protected成员,用protected继承方式;
3.希望派生类类内类外都无法访问父类protected、public成员,用private继承方式。
子类不继承父类构造函数,创建子类对象时,编译器将会先调用父类的构造函数,再调用子类的构造函数。

扫描二维码关注公众号,回复: 10620082 查看本文章
发布了17 篇原创文章 · 获赞 36 · 访问量 481

猜你喜欢

转载自blog.csdn.net/qq_44643644/article/details/105309505
今日推荐