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

在开篇之前,补充一个之前遗留的问题。
在写单链表的时候,有一个成员函数是如下形式:
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_H

template<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;

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值