递推均值滤波算法---链式队列实现

目录

 

为什么要写这篇

为什么要用队列实现

程序是怎么实现的

程序实现结果

程序代码


为什么要写这篇

仍记得当初写了一篇去除极值的均值滤波算法相关的博客,该算法用在了ADC采样上面。当初偶然看见还有一种递推均值滤波算法,用在了实时波形输出上面。没有代码实现,本着早晚会用到以及要使用抽象数据类型队列的想法,开始了这篇博客的筹划。

递推均值滤波算法实现方式不见得只有一种方式。环形队列 或 链式队列,先实现自认为比较难一点儿的,之后再有时间则实现相对容易点的。

后期补充:今天我看到了最简单可行的递推均值滤波算法,简单到一目了然,比链式队列更常用。

为什么要用队列实现

主要因为递推这两个字吧,前面的要出去,后面的要进来,然后就自然而然想到了队列的出队和入队。

程序是怎么实现的

  • 假设已经有了队列的数据类型以及常用的操作,可以直接调用。
  • 先初始化队列,不知道队列初始化是为什么,总知道指针初始化是为什么吧,队列的实现需要指针。
  • 然后判断队列是否为满,队列有设置项数最大值,均值滤波的实现需要几个数做均值就设置项数最大值为多少,若没有最大值限制,既极大占用空间,也不适合队列遍历。
  • 产生随机数存放到队列中。
  • 如果队列为满了,这才是需要的,先从队列中删除一项,然后再将随机数存放到队列中。
  • 调试需要:查看产生的随机数。
  • 在一个合适的时间,遍历队列,获取平均值。
  • 调试需要:遍历队列,逐项输出。
  • 程序结束前清空队列。

程序实现结果

程序代码

test_q.c 文件

#include <stdio.h>      /* 调用printf 函数 */
#include <time.h>       /* 调用time 函数 */
#include <stdlib.h>     /* 调用srand rand 函数 */
#include "queue.h"      /* 调用队列的数据类型以及操作函数 */

#define DEBUG_OUTPUT    1


int main(void)
{
    Queue line;
    Item temp;

    srand((unsigned)time(NULL));
    InitializeQueue(&line);
    for(uint8_t i=0; i<15; i++)
    {
        if(!QueueIsFull(&line))
        {
            temp = rand() % 256;
#if DEBUG_OUTPUT 
            printf("random_val is %d \n", temp);
#endif
            EnQueue(temp, &line);
        }else
        {
            DeQueue(&temp, &line);
            if(!QueueIsFull(&line))
            {
                temp = rand() % 256;
#if DEBUG_OUTPUT 
                printf("random_val is %d \n", temp);
#endif
                EnQueue(temp, &line);
            }
        }
        
    }

    if(QueueIsFull(&line))
    {
        Q_DATA_type sum = GetQueueSum(&line);
        printf("\n Queue sum is %d,average value is %d \n\n",sum, sum/MAXQUEUE);
    }else
    {
        printf(" \n The data is not full, the mean filtering cannot be performed !\n\n");
    }
    
#if DEBUG_OUTPUT   
    ShowQueueNode(&line);
#endif
    EmptyTheQueue(&line);

    return 0;
}

queue.h 文件

#ifndef __QUEUE_H__
#define __QUEUE_H__

#include <stdbool.h>    /* 定义bool 类型 */
#include <stdio.h>      /* 定义输入输出 */
#include <stdint.h>     /* 定义常用数据类型 */
#include <stdlib.h>     /* 定义 malloc free exit */

#define MAXQUEUE    10

typedef int Item;
typedef uint32_t Q_DATA_type;

typedef struct node
{
    Item item;
    struct node* next;
}Node;

typedef struct queue
{
    Node* front;    /* 指向队列首项的指针 */
    Node* rear;     /* 指向队列尾项的指针 */
    int items;      /* 队列中的项数 */
}Queue;

/* 
 * 使用链表来实现队列的好处是删除首项时不必移动其余元素
 * 只需重置头指针指向新的首元素即可
 */

/**
 * 初始化队列
 * 前提条件:pq指向一个队列
 * 后置条件:队列被初始化为空
 */
void InitializeQueue(Queue* pq);

/**
 * 检查队列是否已满
 * 前提条件:pq指向被初始化的队列
 * 后置条件:如果队列已满则返回true, 否则返回false
 */
bool QueueIsFull(const Queue* pq);

/**
 * 检查队列是否为空
 * 前提条件:pq指向被初始化的队列
 * 后置条件:如果队列为空则返回true, 否则返回false
 */
bool QueueIsEmpty(const Queue* pq);

/**
 * 确定队列中的项数
 * 前提条件:pq指向被初始化的队列
 * 后置条件:返回队列中的项数
 */
int QueueItemCount(const Queue* pq);

/**
 * 在队列末尾添加项
 * item 是要被添加在队列末尾的项
 * 前提条件:pq指向被初始化的队列
 * 后置条件:如果队列不为满,item将被添加在队列末尾,返回true
 * 后置条件:否则,队列不改变,该函数返回false
 */
bool EnQueue(Item item, Queue* pq);

/**
 * 在队列的开头删除项
 * 前提条件:pq指向被初始化的队列
 * 后置条件:如果队列不为空,队列首端的item将被拷贝到*pitem中,并被删除,函数返回true
 * 后置条件:如果该操作使得队列为空,则重置队列为空
 * 后置条件:如果队列在操作前为空,则函数返回false
 */
bool DeQueue(Item* pitem, Queue* pq);

/**
 * 清空队列
 * 前提条件:pq指向被初始化的队列
 * 后置条件:队列被清空
 */
void EmptyTheQueue(Queue* pq);

/**
 * 显示队列数据
 * 前提条件:pq指向被初始化的队列
 * 后置条件:printf输出队列中所有项的数据
 */
void ShowQueueNode(const Queue* pq);

/**
 * 得到队列数据和
 * 前提条件:pq指向被初始化的队列
 * 后置条件:返回队列中所有项的数据和
 */
Q_DATA_type GetQueueSum(const Queue* pq);



#endif

queue.c 文件

/**
 * @file queue.c
 * @brief   
 * @details 类型属性:可以存储一系列项
 * @details 类型操作:初始化队列为空
 * @details 类型操作:查询队列是否为空
 * @details 类型操作:查询队列是否为满
 * @details 类型操作:确定队列中的项数
 * @details 类型操作:在队列末尾添加项
 * @details 类型操作:在队列开头删除项
 * @details 类型操作:清空队列
 * @details 类型操作:遍历链表,获取队列数据和
 * @details 类型操作:遍历链表,显示队列数据
 * 
 * 
 * @author Lv_*_Guang
 * @version 1.0.0
 * @date 2021-01-05
 * 
 * @copyright Copyright (c) 2021  
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date           <th>Version     <th>Author      <th>Description
 * <tr><td>2021-01-05     <td>1.0.0     <td>Lv_*_Guang     <td>修改内容描述
 * </table>
 */
#include "queue.h"

static void CopyToNode(Item item, Node* pn);
static void CopyToItem(Node* pn, Item* pi);

static void Traverse(const Queue* pq, void(*pfun)(Item item));
static void ShowNodeItem(Item item);
static void QueueValueSum(Item item);
static Q_DATA_type GetQueueValueSum(void);
static void ClearQueueValueSum(void);

/**
 * InitializeQueue
 * @brief   初始化队列为空
 * @param   pq 参数描述: 队列首指针
 */
void 
InitializeQueue(Queue* pq)
{
    pq->front = pq->rear = NULL;
    pq->items = 0;
}

/**
 * QueueIsFull
 * @brief   队列是否为满
 * @param   pq 参数描述: 队列首指针
 * @retval  true 返回值描述: 队列为满
 * @retval  false 返回值描述: 队列非满
 */
bool 
QueueIsFull(const Queue* pq)
{
    return pq->items == MAXQUEUE;
}

/**
 * QueueIsEmpty
 * @brief   队列是否为空
 * @param   pq 参数描述: 队列首指针
 * @retval  true 返回值描述: 队列为空
 * @retval  false 返回值描述: 队列非空
 */
bool 
QueueIsEmpty(const Queue* pq)
{
    return pq->items == 0;
}

/**
 * QueueItemCount
 * @brief   查询队列中项的数目
 * @param   pq 参数描述: 队列首指针
 * @retval  int 返回值描述: 队列在项的数目
 */
int 
QueueItemCount(const Queue* pq)
{
    return pq->items;
}


/**
 * EnQueue
 * @brief   从队列尾添加一项
 * @param   item 参数描述: 结构体
 * @param   pq 参数描述: 队列首指针
 * @retval  true 返回值描述: 添加成功
 * @retval  false 返回值描述: 添加失败
 * 
 * @note    把项添加到队列中,步骤:
 * @note    创建一个新节点
 * @note    把项拷贝到节点中
 * @note    设置节点的next指针为NULL,表明该节点是最后一个节点
 * @note    设置当前尾节点的next指针指向新节点,把新节点链接到队列中
 * @note    把rear指针指向新节点,以便找到最后的节点
 * @note    项数加1
 */
bool 
EnQueue(Item item, Queue* pq)
{
    Node* pnew;
    if(QueueIsFull(pq))
    {
        return false;
    }

    pnew = (Node*)malloc(sizeof(Node));
    if(pnew == NULL)
    {
        printf("Unable to allocate memory! \n");
        exit(1);
    }

    CopyToNode(item, pnew);
    pnew->next = NULL;

    if(QueueIsEmpty(pq))
    {
        pq->front = pnew;
    }else
    {
        pq->rear->next = pnew;      /* 链接到队列尾端 */
    }
    pq->rear = pnew;            /* 记录队列尾端的位置*/
    pq->items++;
    return true;
    
}

/**
 * DeQueue
 * @brief   从队列头删除一项
 * @param   pitem 参数描述: 结构体指针
 * @param   pq 参数描述: 队列首指针
 * @retval  true 返回值描述: 删除成功
 * @retval  false 返回值描述: 删除失败
 * 
 * @note    把项从队列首端删除,涉及以下几个步骤:
 * @note    把项拷贝到给定的变量中
 * @note    释放空出的节点使用的内存空间
 * @note    重置首指针指向队列中的下一个项
 * @note    如果删除最后一项,把首指针和尾指针都重置为NULL
 * @note    项数减1
 */
bool 
DeQueue(Item* pitem, Queue* pq)
{
    Node* pt;
    
    if(QueueIsEmpty(pq))
    {
        return false;
    }

    CopyToItem(pq->front, pitem);
    pt = pq->front;
    pq->front = pq->front->next;
    free(pt);
    pq->items--;
    if(pq->items == 0)
    {
        pq->rear = NULL;
    }

    return true;
}

/**
 * EmptyTheQueue
 * @brief   清空队列
 * @param   pq 参数描述: 队列首指针
 */
void 
EmptyTheQueue(Queue* pq)
{
    Item dummy;
    while(!QueueIsEmpty(pq))
    {
        DeQueue(&dummy, pq);
    }
}

/**
 * CopyToNode
 * @brief   从结构体赋值到链表节点
 * @param   item 参数描述: 结构体指针
 * @param   pn 参数描述: 链表节点指针
 */
static void 
CopyToNode(Item item, Node* pn)
{
    pn->item = item;
}

/**
 * CopyToItem
 * @brief   从链表节点复制到结构体中
 * @param   pn 参数描述: 链表节点指针
 * @param   pi 参数描述: 结构体指针
 */
static void 
CopyToItem(Node* pn, Item* pi)
{
    *pi = pn->item;
}


static uint8_t term_val = 0;

/**
 * ShowNodeItem
 * @brief   显示队列节点数据
 * @param   item 节点数据
 */
static void 
ShowNodeItem(Item item)
{
    printf("Queue node %d item is %d \n", term_val, item);
}

/**
 * Traverse
 * @brief   遍历链表,执行某种操作
 * @param   pq 队列指针
 * @param   pfun 函数指针
 * @note    目前可调用函数有:queue_valeue_sum 和 show_node_item
 */
static void 
Traverse(const Queue* pq, void(*pfun)(Item item))
{
    Node *pnode = pq->front;

    while(pnode != NULL)
    {
        (*pfun)(pnode->item);
        term_val++;
        pnode = pnode->next;
    }
    term_val = 0;
}

/* 后续添加,对队列数值求和 */
static Q_DATA_type q_val_sum = 0;

/**
 * QueueValueSum
 * @brief   队列各节点数据求和
 * @param   item 节点数据
 */
static void 
QueueValueSum(Item item)
{
    q_val_sum += item;
}

/**
 * GetQueueValueSum
 * @brief   获取队列数据和
 * @return  队列数据和
 */
static Q_DATA_type 
GetQueueValueSum(void)
{
    return q_val_sum;
}

/**
 * ClearQueueValueSum
 * @brief   清除队列和
 */
static void 
ClearQueueValueSum(void)
{
    q_val_sum = 0;
}

/**
 * GetQueueSum
 * @brief   获取队列数据和
 * @param   pq 队列指针
 * @return  队列数据和
 */
Q_DATA_type 
GetQueueSum(const Queue* pq)
{
    Q_DATA_type sum;

    Traverse(pq, QueueValueSum);
    sum = GetQueueValueSum();
    ClearQueueValueSum();

    return sum;
}

/**
 * ShowQueueNode
 * @brief   显示队列数据
 * @param   pq 队列指针
 */
void 
ShowQueueNode(const Queue* pq)
{
    Traverse(pq, ShowNodeItem);
}

猜你喜欢

转载自blog.csdn.net/quanquanxiaobu/article/details/112304431