• 《算法导论》 — Chapter 10 基本数据结构


    在本章中,要讨论如何通过使用了指针的简单数据结构表示动态集合。有很多的复杂的数据结构可以用指针来构造,本章介绍几种基本数据结构,包括栈、队列、链表,以及有根树。

    GitHub 第十章 程序实现代码

    栈和队列都是动态集合,在这种结构中delete操作去掉的元素是预先规定好的。栈数据结构实现的是一种先进后出(FILO)的策略。作用于栈上的Insert操作称为压入Push,而无参数的Delete操作称为弹出Pop。
    本章讨论的栈是用数组实现,数据结构定义为(CStack.h):

    #ifndef _CSTACK_H
    #define _CSTACK_H
    
    #include <iostream>
    
    class CStack{
    
    public:
        CStack();
        ~CStack();
    
        int getTop()
        {
            return data[top];
        }
    
        bool empty()
        {
            return top == -1;
        }
    
        bool full()
        {
            return top == (maxSize - 1);
        }
        void push(int x);
    
        void pop();
    
        void display();
    
    private:
        int *data;
        int top;
        const int maxSize = 100;
    };
    #endif

    如上所示,类CStack表示一个自定义栈,其包含数据成员data这样一个动态数组存储元素,top为当前栈顶元素的下标,当top==-1时表示栈空,当top==maxSize-1时表示栈满。
    类实现(CStack.cpp)如下:

    #include "CStack.h"
    #include <iostream>
    
    CStack::CStack()
    {   
        top = -1;
        data = new int[maxSize];
    }
    
    CStack::~CStack()
    {
        delete data;
    }
    
    void CStack::push(int x)
    {
        if (full())
        {
            std::cout << "栈已满!" << std::endl;
            return;
        }       
        else
            top = top + 1;
        data[top] = x;
    }
    
    void CStack::pop()
    {
        if (empty())
        {
            std::cout << "栈空!" << std::endl;
            return;
        }
        else
            std::cout << data[top--] << std::endl;
    }
    
    void CStack::display()
    {
        if (empty())
        {
            std::cout << "栈空!" << std::endl;
            return;
        }
    
        for (int i = 0; i <= top; i++)
            std::cout << data[i] << "	";
        std::cout << std::endl;
    }

    下面给出对以上自定义栈的测试程序(main.cpp)与测试结果:

    #include "CStack.h"
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        CStack cs;
    
        //显示栈中元素
        cout << "当前栈中元素有:" << endl;
        cs.display();
    
        //添加元素入栈
        for (int i = 0; i < 10; i++)
            cs.push(i*i);
    
        //显示栈中元素
        cout << "当前栈中元素有:" << endl;
        cs.display();
    
        //显示栈顶元素
        cout << "栈顶元素为:" << endl;
        cout << cs.getTop() << endl;
    
        //栈顶元素出栈
        cout << "栈顶元素出栈:"<< endl;
        cs.pop();
    
        //重新打印栈元素
        cout << "当前栈中元素有:" << endl;
        cs.display();
    
        system("pause");
        return 0;
    
    }

    测试结果为:
    自定义栈测试结果

    队列

    类似于数据结构栈,队列实现了一种先进先出(FIFO)的策略,在队列中,可以去掉的那个元素总是在集合中存在时间最长的那个。
    我们把作用于队列上的Insert操作称为入对EnQueue,把作用于队列上的Delete操作称为出队DeQueue。
    本章讨论的队列也是用数组实现的,基本结构(CQueue.h)如下:

    #ifndef _CQUEUE_H_
    #define _CQUEUE_H_
    
    #include <iostream>
    class CQueue{
    public:
    
        CQueue();
        ~CQueue();
    
        bool empty()
        {
            return head == tail;
        }
    
        bool full()
        {
            return ((head == tail + 1) || (head == 0 && tail == maxSize-1));
        }
    
        void enQueue(int x);
        void deQueue();
        void display();
    
    private:
        int *data;
        int head;
        int tail;
        //队列最大容量,最多存储maxSize-1个元素
        const int maxSize = 10;
    };
    #endif

    如上所示,队列使用数据成员data这样一个动态数组存储元素,另外还包含的队头,队尾两个成员变量,在本程序实现的是一个循环队列,当head == tail时队列为空,当((head == tail + 1) || (head == 0 && tail == maxSize-1))队列为满,整个队列中最多存储了maxSize-1个元素。
    类实现(CQueue.cpp)如下:

    #include "CQueue.h"
    #include <iostream>
    
    using namespace std;
    
    CQueue::CQueue()
    {
        head = 0;
        tail = 0;
        data = new int[maxSize];
    }
    
    CQueue::~CQueue()
    {
        delete data;
    }
    
    void CQueue::enQueue(int x)
    {
        if (full())
        {
            cout << "队列已满" << endl;
            return;
        }
    
        data[tail] = x;
        if (tail == maxSize - 1)
            tail = 0;
        else tail += 1;
    }
    
    void CQueue::deQueue()
    {
        if (empty())
        {
            cout << "队列空!" << endl;
            return;
        }
    
        cout << data[head] << endl;
    
        if (head == maxSize - 1)
            head = 0;
        else
            head += 1;
    }
    
    void CQueue::display()
    {
        if (empty())
        {
            cout << "队列空!" << endl;
            return;
        }
    
        if (head > tail)
        {
            for (int i = head; i < maxSize-1; i++)
                cout << data[i] << "	";
            for (int j = 0; j < tail; j++)
                cout << data[j] << "	";
        }
        else{
            for (int i = head; i < tail; i++)
                cout << data[i] << "	";
        }
        cout << endl;
    }

    下面给出对以上自定义队列的测试程序(main.cpp)与测试结果:

    #include "CQueue.h"
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        CQueue cq;
    
        cout << "此时队的状态是:" << endl;
        cq.display();
    
        for (int i = 0; i < 9; i++)
            cq.enQueue(i + 1);
    
        //显示队列中所有元素
        cout << "显示队中所有元素" << endl;
        cq.display();
    
        //再次入队一个元素
        cout << "元素11入队!";
        cq.enQueue(11);
    
        //队头依次出队
        cout << "队头元素:" << endl;
        cq.deQueue();
        cout << "队头元素:" << endl;
        cq.deQueue();
        cout << "队头元素:" << endl;
        cq.deQueue();
    
        //此时入队新元素
        cout << "元素11入队!"<<endl;
        cq.enQueue(11);
        cout << "元素12入队!"<<endl;
        cq.enQueue(12);
    
        //再次显示队列中所有元素
        cout << "显示队中所有元素" << endl;
        cq.display();
    
        system("pause");
        return 0;
    }

    测试结果:
    自定义队列程序测试结果

    双向链表

    在链表这种数据结构中,各个对象按照线性顺序排序。链表与数组不同,数组的线性顺序是由数组下标决定的,而链表中的顺序是由各个对象的指针决定的。
    对于本节讨论的双向链表,定义结构(CList.h)如下:

    #ifndef _CLIST_H_
    #define _CLIST_H_
    
    #include <iostream>
    
    //双向链表结构体
    typedef struct Node{
        Node *prev;
        Node *next;
        int key;
    
        Node(int x) :prev(NULL), next(NULL), key(x){}
    
    };
    
    class CList{
    public:
        CList();
        ~CList();
    
        void Insert(int x);
        Node *Search(int value);
        void Delete(int x);
        void Display();
    
    private:
        Node *head;
    };
    #endif
    

    如上所示,结构体Node定义了链表中结点的基本内容,包括prev,next两个指针来实现双向性,以及一个key存储结点元素。在CList类中定义了4中基本操作,本处对于增、查、删操作函数参数均为基本元素而不是链表结点(书中是以结点作为参数的,本处认为以元素值作为参数封装性更好)。
    类实现(CList.cpp)如下:

    #include "CList.h"
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    CList::CList()
    {
        head = NULL;
    }
    
    CList::~CList()
    {
        delete head;
    }
    
    void CList::Insert(int x)
    {
        //新建一个结点,其值为x
        Node *node = new Node(x);
        node->next = head;
    
        if (head != NULL)
        {
            head->prev = node;
        }
        head = node;
        head->prev = NULL;
    }
    
    Node * CList::Search(int value)
    {
        Node *node = head;
        while (node != NULL && node->key != value)
        {
            node = node->next;
        }
        //如果找到相应结点
        if (node)
            return node;
        else
            return NULL;
    }
    
    //删除值为x的结点
    void CList::Delete(int x)
    {
        if (head == NULL)
        {
            std::cout << "链表为空!" << endl;
            return;
        }
        Node *node = Search(x);
    
        if (node)
        {
            if (node->prev != NULL)
                node->prev->next = node->next;
            else
                head = node->next;
    
            if (node->next != NULL)
                node->next->prev = node->prev;
    
            delete node;
    
            return;
        }
        else{
            std::cout << "链表中无值为"<<x<<"的元素!" << endl;
            return;
        }   
    }
    
    void CList::Display()
    {
        if (head == NULL)
        {
            std::cout << "链表为空!" << std::endl;
            return;
        }
        Node *node = head;
        while (node)
        {
            cout << node->key << "	";
            node = node->next;
        }
        cout << endl;
    }

    代码说明不再赘述,下面给出自定义双向链表的测试程序(main.cpp)以及测试结果:

    #include "CList.h"
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        CList list;
    
        //打印链表
        list.Display();
    
        //向链表插入元素
        for (int i = 0; i < 10; i++)
            list.Insert(i*i);
    
        cout << "打印链表:" << endl;
        list.Display();
    
        //查找元素为9的结点
        Node *node = list.Search(9);
        cout << "查找元素为9的结点" << node->key << endl;
    
        //删除元素为10的结点
        list.Delete(10);
    
        cout << "打印链表:" << endl;
        list.Display();
    
        //删除元素为36的结点
        list.Delete(36);
        cout << "打印链表:" << endl;
        list.Display();
    
        system("pause");
        return 0;
    }

    测试结果:
    自定义双向链表测试结果

    带哨兵的循环双向链表

    我们在处理边界条件时为了方便而且减少错误,经常使用哨兵(Sentinel),它是一个哑对象,可以简化边界条件。此处,在链表的实现中实现哨兵,不仅可以减少因边界处理不当而引起的指针异常,还可以实现循环的双向链表。基本操作与上类似,下面给出具体实现:
    数据结构(SList.h)如下:

    #ifndef _SLIST_H_
    #define _SLIST_H_
    
    #include <iostream>
    
    /**
     *  带哨兵的环形双向链表结构体
     */
    
    typedef struct SNode{
        SNode *prev;
        SNode *next;
        int key;
    
        SNode(int x) :prev(NULL), next(NULL), key(x){}
    };
    
    class SList{
    
    public:
        SList();
        ~SList();
    
        void Insert(int x);
        SNode *Search(int value);
        void Delete(int x);
        void Display();
    private:
        SNode *nil;
    };
    #endif

    类实现(SList.cpp)如下:

    #include "SList.h"
    #include <iostream>
    
    using namespace std;
    
    SList::SList()
    {
        nil = new SNode(0);
        nil->prev = nil;
        nil->next = nil;
    }
    
    SList::~SList()
    {
        delete nil;
    }
    
    void SList::Insert(int x)
    {
        //新建一个结点,其值为x
        SNode *node = new SNode(x);
        node->next = nil->next;
    
        nil->next->prev = node;
        nil->next = node;
        node->prev = nil;
    }
    
    SNode * SList::Search(int value)
    {
        SNode *node = nil->next;
        while (node != nil && node->key != value)
        {
            node = node->next;
        }
        //如果找到相应结点
        if (node != nil)
            return node;
        else
            return NULL;
    }
    
    //删除值为x的结点
    void SList::Delete(int x)
    {
        if (nil->next == nil)
        {
            std::cout << "链表为空!" << endl;
            return;
        }
        SNode *node = Search(x);
    
        if (node)
        {
            node->prev->next = node->next;
            node->next->prev = node->prev;
    
            delete node;
    
            return;
        }
        else{
            std::cout << "链表中无值为" << x << "的元素!" << endl;
            return;
        }
    }
    
    void SList::Display()
    {
        if (nil == nil)
        {
            std::cout << "链表为空!" << std::endl;
            return;
        }
        SNode *node = nil->next;
        while (node->next != nil)
        {
            cout << node->key << "	";
            node = node->next;
        }
        cout << endl;
    }

    下面给出测试程序(main.cpp)和测试结果:

    #include "SList.h"
    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    int main()
    {
        SList list;
    
        //打印链表
        list.Display();
    
        //向链表插入元素
        for (int i = 0; i < 10; i++)
            list.Insert(i*i);
    
        cout << "打印链表:" << endl;
        list.Display();
    
        //查找元素为9的结点
        SNode *node = list.Search(9);
        cout << "查找元素为9的结点" << node->key << endl;
    
        //删除元素为10的结点
        list.Delete(10);
    
        cout << "打印链表:" << endl;
        list.Display();
    
        //删除元素为36的结点
        list.Delete(36);
        cout << "打印链表:" << endl;
        list.Display();
    
        system("pause");
        return 0;
    }

    测试结果:
    带哨兵的循环双向链表测试结果

  • 相关阅读:
    光猫改桥接,默认的超级管理员密码。
    Vmware+爱快软路由单机校园网多拨实现带宽叠加[测试用](非普遍性)
    魔百和破解教程
    用SSH工具XShell连接谷歌云 root用户或普通用户
    MySQL优化技巧【持续更新】
    Navicat常用快捷键
    IDEA实用插件Lombok
    Redis数据类型及命令
    Java代码优化总结(持续更新)
    Spring---AOP注解开发&jdbc模板&Spring事务管理
  • 原文地址:https://www.cnblogs.com/shine-yr/p/5214935.html
Copyright © 2020-2023  润新知