C语言回顾--队列的入队和出队

本文介绍了作者在海康威视笔试中遇到的C语言实现队列入队和出队的问题。通过使用链表结构,文章详细讲解了如何用两个指针表示队列的头和尾,以及如何实现先进先出(FIFO)的逻辑。文中给出了结构体定义、入队和出队的函数实现,并展示了完整的代码和程序运行结果。最后,作者提出可以用循环队列来优化解决方案。

-------------------------------------------------------------------------------------------

前言:

前几天做了海康威视的笔试题,正好大题遇上了用C语言编程实现队列的入队和出队功能;于是考完后就自己调试当时所写的代码,错误颇多,经过几小时的努力调试成功,特定发到博客上分享给大家。

-------------------------------------------------------------------------------------------

队列:就跟排队买饭一样,先到者先买饭,买完就离开,后来的人只能排队,也就是先进先出;因此,本次编程我以链表的形式来实现题目需求;

那这么表示一个队列的头和尾呢?这里,我们引入2指针,分别是指向第一个节点的头指针,指向尾节点的尾指针,那如何移动呢?你可以想一下,买饭的时候是一个接一个的排队,但是买饭的窗口是固定的,你只有到窗口,阿姨才问你吃什么饭,然后打菜给你,拿到了饭你也就走了,轮到下一个人买,也就是说指针pTailNode一直向后移,而pHeadNode一直在处理这些人所需要处理的事情,所以说先进先出的这个道理。再简单一句话,你pTailNode一直在给pHeadNode找麻烦,而pHeadNode又像爸爸一样给你搽屁股的解决这些事;

下面是代码流程:

结构体定义如下:

typedef struct _QUEUE_NODE_         //一个节点的结构体   
{
    struct _QUEUE_NODE_ *pNextNode;
    int                       data;
}QUEUE_NODE,*PQUEUE_NODE;

说明:
1.海康威视里面存放的数据是void *类型,本实验就用简单的int 类型来代替;
2.定义一个结构体类型名为_QUEUE_NODE_,并且用该结构体类型名命名一个叫QUEUE_NODE,它等价于QUEUE_NODE = struct _QUEUE_NODE_ ,而*PQUEUE_NODE呢?
同样,*PQUEUE_NODE = (struct _QUEUE_NODE_ *),所以调用时只需QUEUE_NODE,PQUEUE_NODE即可;
3.该结构体中含有1个数据类型,1个结构体指针类型(用于连接下一个节点);
typedef struct QUEUE        //链表的结构体
{
    QUEUE_NODE *pHeadNode;
    QUEUE_NODE *pTailNode;
}QUEUE_HANDLE,*PQUEUE_HANDLE;

说明:
1.海康威视省略了定义结构体名为QUEUE,这里是我添加的,有无并不影响调用QUEUE_HANDLE和PQUEUE_HANDLE;
2.与第一个结构体相同,QUEUE_HANDLE = struct QUEUE ;PQUEUE_HANDLE = (struct QUEUE *);
3.该结构体内有2个指针,一个是头指针,pHeadNode用于标识这个链表的第一个节点,pTailNode用于标识这个链表的最后一个节点;

入队的函数: 

QUEUE_HANDLE *insert(QUEUE_HANDLE *HQ,int Data)    //入队
{
    PQUEUE_NODE s;

    if(HQ == NULL)                                   //传参检查
    {
        printf("HQ value is NULL.\n"); 
        return NULL;
    }

    s = (QUEUE_NODE *)malloc(sizeof(QUEUE_NODE));    //新节点分配
   
    s->data = Data;
    s->pNextNode = NULL;

    if(HQ->pTailNode == NULL && HQ->pHeadNode == NULL)    //临界条件:插入第一个节点时
    {
        HQ->pHeadNode = s;
        HQ->pTailNode = s;
    }
    else                            //正常情况
    {
        HQ->pTailNode->pNextNode = s;
        HQ->pTailNode = s;
    }

    return HQ;

}

出队函数:

QUEUE_HANDLE *out(QUEUE_HANDLE *HQ)
{
    QUEUE_NODE *p;
    p = HQ->pHeadNode;
    
    if(p == NULL)
    {
        printf("yichu\n");
        return NULL;
    }
    else
    {
        printf("queue data:%d\n",p->data);
        printf("queue adress:%p\n",p);
        
        if(HQ->pHeadNode == HQ->pTailNode)    //临界条件:只剩最后一节点时
        {
            HQ->pHeadNode = NULL;
            HQ->pTailNode = NULL;
            free(p);
            p = NULL;                        //防止p访问被Free的内存;

        }
        else        //正常情况
        {
            HQ->pHeadNode = HQ->pHeadNode->pNextNode;
            free(p);
        }

    return HQ;
    }
}

完整的代码:包括遍历函数和使用上述函数的调用;

/*********************************************************************************
 *      Copyright:  (C) 2018 guozhihao
 *                  All rights reserved.
 *
 *       Filename:  homework.c
 *    Description:  This file 
 *                 
 *        Version:  1.1.0(10/27/2019)
 *         Author:  Guozhihao <810170156@qq.com>
 *      ChangeLog:  2, Release initial version on "10/27/2019 08:35:03 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
typedef struct _QUEUE_NODE_
{
    struct _QUEUE_NODE_ *pNextNode;
    int                       data;
}QUEUE_NODE,*PQUEUE_NODE;

typedef struct QUEUE
{
    QUEUE_NODE *pHeadNode;
    QUEUE_NODE *pTailNode;
}QUEUE_HANDLE,*PQUEUE_HANDLE;

QUEUE_HANDLE *insert(QUEUE_HANDLE *HQ,int Data)
{
    
    if(HQ == NULL)
    {
        printf("HQ value is NULL.\n"); 
        return NULL;
    }

    PQUEUE_NODE s;
    s = (QUEUE_NODE *)malloc(sizeof(QUEUE_NODE));
   
    s->data = Data;
    s->pNextNode = NULL;

    if(HQ->pTailNode == NULL && HQ->pHeadNode == NULL)
    {
        HQ->pHeadNode = s; 
        HQ->pTailNode = s; 
    }
    else
    {
        HQ->pTailNode->pNextNode = s;
        HQ->pTailNode = s;
    }

    return HQ;

}

QUEUE_HANDLE *out(QUEUE_HANDLE *HQ)
{
    QUEUE_NODE *p;
    p = HQ->pHeadNode;
    
    if(p == NULL)
    {
        printf("yichu\n");
        return NULL;
    }
    else
    {
        printf("queue data:%d\n",p->data);
        printf("queue adress:%p\n",p);
        
        if(HQ->pHeadNode == HQ->pTailNode)
        {
            HQ->pHeadNode = NULL;
            HQ->pTailNode = NULL;
            free(p);
            p = NULL;

        }
        else
        {
            HQ->pHeadNode = HQ->pHeadNode->pNextNode;
            free(p);
            p = NULL;
        }

        return HQ;
    }
}

void travel(QUEUE_HANDLE *HQ)
{
    QUEUE_NODE *p;
   
    p = HQ->pHeadNode;

    while(p != NULL)
    {
        printf("queue data:%d\n",p->data);
        printf("queue adress:%p\n",p);
        
        p = p->pNextNode;
                
    }
}

int main (int argc, char **argv)
{
    QUEUE_HANDLE *HQ ;
    int             i;
    HQ = (QUEUE_HANDLE *)malloc(sizeof(QUEUE_HANDLE));
    
    HQ->pTailNode = NULL;
    HQ->pHeadNode = NULL;
    
    printf("now start to add new node:\n");

    for(i = 0;i<10;i++)
    {
        HQ = insert(HQ,i);
        if(!HQ)
        {
            printf("HQ insert failed.\n"); 
            goto END_EXIT;
        }

    }

    travel(HQ);
    
    printf("now start to free node:\n");

#if 1 
    for(i = 0;i<5;i++)
    {
        HQ = out(HQ);
        if(!HQ)
        {
            printf("HQ is NULL.\n"); 
            goto END_EXIT;
        }
    }
    printf("The remaining node:\n");

#endif
    travel(HQ);

END_EXIT:

   if(HQ) 
   {
       free(HQ);
   }

    return 0;
} /* ----- End of main() ----- */

附上makefile文件:(把makefile放置于zhan.c同级目录下后,在命令行里敲make即可编译)

# /bin/bash

PWD := $(shell pwd)

filename := duilie

testfile :=${PWD}/${filename}

tools:=gcc

all: GCC

GCC:
        ${tools} ${testfile}.c -o ${filename} -Wall
        @echo " gcc ${testfile} successfully!";
        ./${filename}

debug:
        gcc -g ${testfile}.c -o ${filename}
        gdb ${testfile}

cleanup:
        @rm ${filename}

程序运行结果:

[guozhihao@bogon shujujiegou]$ make
gcc /home/guozhihao/Master/guozhihao/C6/shujujiegou/duilie.c -o duilie -Wall
 gcc /home/guozhihao/Master/guozhihao/C6/shujujiegou/duilie successfully!
./duilie
now start to add new node:
queue data:0
queue adress:0x14bd030
queue data:1
queue adress:0x14bd050
queue data:2
queue adress:0x14bd070
queue data:3
queue adress:0x14bd090
queue data:4
queue adress:0x14bd0b0
queue data:5
queue adress:0x14bd0d0
queue data:6
queue adress:0x14bd0f0
queue data:7
queue adress:0x14bd110
queue data:8
queue adress:0x14bd130
queue data:9
queue adress:0x14bd150
now start to free node:
queue data:0
queue adress:0x14bd030
queue data:1
queue adress:0x14bd050
queue data:2
queue adress:0x14bd070
queue data:3
queue adress:0x14bd090
queue data:4
queue adress:0x14bd0b0
The remaining node:
queue data:5
queue adress:0x14bd0d0
queue data:6
queue adress:0x14bd0f0
queue data:7
queue adress:0x14bd110
queue data:8
queue adress:0x14bd130
queue data:9
queue adress:0x14bd150

 

思考:

当pHeadNode指针处理完pTailNode安排的事情之后相遇;双方都将其指向NULL这是一种简单的处理方法,还有一种可以给自己加分的方法:就是用循环队列的思想去解决;

<think> 首先,用户的问题是:“我想了解循环队列进队操作出队操作的相关知识 请问循环队列进队操作与出队操作的原理及实现”。系统指令要求我使用中文回答,结构清晰,逐步解决问题,并保证回答真实可靠。 系统指令还指定了LaTeX格式:行内数学表达式用$...$,独立公式用$$...$$,并单独成段。我需要确保LaTeX语法正确。此外,回答末尾需要生成相关问题,并在引用的段落末尾添加引用标识。 用户提供了几个引用,这些是关于循环队列的基本操作、实现问题的总结。引用[1]解释队列的概念FIFO原则;引用[2][3]提供循环队列入队出队代码实现;引用[4][5]涉及进队与出队的算法示例。 我的回答应该基于这些引用,但系统提示说这些引用仅供参考,不是用户真正的需求,我应该参考用户上一次的问题我的回答。但用户说“用户上一次的问题”可能没有上下文,因为这是当前的查询。所以,我主要基于用户提供的引用问题来回答。 关键点: - **循环队列**:是一种队列实现方式,使用数组存储元素,通过模运算实现循环,以避免空间浪费。 - **进队操作(EnQueue)**:在队尾添加元素。 - **出队操作(DeQueue)**:在队头移除元素。 参考引用: - 引用[1]:描述队列为FIFO结构。 - 引用[2]:给出出队操作的代码。 - 引用[3]:详细说明循环队列的初始化、入队出队操作。 - 引用[4]:显示进队出队的示例代码。 - 引用[5]:解释frontrear指针的作用。 回答结构: 1. **介绍循环队列**:简要解释循环队列的概念为什么需要它(避免普通队列的假溢出问题)。 2. **原理**:说明进队出队操作的原理,包括指针的使用(frontrear)。 3. **实现**:提供代码示例(使用C语言或类似语言,因为引用中是C),并解释关键步骤。 4. **注意事项**:讨论队空队满的判断条件。 5. **总结**:简要回顾。 6. **生成相关问题**:在末尾添加相关问题,使用“§§相关问题§§”格式。 代码格式:在回答中使用代码块来展示代码,如示例中的Python代码。用户引用中是C语言代码,所以我应该用类似格式。 LaTeX使用:如果有数学表达式,比如模运算,用$...$格式。例如,模运算可以写成$(index + 1) \% size$。 添加引用标识:在引用的段落末尾添加[^n]。既然用户提供了引用,我应该适当地在回答中引用它们,但系统说“回答中引用的段落末尾自然地添加引用标识”。所以,当我基于某个引用解释时,在句子末尾添加引用标识。 例如,在解释FIFO时,添加[^1]。 完整回答大纲: 1. **引言**:定义循环队列其重要性。 2. **进队操作**: - 原理:在队尾添加元素,rear指针移动。 - 实现:代码示例,解释队空/队满判断。 - 引用[^3]或[^5]。 3. **出队操作**: - 原理:在队头移除元素,front指针移动。 - 实现:代码示例。 - 引用[^2]或[^3]。 4. **完整示例**:提供一个简单的代码片段展示入队出队。 5. **常见问题**:如队满队空的判断。 6. **相关问题**:生成2-3个相关问题。 生成相关问题: - 例如:循环队列的队满条件是什么? - 如何避免循环队列的冲突? - 循环队列与普通队列的区别? 确保回答真实可靠:基于数据结构知识,不要添加虚假信息。我可以用引用中的代码作为基础。 </think>### 循环队列进队操作与出队操作的原理及实现 循环队列是一种基于数组实现队列结构,通过模运算实现空间的循环利用,避免普通队列的“假溢出”问题。队列遵循先进先出(FIFO)原则,只允许在队尾进行入队(进队)操作,在队头进行出队操作[^1]。下面我将逐步解释其原理实现。 #### 1. **循环队列的基本原理** - **指针定义**:循环队列使用两个指针: - `front`:指向队头元素的前一个位置(或直接指向队头,具体实现可能略有差异)。 - `rear`:指向队尾元素的下一个可用位置(即新元素插入的位置)。 - **循环机制**:通过模运算(取余操作)实现指针的循环移动。例如,当指针到达数组末尾时,通过$(index + 1) \% size$跳回数组起始位置,其中$size$是队列容量。 - **队空与队满判断**: - 队空条件:$front == rear$。 - 队满条件:$(rear + 1) \% size == front$(牺牲一个存储单元以区分空满状态)[^3][^5]。 #### 2. **进队操作(EnQueue)** 进队操作在队尾添加元素,rear指针向后移动。 - **原理**: - 检查队列是否已满:如果$(rear + 1) \% size == front$,则队列满,无法插入。 - 如果不满,将新元素存入rear指向的位置,然后更新rear:$rear = (rear + 1) \% size$。 - 时间复杂度:$O(1)$,是常数时间操作[^3][^5]。 - **代码实现(基于C语言)**: 参考引用中的实现,以下是一个清晰的示例: ```c #define MAXSIZE 100 // 队列最大容量 typedef struct { int data[MAXSIZE]; // 存储元素的数组 int front; // 队头指针 int rear; // 队尾指针 } SqQueue; // 进队函数 int EnQueue(SqQueue *Q, int x) { if ((Q->rear + 1) % MAXSIZE == Q->front) { // 队满判断 return 0; // 插入失败 } Q->data[Q->rear] = x; // 元素存入队尾 Q->rear = (Q->rear + 1) % MAXSIZE; // rear指针循环移动 return 1; // 插入成功 } ``` **解释**: - `(Q->rear + 1) % MAXSIZE == Q->front`:利用模运算判断队满,避免溢出。 - `Q->rear = (Q->rear + 1) % MAXSIZE`:rear指针移动到下一个位置,如果到达数组末尾则循环到起始位置[^3][^4]。 #### 3. **出队操作(DeQueue)** 出队操作在队头移除元素,front指针向后移动。 - **原理**: - 检查队列是否为空:如果$front == rear$,则队列空,无法删除。 - 如果不空,从front指向的位置取出元素,然后更新front:$front = (front + 1) \% size$。 - 时间复杂度:$O(1)$,是常数时间操作[^2][^3]。 - **代码实现(基于C语言)**: 以下是一个标准实现: ```c // 出队函数 int DeQueue(SqQueue *Q, int *e) { if (Q->front == Q->rear) { // 队空判断 return 0; // 删除失败 } *e = Q->data[(Q->front + 1) % MAXSIZE]; // 取出队头元素(front指向队头前一个位置) Q->front = (Q->front + 1) % MAXSIZE; // front指针循环移动 return 1; // 删除成功 } ``` **解释**: - `Q->front == Q->rear`:简单判断队空。 - `(Q->front + 1) % MAXSIZE`:计算实际队头位置(因为front通常指向队头元素的前一个位置)。 - 指针移动通过模运算实现循环,例如`Q->front = (Q->front + 1) % MAXSIZE`[^2][^3]。 #### 4. **完整操作示例** 以下是一个简单的演示代码,展示进队出队的全过程: ```c #include <stdio.h> #define MAXSIZE 5 // 小容量便于演示 int main() { SqQueue Q; Q.front = Q.rear = 0; // 初始化队列 // 进队操作:插入元素1,2,3 EnQueue(&Q, 1); EnQueue(&Q, 2); EnQueue(&Q, 3); // 出队操作:依次取出元素 int y; DeQueue(&Q, &y); printf("出队元素: %d\n", y); // 输出: 出队元素: 1 DeQueue(&Q, &y); printf("出队元素: %d\n", y); // 输出: 出队元素: 2 // 继续进队 EnQueue(&Q, 4); EnQueue(&Q, 5); // 再次出队 DeQueue(&Q, &y); printf("出队元素: %d\n", y); // 输出: 出队元素: 3 return 0; } ``` **输出说明**:队列元素按FIFO顺序处理,指针循环移动避免空间浪费[^4][^5]。 #### 5. **关键注意事项** - **队满处理**:牺牲一个存储单元来区分空满状态,提高效率。 - **边界条件**:始终检查队空/队满,否则可能导致数据错误或溢出。 - **性能优势**:循环队列入队出队操作均高效,适合高频数据缓冲场景,如操作系统调度或实时系统。 - **常见错误**:如指针移动未使用模运算,可能造成数组越界[^3][^5]。 总结来说,循环队列的进队出队操作依赖于frontrear指针的模运算移动,确保空间高效利用。如果您需要更详细的代码优化或特定语言实现,请进一步说明。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值