数据结构篇 - 循环队列

开源仓库: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
相关推荐
NineData2 天前
数据库管理工具NineData,一年进化成为数万+开发者的首选数据库工具?
运维·数据结构·数据库
琢磨先生David10 天前
Day1:基础入门·两数之和(LeetCode 1)
数据结构·算法·leetcode
qq_4542450310 天前
基于组件与行为的树状节点系统
数据结构·c#
超级大福宝10 天前
N皇后问题:经典回溯算法的一些分析
数据结构·c++·算法·leetcode
岛雨QA10 天前
常用十种算法「Java数据结构与算法学习笔记13」
数据结构·算法
weiabc10 天前
printf(“%lf“, ys) 和 cout << ys 输出的浮点数格式存在细微差异
数据结构·c++·算法
wefg110 天前
【算法】单调栈和单调队列
数据结构·算法
岛雨QA10 天前
图「Java数据结构与算法学习笔记12」
数据结构·算法
czxyvX10 天前
020-C++之unordered容器
数据结构·c++
岛雨QA10 天前
多路查找树「Java数据结构与算法学习笔记11」
数据结构·算法