C++ 队列(queue)实现(附源码)

队列简介

周知众所,队列(queue)是计算机技术中常见的数据结构,先入先出,先进去的就先出来。就像这样:

为了更好地了解队列的结构和原理,今天,我们就用单链表来简单实现队列。

有的小伙伴可能要问了:搞那么麻烦干什么?用数组,再维护一个队头下标、一个队尾下标不就好了吗?

这里,就不得不提到队列的优点——它数据存取速度快。数组插入、删除操作时间复杂度是O(N),而队列的是O(1)。而在特定的问题中,栈完全够用,但是数组的功能有冗余,反而拖慢了速度。同时,数组有固定的长度,而单链表则没有这种限制。

队列实现

环境:VS2022/std:C++20

单链表

首先,单链表会写吧?但是还有一个问题:要是直接设定方法的参数与返回值的类型的话,我们的node就只能适应一种类型。周知众所,结构体不能重载,同时编写int_node、float_node、double_node、char_node......也根本不切实际。因此,C++提供了泛型编程——模板。为了让node能够适应各种数据类型,我们可以使用类模板。就像这样:

template <typename T>
struct node {
    ......
};

注意:此处typename就是typename(也可以是class),不是用来填的!

那么这时,我们就可以在node中把T当成正常的数据类型来使用,使得我们能够用T来定义要存储的数据。这时,node要这么用:

node<数据类型> 节点名;

所以最终,node长这样:

template <typename T>
struct node {
public:
	T data;        //数据
	item<T>* last; //指向上一个node的指针
};

方法实现

今天,我们主要实现的是以下几个方法:

size();//查看队列中元素个数
empty();//检查队列是否为空
top();//查看队首元素
push();//向队首增加元素
pop();//从队尾删除元素

当然,还包括构造函数与析构函数。

size()

先从最简单的size()开始吧。size()方法要求我们在类内维护一个变量,我们命名为s。同时,为了计数,在构造函数中设置其初始值为零,再在push()与pop()函数中改变s的值。对了,queue类也要用上类模板。

 好了,现在我们的queue类成了这样:

template <typename T>
class queue {
public:
    queue() {
        s = 0;
    }
    ~queue() {
        
    }
    T push(T type) {
        s++;
    }
    T pop() {
        s--;
    }
    T top() {
        
    }
    int size() {
        return s;
    }
    bool empty() {
        
    }
private:
    int s;
};

empty()

接下来写empty()。 超级简单,只要判断s的值是不是等于0就可以了。

bool empty() {
    return s == 0;
}

top()

再写top()。top()方法要求我们维护一个指向队首节点的指针,我们命名为f。所以,应该在构造函数中赋初值。返回值不应该是node对象,而是node中的data。我们还可以用inline来修饰上面三个极简单的函数,减少函数调用开销,使得stack类更加高效。我们现在的stack类:

template <typename T>
class queue {
public:
    queue() {
        s = 0;
        t = NULL;
    }
    ~queue() {
 
    }
    inline int size() {
        return s;
    }
    inline bool empty() {
        return s == 0;
    }
    inline T top() {
        return t->type;
    }
    T push(T& item) {
        s++;
    }
    T pop() {
        s--;
    }
private:
    int s;
    item<T>* t;
};

push()与pop()

实现push()与pop() ,稍微难一点。

新创建一个node对象,设置好值,让原来的尾节点的last指向新node,就能完成push()。同时,为了操控新对象,再创建一个指针,命名为n。

T push(T type) {
	item<T> *n = new item<T>;
	n->data = type;
	n->last = NULL;
	if (empty()) {
		f = r = n;
		s++;
		return type;
	}
	r->last = n;
	r = n;
	s++;
	return type;
}

让f等于队首节点的last,再删除队首节点,就能完成pop()。同时,为了有返回值,还得先保存栈顶节点的data,为了操控不再被f指向的栈顶节点,新创建一个指针,命名为d。特殊情况:pop()至队空,此时f = NULL,但r !=,所以把r也赋值为NULL。

T pop() {
	item<T> *d = f;
	f = d->last;
	T type = d->data;
	delete d;
	d = NULL;
	if (f == NULL)
		r = NULL;
	s--;
	return type;
}


析构函数

还缺一个析构函数,就是删除所有节点。

~stack() {
    while (!empty())
        pop();
}

构造函数

其实刚刚已经编好了,整理出来:

queue() {
	s = 0;
	f = r = NULL;
}

 好啦!放在一个头文件queue.h里,放进std,正式完工。

成品

我的编译器没有NULL宏,就自己定义了一个。

#pragma once

#define NULL 0
namespace std {
	template <typename T>
	struct item {
	public:
		T data;
		item<T> *last;
	};
	template <typename T>
	class queue {
	public:
		queue() {
			s = 0;
			f = r = NULL;
		}
		~queue() {
			while (!empty())
				pop();
		}
		T push(T type) {
			item<T> *n = new item<T>;
			n->data = type;
			n->last = NULL;
			if (empty()) {
				f = r = n;
				s++;
				return type;
			}
			r->last = n;
			r = n;
			s++;
			return type;
		}
		T pop() {
			item<T> *d = f;
			f = d->last;
			T type = d->data;
			delete d;
			d = NULL;
			if (f == NULL)
				r = NULL;
			s--;
			return type;
		}
		inline int size() {
			return s;
		}
		inline bool empty() {
			return s == 0;
		}
		inline T top() {
			return f->data;
		}
	private:
		int s;
		item<T> *f, *r;
	};
}

编译通过。

测试

测试代码:

#include "cstdio"
#include "queue.h"
int main() {
	std::queue<int> myqueue;
	myqueue.push(1);
	myqueue.push(2);
	myqueue.push(3);
	myqueue.push(4);
	if (myqueue.empty())
		printf("empty\n");
	else
		printf("not empty\n");
	printf("%d\n", myqueue.pop());
	printf("%d\n", myqueue.top());
	myqueue.pop();
	myqueue.pop();
	printf("%d\n", myqueue.size());
}

测试结果:

not empty
1
2
1

完全符合预期!


link期间,遇到fatal error LNK1000: Internal error during Incr的问题。

下补丁不行,网上找的方法:

项目->属性->链接器->常规   底下的“启用增量链接”,将“是(/INCREMENTAL)”改成“否(/INCREMENTAL:NO)”。
不过这又引入了另外一个警告:FormatCom.obj : warning LNK4075: 忽略“/EDITANDCONTINUE”(由于“/INCREMENTAL:NO”规范)。

                                                      (《搭建ACE-5.7.4+VS2008开发环境》——优快云博客)


有熟悉我的粉丝可能发现了,我从栈那篇里借鉴了亿点。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值