数据结构篇 - 循环队列

开源仓库:unittest

基本概念

循环队列是对普通顺序队列的优化版,核心是把存储数据的数组「首尾相连」,形成一个环形的缓冲空间(就像把奶茶店的直线排队区弯成一个圆圈),解决普通顺序队列的「假溢出」问题。

上图演示中,共有8个人,当排到位置7时,发现位置0的人已经走了,空了一段时间了。那么这时候新来的8号顾客就可以站到位置0处。tail = (tail + 1) % 8 = 0; head = (head + 1) % 8 = 1。tail显示了pop(出队)后的变化,head显示的是push(入队)后的变化。

一个实例

  • queue_circle.h

头文件定义的queue_frame是一个稍复杂的数据结构体,这样更符合实际项目开发需求。如果只是单纯学习循环队列原理,可以将 其理解为一个数据体,不用过多关注。重点是queue_frame_circlebuf结构体的定义。

c 复制代码
#ifndef __QUEUE_CIRCLE_H
#define __QUEUE_CIRCLE_H

#ifdef __cplusplus
 extern "C" {
#endif

struct queue_frame {
	int	id;
	int	len;
	int	flags;
	int	__reserved0;
	int	__reserved1;
	int	data[64];
};
// 通用环形缓冲区(队列)结构体及操作函数
#define CIRCLE_BUF_SIZE 32
struct queue_frame_circlebuf {
	struct queue_frame buf[CIRCLE_BUF_SIZE];
	int head;
	int tail;
	//int ready;
	int size;
};

int queue_cbuf_is_empty(const struct queue_frame_circlebuf *cbuf);
int queue_cbuf_is_full(const struct queue_frame_circlebuf *cbuf);
int queue_cbuf_push(struct queue_frame_circlebuf *cbuf,
                                const struct queue_frame *frame);
int queue_cbuf_pop(struct queue_frame_circlebuf *cbuf,
                                struct queue_frame *frame);

#ifdef __cplusplus
}
#endif  
#endif 	/* __QUEUE_CIRCLE_H */
  • queue_circle.c

循环队列逻辑实现代码重点实现了队空、队满、入队、出队。需要注意的是这里和顺序队列的逻辑区别。循环队列通过头尾指针对最大元素数量(CIRCLE_BUF_SIZE)取余操作,实现循环。

c 复制代码
#include "queue_circle.h"
#include <string.h>
/**
 * ************************************************ 
 * @brief   头地址等于尾地址,则队列为空
 * @param   cbuf            Param...
 * @return int
 * ************************************************ 
 */
int queue_cbuf_is_empty(const struct queue_frame_circlebuf *cbuf) {
	return cbuf->head == cbuf->tail;
}
/**
 * ************************************************ 
 * @brief   循环队列中剩余1个空位置时,则队列为满
 * @param   cbuf            Param...
 * @return int
 * ************************************************ 
 */
int queue_cbuf_is_full(const struct queue_frame_circlebuf *cbuf) {
	return ((cbuf->head + 1) % CIRCLE_BUF_SIZE) == cbuf->tail;
}
/**
 * ************************************************ 
 * @brief   将数据*frame插入队列
 * @param   cbuf            Param...
 * @param   frame           Param...
 * @return int
 * ************************************************ 
 */
int queue_cbuf_push(struct queue_frame_circlebuf *cbuf,
                                const struct queue_frame *frame) {
	if (queue_cbuf_is_full(cbuf)) {
		return -1; // 满
	}
	memcpy(&cbuf->buf[cbuf->head], frame, sizeof(struct queue_frame));
	cbuf->head = (cbuf->head + 1) % CIRCLE_BUF_SIZE;
	//cbuf->ready = 1;
	cbuf->size++;
	return 0;
}
/**
 * ************************************************ 
 * @brief   将数据*frame弹出队列
 * @param   cbuf            Param...
 * @param   frame           Param...
 * @return int
 * ************************************************ 
 */
int queue_cbuf_pop(struct queue_frame_circlebuf *cbuf,
                                struct queue_frame *frame) {
	if (queue_cbuf_is_empty(cbuf)) {
		//cbuf->ready = 0;
		return -1; // 空
	}
	memcpy(frame, &cbuf->buf[cbuf->tail], sizeof(struct queue_frame));
	cbuf->tail = (cbuf->tail + 1) % CIRCLE_BUF_SIZE;
	cbuf->size--;
	//if (cbuf->tail == cbuf->head) {
		//cbuf->ready = 0;
	//}
	return 0;
}

实例测试

基于GoogleTest单元测试框架对循环队列实现逻辑进行测试分析,能够清晰的理解代码运行逻辑。

c 复制代码
#define GOOGLE_GLOG_DLL_DECL           
#define GLOG_NO_ABBREVIATED_SEVERITIES  
#define GLOG_EXPORT  
#define GLOG_NO_EXPORT 
#include <gtest/gtest.h>             
#include <gtest/stub.h>              
#include <glog/logging.h>            
#include <iostream>
#include "queue_circle.h"            
using namespace std;       
using namespace std::chrono_literals;                           
/*-----------------BEGIN----------------*/ 
/**
 * ************************************************ 
 * @brief   循环队列测试函数
 * @return int
 * ************************************************ 
 */
int queue_circle_main(void)
{
    queue_frame_circlebuf q[8] = {0};
    queue_frame frame[4] = {
        {0x01, 0x0A, 0x55, 0x00, 0x00, 0x10, 0x11, 0x12}, 
        {0x02, 0x0B, 0x55, 0x00, 0x00, 0x13, 0x14, 0x15}, 
        {0x03, 0x0C, 0x55, 0x00, 0x00, 0x16, 0x17, 0x18},
        {0x00}
    };
    queue_cbuf_push(&q[0], &frame[0]);
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[0].buf[0].data[0];
    LOG(INFO) << "the queue size: "       << "0x" << std::hex << q[0].size;
    queue_cbuf_push(&q[0], &frame[1]);
    queue_cbuf_push(&q[0], &frame[2]);
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[0].buf[0].data[0];
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[0].buf[1].data[0];
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[0].buf[2].data[1];
    LOG(INFO) << "the queue size: "       << "0x" << std::hex << q[0].size;
    LOG(INFO) << "the frame data value: " << "0x" << std::hex << frame[3].data[0];
    queue_cbuf_pop(&q[0],  &frame[3]);
    LOG(INFO) << "the frame data value: " << "0x" << std::hex << frame[3].data[0];
    LOG(INFO) << "the queue size: "       << "0x" << std::hex << q[0].size;
    queue_cbuf_push(&q[1], &frame[0]);
    queue_cbuf_push(&q[2], &frame[0]);
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[1].buf[0].data[0];
    LOG(INFO) << "the queue data value: " << "0x" << std::hex << q[2].buf[0].data[0];
    return 0;
}
TEST(TestSuite0, TEST3){
    LOG(INFO) << "\r\nUT >>>  queue_circle_main";
    queue_circle_main();
}
/*------------------END-----------------*/     
int gtest_main(int argc, char *argv[]){       
    google::InitGoogleLogging("UNITTEST");      
    google::SetStderrLogging(google::GLOG_INFO);   
    google::SetLogDestination(google::GLOG_INFO, ".//log//INFO_"); 
    google::EnableLogCleaner(2); 
    //init global variable                
                                               
    testing::InitGoogleTest(&argc, argv); 
    return RUN_ALL_TESTS();                
    google::ShutdownGoogleLogging();       
}  

测试报告

测试报告基于GoogleLog生成。通过查看测试报告和上面的测试case,相信读者一定你能对循环队列的代码实现有深入的理解。

powershell 复制代码
Log file created at: 2026/02/12 18:04:27
Running on machine: DESKTOP-J6Q1P85
Running duration (h:mm:ss): 0:00:00
Log line format: [IWEF]yyyymmdd hh:mm:ss.uuuuuu threadid file:line] msg
I20260212 18:04:27.791738 13304 test_case.cpp:102] 

UT >>>  queue_circle_main
I20260214 10:39:15.203192 15320 test_case.cpp:88] the queue data value: 0x10
I20260214 10:39:15.203192 15320 test_case.cpp:89] the queue size: 0x1
I20260214 10:39:15.204190 15320 test_case.cpp:92] the queue data value: 0x10
I20260214 10:39:15.204190 15320 test_case.cpp:93] the queue data value: 0x13
I20260214 10:39:15.204190 15320 test_case.cpp:94] the queue data value: 0x17
I20260214 10:39:15.204190 15320 test_case.cpp:95] the queue size: 0x3
I20260214 10:39:15.204190 15320 test_case.cpp:96] the frame data value: 0x0
I20260214 10:39:15.204190 15320 test_case.cpp:98] the frame data value: 0x10
I20260214 10:39:15.204190 15320 test_case.cpp:99] the queue size: 0x2
I20260214 10:39:15.204190 15320 test_case.cpp:102] the queue data value: 0x10
I20260214 10:39:15.204190 15320 test_case.cpp:103] the queue data value: 0x10
相关推荐
俩娃妈教编程2 小时前
洛谷选题:P1420 最长连号
数据结构·算法
二年级程序员2 小时前
单链表算法题思路详解(上)
c语言·数据结构·c++·算法
wangluoqi2 小时前
c++ 数据结构-树状数组、线段树 小总结
开发语言·数据结构·c++
化学在逃硬闯CS2 小时前
Leetcode111. 二叉树的最小深度
数据结构
重生之后端学习3 小时前
230. 二叉搜索树中第 K 小的元素
java·数据结构·算法·深度优先
星火开发设计4 小时前
类模板:实现通用数据结构的基础
java·开发语言·数据结构·c++·html·知识
EE工程师4 小时前
数据结构篇 - 顺序队列
数据结构·顺序队列
白中白121384 小时前
算法题-14
数据结构·算法·leetcode
plus4s5 小时前
2月13日(73-75题)
数据结构·c++·算法