关闭

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

标签: 算法导论
563人阅读 评论(0) 收藏 举报
分类:

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

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] << "\t";
    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] << "\t";
        for (int j = 0; j < tail; j++)
            cout << data[j] << "\t";
    }
    else{
        for (int i = head; i < tail; i++)
            cout << data[i] << "\t";
    }
    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 << "\t";
        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 << "\t";
        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;
}

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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:638071次
    • 积分:9632
    • 等级:
    • 排名:第1836名
    • 原创:389篇
    • 转载:49篇
    • 译文:0篇
    • 评论:167条
    博客专栏
    最新评论