数据结构之 队列 链表表示与数组表示

来源:互联网 发布:淘宝客采集器 编辑:程序博客网 时间:2024/05/21 19:38

在开篇之前,补充一个之前遗留的问题。
在写单链表的时候,有一个成员函数是如下形式:
struct snode* getPtrByIndex(int pos);///通过传入的索引,返回节点的指针,如果不存在该索引,则返回空指针
同样的还有:
struct snode* getPtrByVal(const T val);///返回值满足传入的第一个节点的指针,不存在则返回空指针
但是这两个函数在编译的时候始终无法通过。而且,之前曾经提到过,模板类如果不调用函数的话,在编译的时候,即使有语法错误都不会提示,那么这个函数在没有调用的情况下反而在编译的时候出现了问题,是什么原因呢。
原因我还是不知道!!!!
但是现在知道怎么解决了!!!!
方法很简单,就是将前面的struct删除即可。

template<class T>snode<T>* mychain<T>::getPtrByVal(const T val){    struct snode<T>* p = m_pHead;    while (NULL != p)    {        if (val == p->val)        {            return p;        }        p = p->next;    }    return p;}

之所以我会在前面加上struct,其实是一个以前在C语言中遗留下来的习惯。在以前上课的时候强调了C语言中对于结构体指针,必须要带上struct,不然会出错。一直这样的习惯导致到了在C++中也在坚持,但是可能是C++放松了这样的要求,以及类class的出现和两者的相似性,取消了这样的限制。反而是在使用中,添加struct的方法可能会产生混淆。
在以后,尽量全部使用class,毕竟即使是一个坐标结构体,也可以携程class的形式,添加上成员变量和成员函数,管理起来方便,使用起来也很方便。

其实队列的部分我是真的不想写来的,因为从其定功能上来说,都只能算得上“穷人版的线性表”。队列在外表上和线性表具有太多的相似性了, 以至于几乎可以将线性表部分拿过来,再删除掉几个中间操作的函数,就成为了队列的数据结构。
但是最后决定还写一写的原因在于:
1、写队列,我决定链表采用双链表,以补充之前在写线性表的时候采用的是单链表;
2、数组表示的队列决定采用循环队列的方式,一方面能够很好的利用空间,另一方面也是补充之前的普通数组线性表。
其实循环队列的方式更加适用于队列的数据结构,因为随着队伍的进进出出,整个数据块在数组申请空间的位置逐渐后移,如果不采用循环队列的方式,那么空间的利用率就会大打折扣。

下面贴上队列的链表描述源程序代码:其中等于符号进行操作符重载的时候偷懒了,利用了拷贝构造函数

#ifndef _H_MYQUEUECHAIN_H#define _H_MYQUEUECHAIN_H#include <iostream>template<class T>class myQueueChain;template<class T>struct snode{    snode<T>* next;    T val;};template<class T>class myQueueChain{public:    myQueueChain(void);    myQueueChain(const myQueueChain<T>& mqc);    int push_back(T val);    void pop_front(void);    bool Empty(void);    T top(void);    int size(void);    myQueueChain<T>& operator=(const myQueueChain<T>& mqc);protected:private:    snode<T>* m_pHead;};template<class T>myQueueChain<T>::myQueueChain(void){    std::cout<<"默认构造函数,仅将头指针置为NULL"<<std::endl;    m_pHead = NULL;}template<class T>myQueueChain<T>::myQueueChain(const myQueueChain<T>& mqc){    std::cout<<"拷贝构造函数"<<std::endl;    snode<T>* p = mqc.m_pHead;    m_pHead = NULL;    snode<T>* p_last = m_pHead;    while (NULL != p)    {        if (NULL == m_pHead)        {            m_pHead = new struct snode<T>;            m_pHead->val = p->val;            p_last = m_pHead;            p_last->next = NULL;        }        else        {            p_last->next = new struct snode<T>;            p_last = p_last->next;            p_last->val = p->val;            p_last->next = NULL;        }        p = p->next;    }}//template<class T>//myQueueChain<T> myQueueChain<T>::operator=(const myQueueChain<T>& mqc)//{//  myQueueChain<T> m(mqc);//  return m;//  ////之前上面的这种方法是不正确的://  //// 1、没有考虑到原来的队列中存在元素的情况,//  //// 2、正常应该返回引用//  //// 3、即使考虑了存在元素的情况也应该考虑两边元素的多少////}template<class T>myQueueChain<T>& myQueueChain<T>::operator=(const myQueueChain<T>& mqc){    std::cout<<"重载操作符 = "<<std::endl;    if (this != &mqc)    {        struct snode<T>* p_leftPre = NULL;        struct snode<T>* p_left = m_pHead;        struct snode<T>* p_right = mqc.m_pHead;        while (NULL != p_left && NULL != p_right)        {            p_leftPre = p_left;            p_left->val = p_right->val;            p_left = p_left->next;            p_right = p_right->next;        }        if (NULL == p_left)////旧空间使用完了        {            while (NULL != p_right)            {                if (NULL == p_leftPre)///这种情况是左边上来就是个空的                {                    p_leftPre = new struct snode<T>;                    p_leftPre->val = p_right->val;                    p_leftPre->next = p_left;                }                else                {                    p_leftPre->next = new struct snode<T>;                    p_leftPre = p_leftPre->next;                    p_leftPre->val = p_right->val;                    p_leftPre->next = NULL;                }                p_right = p_right->next;            }        }        if (NULL == p_right)///// 旧空间还有剩余        {            p_leftPre->next = NULL;            while (NULL != p_left)            {                p_leftPre = p_left->next;                delete p_left;                p_left = p_leftPre;            }        }    }    return (*this);}template<class T>int myQueueChain<T>::push_back(T val){    struct snode<T>* p = m_pHead;    int cnt = 0;    if (NULL == p)    {        m_pHead = new struct snode<T>;        m_pHead->val = val;        m_pHead->next = NULL;        return cnt;    }    while (NULL != p)    {        cnt = cnt + 1;        if (NULL == p->next)        {            break;        }        p = p->next;    }    p->next = new struct snode<T>;    p = p->next;    p->next = NULL;    p->val = val;    return cnt;}template<class T>void myQueueChain<T>::pop_front(void){    if (NULL != m_pHead)    {        struct snode<T>* p = m_pHead;        m_pHead = m_pHead->next;        delete p;    }    else    {        throw("NoElementToPop");    }}template<class T>T myQueueChain<T>::top(void){    if (NULL == m_pHead)    {        throw("NoElement");    }    return m_pHead->val;}template<class T>bool myQueueChain<T>::Empty(void){    return (NULL == m_pHead);}template<class T>int myQueueChain<T>::size(void){    struct snode<T>* p = m_pHead;    int cnt = 0;    while (NULL != p)    {        cnt = cnt + 1;        p = p->next;    }    return cnt;}#endif

下面是链表描述的队列数据结构的测试程序:

#include "myQueueChain.cpp"#include <iostream>void main(void){    myQueueChain<int> mqc1;    std::cout<<"**********************************"<<std::endl;    for (int ii = 0; ii < 6; ii++)    {        mqc1.push_back(ii);    }    myQueueChain<int> mqc2(mqc1);    std::cout<<"**********************************"<<std::endl;    std::cout<<mqc1.size()<<std::endl;    while (!mqc1.Empty())    {        std::cout<<mqc1.top()<<"\t";        mqc1.pop_front();    }    std::cout<<std::endl;    myQueueChain<int> mqc3 = mqc2;    std::cout<<"*上面的并没有进入到默认构造然后重载操作符,而是直接进入拷贝构造函数"<<std::endl;    myQueueChain<int> mqc4;    mqc4 = mqc2;    std::cout<<"**********************************"<<std::endl;    while (!mqc2.Empty())    {        std::cout<<mqc2.top()<<"\t";        mqc2.pop_front();    }    std::cout<<std::endl<<mqc2.size()<<std::endl;    while (!mqc3.Empty())    {        std::cout<<mqc3.top()<<"\t";        mqc3.pop_front();    }    std::cout<<std::endl<<mqc3.size()<<std::endl;    std::system("pause");}

下面是链表表示的队列的代码,暂时还没有测试所有的成员函数。

#ifndef _H_MYQUEUEARRAY_H#define _H_MYQUEUEARRAY_Htemplate<class T>class myQueueArray;template<class T>class myQueueArray{public:    myQueueArray(int len = 64);    myQueueArray(const myQueueArray<T>& mqa);    myQueueArray(T arr[], int len);    ~myQueueArray();    T pop_front(void);    void push_back(T val);    int size(void);    bool full(void);    bool empty(void);    T top();    myQueueArray<T>& operator=(myQueueArray<T>& mqa);private:    T* m_ptr;    int m_front;    int m_back;    int m_len;};template<class T>myQueueArray<T>::myQueueArray(int len = 64){    m_front = 0;    m_back = 0;    if (len <= 0)    {        len = 16;    }    m_len = len;    m_ptr = new T[m_len];}template<class T>myQueueArray<T>::myQueueArray(const myQueueArray<T>& mqa){    m_front = mqa.m_front;    m_back = mqa.m_back;    m_len = mqa.m_len;    m_ptr = new T[m_len];    memcpy(m_ptr, mqa.m_ptr, sizeof(T) * m_len);}template<class T>myQueueArray<T>::myQueueArray(T arr[], int len){    m_front = 0;    m_back = len;    m_len = len * 1.2 + 1;    m_ptr = new T[m_len];    memcpy(m_ptr, arr, sizeof(T) * len);}template<class T>myQueueArray<T>::~myQueueArray(){    if (NULL != m_ptr)    {        delete[] m_ptr;    }}template<class T>T myQueueArray<T>::pop_front(void){    if(m_back == m_front)    {        throw("NoElmentToPop");    }    else    {        T val = m_ptr[m_front];        m_front = (m_front + 1)%m_len;        return(val);    }}template<class T>void myQueueArray<T>::push_back(T val){    if (0 == (m_back - m_front + 1 + m_len) % m_len)////将要满了,扩容    {        T* p = new T[m_len * 1.2 + 1];        if (m_back >= m_front)/////正常模式,正常扩容,仅修改m_len即可        {            memcpy(p, m_ptr, sizeof(T) * m_len);        }        else        {            memcpy(p, &(m_ptr[m_front]), sizeof(T) * (m_len - m_front));            memcpy(&(p[m_len - m_front]), m_ptr, m_back);            m_front = 0;            m_back = (m_back - m_front) % m_len;        }        delete[] m_ptr;        m_ptr = p;        m_len = m_len * 1.2 + 1;    }    m_ptr[m_back] = val;    m_back = m_back + 1;}template<class T>int myQueueArray<T>::size(void){    return((m_back - m_front + 1 + m_len) % m_len);}template<class T>bool myQueueArray<T>::full(void){    return(0 == (m_back - m_front + 1 + m_len) % m_len);}template<class T>bool myQueueArray<T>::empty(void){    return(m_back == m_front);}template<class T>T myQueueArray<T>::top(){    if (m_front != m_back)    {        return(m_ptr[m_front]);    }    else    {        throw("NoElement");    }}template<class T>myQueueArray<T>& myQueueArray<T>::operator=(myQueueArray<T>& mqa){    if (this != &mqa)    {        m_len = mqa.m_len;        m_front = mqa.m_front;        m_back = mqa.m_back;        delete[] m_ptr;        m_ptr = new T[m_len];        memcpy(m_ptr, mqa.m_ptr, sizeof(T) * m_len);    }    return(*this);}#endif

下面是测试数组表示的队列的代码

    myQueueArray<int> mqa;    int arr[9];    for (int ii = 0; ii < 9; ii++)    {        mqa.push_back(ii);        arr[ii] = ii;    }    myQueueArray<int> mqa1;    mqa1 = mqa;    myQueueArray<int> mqa2(mqa);    while (!mqa2.empty())    {        std::cout<<mqa2.pop_front()<<"\t";    }    std::cout<<std::endl;    myQueueArray<int> mqa3(arr, 9);    while (!mqa3.empty())    {        std::cout<<mqa3.pop_front()<<"\t";    }    std::cout<<std::endl;

昨天还看到一个有意思的事情。在两个模板类几个构造函数和重载操作符的函数中,都添加了输出语句,就是为了验证这个现象。

myQueueChain<int> mqc3 = mqc2;

上面这个句子,正常的期待是首先进入到默认的构造函数中,然后再执行操作符重载函数中的内容。但是实际上的情况却是两个都没有执行,而是直接进入到了拷贝构造函数中。
那么如果没有拷贝构造函数的情况下面又该是什么样呢?
应该就是期待的先进入默认构造函数,然后执行操作符重载函数。

原创粉丝点击