- 队列和栈是描述数据存取方式的概念,队列是先进先出,而堆栈是后进先出;队列和栈都可以使用数组或者链表实现。
数据存储结构:它是计算机的一个概念,简单讲,就是描述数据在计算机中存储方式的学科;常用的数据存储 方式就两种:顺序存储,非顺序存储!顺序存储就是把数据存储在一块连续的存储介质(比如硬盘或内存)上----举个例子:从内存中拿出第100个字节到 1000个字节间的连续位置,存储数据;数组就是典型的顺序存储!非顺序存储就是各个数据不一定存在一个连续的位置上,只要每个数据知道它前面的数据和后 面的数据,就能把所有数据连续起来啦;链表就是典型的非顺序存储啦!
至此,基本就应该明白它们之间的区别了吧!
队列、栈是线性数据结构的典型代表,而数组、链表是常用的两种数据存储结构;队列和栈均可以用数组或链表的存储方式实现它的功能!
数 组属于顺序存储中,由于每个元素的存储位置都可以通过简单计算得到,所以访问元素的时间都相同(直接访问数组下标);链表属于数据的链接存储,由于每个元 素的存储位置是保存在它的前驱或后继结点中的,所以只有当访问到其前驱结点或后继结点后才能够按指针访问到自己,访问任一元素的时间与该元素结点在链接存 储中的位置有关。
链表和数组是常用的两种数据存储结构,都能用来保存特定类型的数据。两者存在着一些差异:
1.占用的内存空间
链表存放的内存空间可以是连续的,也可以是不连续的,数组则是连续的一段内存空间。一般情况下存放相同多的数据数组占用较小的内存,而链表还需要存放其前驱和后继的空间。
2.长度的可变性
链表的长度是按实际需要可以伸缩的,而数组的长度是在定义时要给定的,如果存放的数据个数超过了数组的初始大小,则会出现溢出现象。
3.对数据的访问
链表方便数据的移动而访问数据比较麻烦;数组访问数据很快捷而移动数据比较麻烦。
链表和数组的差异决定了它们的不同使用场景,如果需要很多对数据的访问,则适合使用数组;如果需要对数据进行很多移位操作,则适合使用链表。
链表实现队列的程序示例:
这是最基础的链表队列,只包含增删操作,可以自己根据需要灵活使用,这里的程序只是一个引子。
#include<iostream>
using namespace std;
template<class T>
struct Node {
T data;
Node(T data, Node* next) :data(data), next(next) { }
Node* next;
};
template<class T>
class Link {
Node<T>* front, * rear; //链表头指针,链表尾指针
int size = 0; //链表结点容量
public:
Link() {}
bool isEmpty(); //判断链表是否为空
bool push(const T& item); //插入元素
T& pop(); //弹出元素并删除
~Link(); //析构函数,用来删除存入到堆区结点中的数据
};
template<class T>
bool Link<T>::isEmpty() {
return size == 0;
}
template<class T>
bool Link<T>::push(const T& item) {
if (isEmpty()) {
front = rear = new Node<T>(item, NULL);
}
else {
rear->next = new Node<T>(item, NULL); //这出错了,不是rear,而是rear->next,道理自己想想就懂了
rear = rear->next;
}
size++;
return true;
}
template<class T>
T& Link<T>::pop() {
if (isEmpty()) {
cout << "队列为空,取出元素失败" << endl;
exit(-1);
}
else {
Node<T>* temp;
temp = front;
front = front->next;
size--;
return temp->data;
delete temp;
}
}
template<class T>
Link<T>::~Link(){
Node <T>*temp=front;
while(temp!=NULL){
delete temp;
temp=temp->next;
}
}
int main() {
Link<int> l;
for(int i=0;i<3;i++)
l.push(i); //插入元素
for(int i=0;i<3;i++)
cout<<l.pop(); //弹出元素
}
示例二:复杂型的模板结点和链表
//Node.h的内容
#ifndef _NODE_H
#define _NODE_H
template<class T>
class Node {
Node<T>* next; //指向后继节点的指针
public:
T data; //存放数据的数据域
Node(const T& data, Node<T>* next = NULL); //构造函数,创建一个的节点以保存数据data
void insertAfter(Node<T>* p); //在本结点之后插入一个新的同类节点
Node<T>* deleteAfter(); //删除本结点之后的后继结点,并返回其地址。
Node<T>* nextNode(); //获取后继节点的地址
const Node<T>* nextNode()const; //获取后继节点的地址
};
//具体实现过程
template<class T>
Node<T>::Node(const T& data, Node<T>* next) :data(data), next(next) {}
template<class T>
Node<T>* Node<T>::nextNode() {
return next;
}
template<class T>
const Node<T>* Node<T>::nextNode() const {
return next;
}
template<class T>
void Node<T>::insertAfter(Node<T>* p) {
p->next = next;
next = p;
}
template<class T>
Node<T>* Node<T>::deleteAfter() {
Node<T>* temptr = next;
if (next == 0) return 0;
next = temptr->next;
return temptr;
}
#endif
//LinkedList.h的内容
//下面的代码就是使用上面的结点类来建立一个链表并且增加了很多对链表的操作,所以看着很大,但是光创建一个普通的链表其实不会这么复杂,这个功能比较全
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
//#include <cstdio>
#include <cassert>
#include "Node.h"
template<class T>
class LinkedList{
private:
Node<T>*front,*rear; //表头指针和表位指针,表的首尾指针,而不是结点的首位指针
Node<T>*prevPtr,*currPtr; //记录表当前遍历位置的指针,由插入和删除操作更新。
int size;//链表中的结点个数
int position;//当前元素在表中的位置序号,由函数reset管理
//生成新结点,并且用来存储数据item,指针域为ptrNext
Node<T>*newNode(const T&item,Node<T>*ptrNext=NULL);
//释放结点
void freeNode(Node<T>*p);
//将链表L复制到当前表 ,不是复制构造函数,等会将要被复制构造函数和operator=的重载函数调用。
void copy(const LinkedList<T>& L);
public:
LinkedList(); //链表的构造函数
LinkedList(const LinkedList<T>& L); //链表的复制构造函数
~LinkedList();//链表的析构函数
LinkedList<T>& operator=(const LinkedList<T>&L); //operator=的重载函数
int getSize()const; //返回链表当前元素个数
bool isEmpty() const; //判断链表是否为空
void reset(int pos=0); //初始化游标位置,
void next();//游标移动到下一个结点
bool endOfList() const;//判断是否到文件尾
int currentPosition(void) const;//返回游标当前位置
void insertFront(const T &item);//在表头插入结点
void insertRear(const T &item); //在表尾插入结点
void insertAt(const T &item); //在当前结点之前插入结点
void insertAfter(const T &item); //在当前结点之后插入结点
T deleteFront(); //删除头结点
void deleteCurrent(); //删除当前结点
T& data(); //返回当前结点的数据内容的引用
const T& data() const; //返回当前结点的数据内容的常引用
void clear(); //清空链表,释放所有的结点占用的内存空间,被析构函数和operator=调用
};
template<class T>
void LinkedList<T>::freeNode(Node<T> *p){
delete p;
}
template<class T>
void LinkedList<T>::copy(const LinkedList<T> &L){
front = L.front;
rear = L.front;
currPtr = L.currPtr;
prevPtr = L.prevPtr;
size = L.size;
position = L.position;
}
template<class T>
LinkedList<T>::LinkedList():front(NULL), rear(NULL), prevPtr(NULL), currPtr(NULL), size(0), position(0) {}
template<class T>
LinkedList<T>::LinkedList(const LinkedList<T> &L){
copy(L);
}
template<class T>
LinkedList<T>::~LinkedList(){
clear();
delete prevPtr;
delete currPtr;
}
template<class T>
LinkedList<T> &LinkedList<T>::operator=(const LinkedList<T> &L){
copy(L);
}
template<class T>
int LinkedList<T>::getSize() const{
return size;
}
template<class T>
bool LinkedList<T>::isEmpty() const{
return size == 0;
}
template<class T>
void LinkedList<T>::reset(int pos){
if (pos >= 0 && pos <= size){
position = 0;
currPtr = front;
prevPtr = NULL;
while (pos--) next();
}
else {
position = pos;
currPtr = prevPtr = NULL;
}
}
template<class T>
void LinkedList<T>::next(){
++position;
prevPtr = currPtr;
if (currPtr != NULL) currPtr = currPtr->nextNode();
}
template<class T>
bool LinkedList<T>::endOfList() const{
return position == size;
/*错误的如下
return position == size - 1;
*/
}
template<class T>
int LinkedList<T>::currentPosition() const{
return position;
}
//创建一个新的节点以保存数据item,并返回地址
template<class T>
Node<T> *LinkedList<T>::newNode(const T &item, Node<T> *ptrNext){
Node<T> *p = new Node<T>(item, ptrNext); //注意分开的new Node,函数名是合起来的newNode,两个含义不同
assert(p != NULL);
return p;
}
template<class T>
void LinkedList<T>::insertFront(const T &item){
front = newNode(item, front);
if(isEmpty())
rear = front;
++size;
reset(++position);
}
template<class T>
void LinkedList<T>::insertRear(const T &item){
Node<T> *p = newNode(item);
if (isEmpty()) {
front = rear = p;
} else {
rear->insertAfter(p);
rear = p;
}
++size;
reset(position);
}
template<class T>
void LinkedList<T>::insertAt(const T &item){
if(currPtr != NULL){
Node<T> *p = newNode(item, currPtr);
if (prevPtr != NULL) prevPtr->insertAfter(p);
else front = rear = p;
++size;
reset(++position);
}
}
template<class T>
void LinkedList<T>::insertAfter(const T &item){
if(currPtr != NULL){
Node<T> *p = newNode(item, currPtr->next);
currPtr->insertAfter(p);
++size;
reset(position);
}
}
template<class T>
T LinkedList<T>::deleteFront(){
assert(!isEmpty());
Node<T> *p = front;
front = front->nextNode();
if (--size == 0) front = rear = NULL;
reset(--position);
T item = p->data;
freeNode(p);
return item;
}
template<class T>
void LinkedList<T>::deleteCurrent(){
assert(!isEmpty());
Node<T> *p = currPtr;
if(currPtr){
if (currPtr == front) front = front->nextNode();
else if (currPtr == rear) rear = prevPtr;
else if (prevPtr != NULL) prevPtr->deleteAfter();
freeNode(currPtr);
--size;
reset(position);
}
}
template<class T>
T &LinkedList<T>::data(){
return currPtr->data;
}
template<class T>
const T& LinkedList<T>::data() const{
return currPtr->data;
}
template<class T>
void LinkedList<T>::clear(){
while (!isEmpty()) //解决了浅复制的重复析构问题。
deleteFront();
}
#endif //LINKEDLIST_H