数据结构之 队列 链表表示与数组表示
来源:互联网 发布:淘宝客采集器 编辑:程序博客网 时间: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;
上面这个句子,正常的期待是首先进入到默认的构造函数中,然后再执行操作符重载函数中的内容。但是实际上的情况却是两个都没有执行,而是直接进入到了拷贝构造函数中。
那么如果没有拷贝构造函数的情况下面又该是什么样呢?
应该就是期待的先进入默认构造函数,然后执行操作符重载函数。
- 数据结构之 队列 链表表示与数组表示
- 数据结构之队列顺序表示与实现1
- 数据结构之队列顺序表示与实现2
- 队列的链表表示
- 一步一步学数据结构之1--1(队列--单链表表实现--不含队头尾指针)
- 数据结构之图的十字链表表示法
- 数据结构之 栈stack 模板类(链表表示)
- 数据结构之队列(数组队列)
- 二叉树数组表示法,链表表示法,中序遍历
- 数据结构(队列--单链表表实现--不含队头尾指针)
- 队列(queue)的链表与数组表示
- 循环队列的链表表示
- 双端队列(链表表示)
- 数据结构之数组实现队列
- 【数据结构】双向链表表示和实现
- 数据结构---C语言之队列的链式表示和实现(链队列)
- 数据结构之图的数组表示法
- 数据结构,队列,顺序表示
- List的三种遍历
- javascript基本语句
- CONFIG_BLK_DEV_INITRD
- 欢迎使用CSDN-markdown编辑器
- python中查看变量内存地址的方法
- 数据结构之 队列 链表表示与数组表示
- 交换机简介
- JavaScript基础知识——作用域
- Android SERVICE后台服务进程
- JZOJ5462. 【NOIP2017提高A组冲刺11.8】好文章
- 面向对象程序设计的结构
- jsp运行机制原理
- c语言实现在文件中记录20条最新的log记录
- 偷菜时间表