numeric getrealsize(array<Numeric> arr)
{
Numeric i;
numeric result;
for i = 0 to GetArraySize(arr)-1
{
If(arr[i]<>0) result = i;
}
return result;
}有效长度定义类似小数的有效位数,按数组最后一个非0元素的位置作为数组的有效长度。
其实设计成为 arr[0]存放count更简单,还不需要遍历。然后设计一个enqueue和delete函数 维护count即可。
如果涉及到大量的删除和enqueue,可以考虑使用环形结构,扩大表头,存放表头大小,front和tear指针,不需要频繁进行内存拷贝。
可以给出实例🤝
// =============================================================================
// 基础队列模块(BQue)设计文档
// 版本:3.1(增强版)
// =============================================================================
// =============================================================================
// 第一部分:模块概述
// =============================================================================
// 基础队列模块提供灵活的队列操作功能,在标准FIFO基础上增加条件删除等操作。
// 每个子项包含多个字段,支持结构化数据存储和条件筛选。
// =============================================================================
// 第二部分:数据结构定义
// =============================================================================
// 【基础队列】一维数组模拟二维表格结构设计
// 二维结构定义:
// - 行:代表队列的“行维度”,分为【表头行】和【子项行】
// - 列:代表每行的“字段维度”,表头行/子项行均包含多个字段列
// 一维数组与二维行列的映射:数组索引 = 行偏移 + 列偏移(按“行优先”顺序存储)
// 【表头行(第0行)】
// - 表头行总列数 HeaderSize = max(2, FieldSize) (最小占2列,保证基础元数据存储)
// 表头行列定义:
// - 第0行第0列:存储队列当前有效子项个数(核心元数据)
// - 第0行第1列:存储单个子项行的字段列数(FieldSize)
// - 第0行第2~HeaderSize-1列:预留扩展列,暂未使用
// 【子项行(从第1行开始)】
// - 子项行起始行号:1(表头行的下一行)
// - 单个子项行占用列数:FieldSize
// - 队列子项访问规则(二维行列 → 一维数组索引):
// 第N个子项行的第1列 → 一维数组索引 = HeaderSize + (N - 1) * FieldSize + 0
// 第N个子项行的第2列 → 一维数组索引 = HeaderSize + (N - 1) * FieldSize + 1
// 第N个子项行的第M列 → 一维数组索引 = HeaderSize + (N - 1) * FieldSize + (M - 1)
// 说明:
// N ≥ 1(子项行号从1开始)
// M ≥ 1且M ≤ FieldSize(字段编号从1到FieldSize)
Array<Numeric> BQue_Queue_n_a;
// =============================================================================
// 第三部分:接口定义
// =============================================================================
// 3.1 初始化队列
// 功能:设置队列容量和字段数,并重置队列
// 输入:Queue_n_a - 队列数组,Capacity_n - 队列容量(最大可存储子项个数),FieldSize_n - 每个子项的字段数
BQue_Init(ArrayRef<Numeric> Queue_n_a, Numeric Capacity_n, Numeric FieldSize_n,Numeric Default_Value_n);
// 3.2 重置队列
// 功能:将表头[0][0]设为0,[0][1]保持不变,所有子项字段设置为Default_n
// 输入:Queue_n_a - 队列数组,Default_n - 默认填充值
BQue_Reset(ArrayRef<Numeric> Queue_n_a, Numeric Default_n);
// 3.3 入队
// 功能:将新子项添加到队尾
// 对于波峰波谷队列,不添加关键字段重复的子项(从后向前搜索,遇到小于KeyFld1_n时停止)
// 对于普通队列,只检查最后一项是否重复
// 如果队列已满,自动删除第一项
// 输入:Queue_n_a - 队列数组
// KeyFld1_n - 关键字段1(必选)
// KeyFld2_n - 关键字段2(FieldSize>1时使用,默认0)
// QueueType_n - 队列类型:0=普通队列(只查最后一项),1=波峰波谷队列(从后向前搜索,默认1)
// QV_Chk_En_n - 队列合法性检查开关:1=检查,0=不检查(默认0)
// 关键字段定义:
// - 当 FieldSize = 1 时,关键字段为 KeyFld1_n(对应第1列)
// - 当 FieldSize > 1 时,关键字段为 KeyFld1_n 和 KeyFld2_n(对应第1列和第2列)
// 其他字段默认填充0,可通过 BQue_SetItemField 后续设置
// 返回:1成功,-2关键字段重复,-3关键字段无效,负数其他错误码
BQue_Enqueue(ArrayRef<Numeric> Queue_n_a, Numeric KeyFld1_n, Numeric KeyFld2_n, Numeric QueueType_n, Numeric QV_Chk_En_n);
// 3.4 删除指定子项索引
// 功能:删除指定子项索引的子项,后续子项依次上移一行
// 如果 ItemIndex_n = -1,则删除最后一项
// 输入:Queue_n_a - 队列数组,ItemIndex_n - 子项索引(1 ~ 当前大小)或 -1(删除最后一项)
// 返回:1成功,-1子项索引超出有效范围,负数其他错误码
BQue_DeleteByIndex(ArrayRef<Numeric> Queue_n_a, Numeric ItemIndex_n, Numeric QV_Chk_En_n);
// 3.5 删除匹配关键字段的子项
// 功能:根据关键字段值查找匹配的子项并删除
// 支持两种队列类型的查找策略:
// - 普通队列(QueueType_n=0):只检查最后一项
// - 波峰波谷队列(QueueType_n=1):从后向前搜索,遇到小于KeyFld1_n时停止
// 如果找到匹配项,调用 BQue_DeleteByIndex 删除
// 输入:Queue_n_a - 队列数组
// KeyFld1_n - 关键字段1
// KeyFld2_n - 关键字段2(FieldSize>1时使用,默认0)
// QueueType_n - 队列类型:0=普通队列,1=波峰波谷队列(默认1)
// QV_Chk_En_n - 队列合法性检查开关:1=检查,0=不检查(默认0)
// 输出:返回值 - 1成功删除,0未找到匹配项,负数错误码
// 关键字段定义:
// - 当 FieldSize = 1 时,关键字段为 KeyFld1_n(对应第1列)
// - 当 FieldSize > 1 时,关键字段为 KeyFld1_n 和 KeyFld2_n(对应第1列和第2列)
BQue_DeleteByKey(ArrayRef<Numeric> Queue_n_a, Numeric KeyFld1_n, Numeric KeyFld2_n, Numeric QueueType_n, Numeric QV_Chk_En_n);
// 3.6 获取子项字段
// 功能:获取指定子项索引的指定字段值
// 如果 ItemIndex_n = -1,则获取最后一项(队尾)
// 输入:Queue_n_a - 队列数组,ItemIndex_n - 子项索引(1 ~ 当前大小)或 -1(最后一项)
// FieldIndex_n - 字段编号(1 ~ FieldSize)
// 输出:Fld_Value_n - 字段值
// 返回:1成功,-1子项索引或字段索引超出有效范围,负数其他错误码
BQue_GetItemField(ArrayRef<Numeric> Queue_n_a, Numeric ItemIndex_n, Numeric FieldIndex_n, NumericRef Fld_Value_n, Numeric QV_Chk_En_n);
// 3.7 设置子项字段
// 功能:设置指定子项索引的指定字段值
// 如果 ItemIndex_n = -1,则设置最后一项(队尾)
// 输入:Queue_n_a - 队列数组,ItemIndex_n - 子项索引(1 ~ 当前大小)或 -1(最后一项)
// FieldIndex_n - 字段编号(1 ~ FieldSize),Fld_Value_n - 字段值
// 返回:1成功,-1子项索引或字段索引超出有效范围,负数其他错误码
BQue_SetItemField(ArrayRef<Numeric> Queue_n_a, Numeric ItemIndex_n, Numeric FieldIndex_n, Numeric Fld_Value_n, Numeric QV_Chk_En_n);
// 3.8 判断队列是否已满
// 功能:判断队列是否已满(当前大小 == 容量)
// 返回:1已满,0未满,负数错误码
BQue_IsFull(ArrayRef<Numeric> Queue_n_a, Numeric QV_Chk_En_n);
// 3.9 根据关键字段获取子项索引
// 功能:根据关键字段值查找匹配的子项索引
// 支持两种队列类型的查找策略:
// - 普通队列(QueueType_n=0):只检查最后一项
// - 波峰波谷队列(QueueType_n=1):从后向前搜索,遇到小于KeyFld1_n时停止
// 输入:Queue_n_a - 队列数组
// KeyFld1_n - 关键字段1
// KeyFld2_n - 关键字段2(FieldSize>1时使用,默认0)
// QueueType_n - 队列类型:0=普通队列,1=波峰波谷队列(默认1)
// QV_Chk_En_n - 队列合法性检查开关:1=检查,0=不检查(默认0)
// 输出:返回值 - 找到的子项索引(1 ~ QueueSize_n),0表示未找到,负数表示错误码
// 关键字段定义:
// - 当 FieldSize = 1 时,关键字段为 KeyFld1_n(对应第1列)
// - 当 FieldSize > 1 时,关键字段为 KeyFld1_n 和 KeyFld2_n(对应第1列和第2列)
BQue_GetItemIdx_ByKey(ArrayRef<Numeric> Queue_n_a, Numeric KeyFld1_n, Numeric KeyFld2_n, Numeric QueueType_n, Numeric QV_Chk_En_n);
// 3.10 队列有效性检查
// 功能:检查队列的数据结构是否完整有效
// 包括:数组已初始化、字段大小有效、数组长度满足最小要求、数组结构完整
// 输入:Queue_n_a - 队列数组
// 返回:1检查通过,负数错误码
BQue_QValid_check(ArrayRef<Numeric> Queue_n_a);
// 3.11 初始化二维队列数组
// 功能:批量初始化二维队列数组中的每个一维队列(用于多数据源/多周期场景)
// 输入:Queue_aa - 二维队列数组 [图层索引][队列数据]
// DataCount_n - 图层总数(对应data1、data2...的个数)
// Capacity_n - 每个队列的容量(最大可存储子项个数)
// FieldSize_n - 每个子项的字段数
// Default_Value_n - 默认填充值
// 返回:1成功,负数错误码
// 说明:通常在策略的Init阶段调用,为每个数据图层初始化独立的队列
BQue_Init2D(ArrayRef<Array<Numeric>> Queue_aa, Numeric DataCount_n, Numeric Capacity_n, Numeric FieldSize_n, Numeric Default_Value_n);
// 3.12 获取表头字段
// 功能:获取表头列指定索引的字段值
// 表头索引从0开始,0=队列大小,1=字段数,2~HeaderSize-1=预留字段
// 输入:Queue_n_a - 队列数组,HeaderIndex_n - 表头字段索引(0 ~ HeaderSize-1)
// 输出:Hdr_Value_n - 表头字段值
// 返回:1成功,-1表头索引超出有效范围,负数其他错误码
BQue_GetHeaderField(ArrayRef<Numeric> Queue_n_a, Numeric HeaderIndex_n, NumericRef Hdr_Value_n, Numeric QV_Chk_En_n);
// 3.13 设置表头字段
// 功能:设置表头列指定索引的字段值
// 表头索引从0开始,0=队列大小,1=字段数,2~HeaderSize-1=预留字段
// 输入:Queue_n_a - 队列数组,HeaderIndex_n - 表头字段索引(0 ~ HeaderSize-1),Hdr_Value_n - 表头字段值
// 返回:1成功,-1表头索引超出有效范围,负数其他错误码
BQue_SetHeaderField(ArrayRef<Numeric> Queue_n_a, Numeric HeaderIndex_n, Numeric Hdr_Value_n, Numeric QV_Chk_En_n);
// =============================================================================
// 第四部分:函数内部变量命名
// =============================================================================
// QueueSize_n - 队列当前有效子项个数(表头[0][0])
// FieldSize_n - 每个子项的字段数(表头[0][1])
// HeaderSize_n - 表头大小 = max(2, FieldSize_n)
// Array_Capacity_n - 数组总长度(GetArraySize返回值)
// Queue_Capacity_n - 队列容量(最大可存储子项个数)
// Default_Value_n - 默认填充值
// Debug_Switch_n - 内部调试开关(1开启,0关闭)
// QV_Chk_Res_n - 队列合法性检查结果
// QV_Chk_En_n - 队列合法性检查开关
// KeyFld1_n - 关键字段1
// KeyFld2_n - 关键字段2
// QueueType_n - 队列类型(0=普通,1=波峰波谷)
// ItemIndex_n - 子项索引
// FieldIndex_n - 字段索引
// Fld_Value_n - 字段值
// TargetIndex_n - 转换后的目标索引
// MatchFound_n - 匹配标志
// temp_fld_value1_n - 临时字段1值
// temp_fld_value2_n - 临时字段2值
// FoundIndex_n - 找到的子项索引
// ai_arr_n, bj_arr_n - 循环变量
// =============================================================================
// 第五部分:注意事项
// =============================================================================
// 1. 所有删除操作都会引起后续子项依次上移一行,时间复杂度O(n * FieldSize)
// 2. 子项索引从1开始,1对应队首,最大子项索引 = 当前大小,-1表示最后一项
// 3. 字段编号从1开始,范围:1 ~ FieldSize
// 4. 表头[0][0]存当前大小,[0][1]存FieldSize,重置时[0][1]保持不变
// 5. HeaderSize = max(2, FieldSize),保证FieldSize=1时表头有2列
// 6. 内部访问公式:第N子项第M字段的数组索引 = HeaderSize + (N-1) * FieldSize + (M-1)
// 7. Init必须在首次使用前调用,指定队列容量和字段数
// 8. 关键字段定义:
// - FieldSize=1:关键字段为第1列
// - FieldSize>1:关键字段为第1列和第2列
// 9. 队列类型:
// - 普通队列(0):适用于唯一性检查场景,只检查最后一项
// - 波峰波谷队列(1):适用于时间序列数据,从后向前搜索,遇小停止
// 10. 所有对外函数都包含 QV_Chk_En_n 参数,用于控制是否进行完整性检查
// 11. 函数成功返回1,失败返回负数错误码,特殊返回值见具体函数说明
// =============================================================================
// 文档结束
// =============================================================================
如果是使用环形数组的话,就对底层结构修改一下即可
// 【波峰波谷队列】物理存储为一维数组,逻辑层面表现为二维数组
// 第一维:图层维度,索引为图层索引
// 第二维:环形队列数组(核心存储层)
// 第二维【环形队列数组】
// 环形结构示意图
// FldCap
// ┌─────────────────────────────┐
// │ │
// │ Header0 phy │
// └─────────────────────────────┘ Phy_Idx Seq_Idx
//┌────────────────────────────────────────────────────────────────────┐
//│ ┌─────────────────────────────┐ │
//│ │ │ │
//│ │ Item1 │ 0 1 │
//│ └─────────────────────────────┘ │
//│ ┌─────────────────────────────┐ │
//│ │ Item2 │ 1 2 │
//│ Circular │ │ │
//│ └─────────────────────────────┘ │
//│ Array │
//│ ... │
//│ ... QueCap │
//│ - │
//│ ┌─────────────────────────────┐ CacheSize │
//│ │ │ │
//│ │ Item_QueCap │ QueCap-1 │
//│ │ │ │
//│ └─────────────────────────────┘ │
//└────────────────────────────────────────────────────────────────────┘
// =========================================================================
// 环形队列数组的定义
// 通过一维数组模拟二维表格结构,实现环形队列特性
// 一维数组模拟二维表格结构设计:
// - 行:将一维数组按 FldCap 长度划分为 QueCap 段,每段为一行,分为【表头行】和【子项行】
// - 列:每行包含 FldCap 个字段列
// 注:后续提及的“一维数据/二维数据”均特指该模拟的二维队列结构
//---------------------------------------------------------
// - 重要参数:
// QueCap - 子项行容量,取值范围 ≥ Max(CacheSize+1, 2),初始化函数设定
// FldCap - 子项行字段数,取值范围 ≥ 1,与表头行总列数一致
// HdCap - HdCap = max(6, FldCap) 取值范围>=6(因表头固定6个核心列),由初始化函数设定,强调:必须使用 PVQue_CalcHdCap 函数计算
// CacheSize - 缓存大小,实际可用子项容量 = QueCap - CacheSize
//---------------------------------------------------------
// - 重要概念(索引命名统一):
// Phy_Item_Idx - 行索引,物理行索引,对应模拟二维数组的行索引,取值范围 [0, QueCap]
// Phy_Idx - 数组实际访问索引(一维数组最终寻址下标)
// Ring_Idx - 环形索引,环形数组行索引,子项在环形缓冲区中的存储位置,范围 0 ~ QueCap-1
// Seq_Idx - 环形序号,环形数组顺序序号行索引,子项在有效队列中的排序位置(对外暴露),范围 1 ~ Count
// 队首有效子项 Seq_Idx = 1,队尾有效子项 Seq_Idx = Count
// Fld_Idx - 字段索引,从1开始计数(所有行的字段均按此规则)
// 索引转换关系(模块内部使用):
// Ring_Idx = (Front + Seq_Idx - 1) MOD QueCap
// Phy_Idx = HdCap + Ring_Idx * FldCap + (Fld_Idx - 1)
// 【子项关键字段】
// - 子项行第1列:KeyFld1_n(对应X_Data,X坐标)
// - 子项行第2列:KeyFld2_n(对应Y_Data,Y坐标)
//---------------------------------------------------------
// 索引规则补充:
// Ring_Idx(环形索引):0 ~ QueCap-1(底层存储下标)
// Seq_Idx(环形序号):1 ~ Count(业务访问序号)
// 【子项访问规则】:
// 访问第 Seq_Idx 个有效子项的第 Fld_Idx 列:
// 1. 计算环形索引:Ring_Idx = (Front + Seq_Idx - 1) MOD QueCap
// 2. 计算最终物理索引:Phy_Idx = HdCap + Ring_Idx * FldCap + (Fld_Idx - 1)
// =========================================================================
//---------------------------------------------------------
// 【队列满处理与丢弃项保存规则】
// - 核心要求:保留因队满被丢弃的前队首项(专属CacheSize区域存储)
// - 队列满判定条件:Count = QueCap - CacheSize(有效子项达可用容量上限)
// - 队列满时入队操作(核心:保留被丢弃项):
// 1. 保留丢弃项:Front指针指向的子项位置不覆盖,作为历史丢弃项(专属存储位)
// 2. 写入新子项:
// (1) 若写入位置 < Rear(环形绕回场景):先将「写入位置~Rear-1」子项整体后移1位,再写入新子项
// (2) 若写入位置 = Rear:直接将新子项写入该位置
// 3. 更新指针:Rear = (Rear + 1) MOD QueCap;Front = (Front + 1) MOD QueCap
// 4. 计数保持:Count 维持 QueCap - CacheSize 不变
// - 丢弃项追溯公式(Insert函数调用时使用):
// Discard_Ring_Idx = (Front - 1 + QueCap) MOD QueCap // 丢弃项环形索引
// Discard_Phy_Idx = HdCap + Discard_Ring_Idx * FldCap + (Fld_Idx - 1) // 丢弃项物理索引
这个算法精巧
学习了
👍
咦 点击tag 不能出现全部“造轮子”相关的帖子哎
恭喜你又找到了bug
加鸡腿加鸡腿~~~~😃
嘿嘿 tag好像彩蛋哦