数据结构之队列对数据的相关运算及其特征

目录

顺序队列

 链队列

 

顺序队列

思路 :先进先出,可以想象成不能插队的排队,其本质也是受限制的线性表,只允许在一端插入,在另一端取出

顺序实现
    数组:人为抽象理解为环状数组
    判空:队头与队尾相等为空
    判满:(队尾下标+1)%最大元素个数 == 队头下标    
    队列元素有:存储数据的空间、队头和队尾下标、队列最大元素个数

自定义头文件_SEQUEUE_H_:

#ifndef _SEQUEUE_H_
#define _SEQUEUE_H_
#include <stdio.h>
#include <stdlib.h>

typedef struct node{
    int maxlen;
    int front,rear;
    char *str;//数组的首地址
}Sequeue,*pSequeue;
//初始化顺序队列
int queue_create(pSequeue *Q,int maxlen);
//队列判空
int queue_empty(pSequeue q);
//队列判满
int queue_full(pSequeue q);
//入队
int queue_in(pSequeue q,char ch);
//出队
char queue_out(pSequeue q,char *ch);
//队列show
int queue_show(pSequeue q);
//清空队列
int queue_clear(pSequeue q);
//释放队列
int queue_destory(pSequeue *Q);

#endif

sequeue.c

#include "sequeue.h"

//初始化顺序循环队列
int queue_create(pSequeue *Q,int maxlen)
{
    *Q = malloc(sizeof(Sequeue));
    if(NULL == *Q){
        perror("queue_malloc");
        return -1;
    }
    (*Q)->str = malloc(maxlen*sizeof(char));
    if(NULL == (*Q)->str){
        perror("data_malloc");
        free(*Q);
        *Q = NULL;
        return -1;
    }
    (*Q)->maxlen = maxlen;
    (*Q)->front = (*Q)->rear = 0;
    return 0;
}
//队列判空
int queue_empty(pSequeue q)
{
    if(NULL == q)
        return -1;
    //队头队尾可以不等于0,只要相等即可 
    if(q->front == q->rear)
        return 0;
    return -1;
}
//队列判满
int queue_full(pSequeue q)
{
    if(NULL == q)
        return -1;
    if((q->rear + 1) % q->maxlen == q->front)
        return 0;
    return -1;
}
//入队
int queue_in(pSequeue q,char ch)
{
    if(NULL==q || 0==queue_full(q))
        return -1;
//约定队尾没有元素,将队尾元素赋给队尾,队尾再加1 
//这样做只是为了解决"假溢出",空出队尾空间
    q->str[q->rear] = ch;
    q->rear = (q->rear+1) % q->maxlen;
    return 0;
}
//出队
char queue_out(pSequeue q,char *ch)
{
    if(NULL==q || 0==queue_empty(q))
        return -1;
    *ch = q->str[q->rear-1];
    q->front = (q->front+1) % q->maxlen;
    if(NULL == ch)
        return 0;
    else
        return *ch;
//    if(NULL == ch){
//        char t;//等价于上面那些代码
//        t = q->str[q->front];
//        q->front = (q->front+1)%q->maxlen;
//        return t;
//    }
//    *ch = q->str[q->front];
//    q->front = (q->front+1)%q->maxlen;
//    return *ch;
}
//队列show
int queue_show(pSequeue q)
{
    if(NULL==q || 0==queue_empty(q))
        return -1;
    int flag = q->front;
    while(flag != q->rear){
        printf("%c ",q->str[flag]);
        flag = (flag+1) % q->maxlen;
    }
//  for(int i=q->front;i!=q->rear;i=(i+1)%q->maxlen)
//      printf("%c ",q->str[i]);
    puts("");
    return 0;
}
//清空队列
int queue_clear(pSequeue q)
{
    if(NULL == q)
        return -1;
    q->front = q->rear;
    return 0;
}
//释放队列
int queue_destory(pSequeue *Q)
{
    if(NULL == *Q)
        return -1;
    free((*Q)->str);
    free(*Q);
    *Q = NULL;
    return 0;
}

main.c:

#include "sequeue.h"

int queue_create(pSequeue *Q,int maxlen);
int queue_empty(pSequeue q);
int queue_full(pSequeue q);
int queue_in(pSequeue q,char ch);
char queue_out(pSequeue q,char *ch);
int queue_show(pSequeue q);
int queue_clear(pSequeue q);
int queue_destory(pSequeue *Q);
int main(int argc, char *argv[])
{ 
    pSequeue p = NULL;
    queue_create(&p,10);
    char ch;
    while(1){
        fputs("please queue_in data :",stdout);
//if(((0<=ch && 9>=ch) || ('0'<=ch && '9'>=ch))=scanf("%c",&ch))
//这样判断不行,因为键盘上的按键都是字符,另外,下面还有一点
        scanf("%c%*c",&ch);//%*c也可以读取脏字符
        if(ch>='0' && ch <= '9'){
//            getchar();
//这里用getchar()会导致只有输入的是0~9的字符才会读取脏字符 
            break;
        }
        queue_in(p,ch);
    }
    queue_show(p);
    while(1){
        printf("温馨提示:一个字母代表出队一次.\n");
        fputs("please queue_out data :",stdout);
        scanf("%c%*c",&ch);
        if(ch>='0' && ch <= '9')
            break;
        queue_out(p,&ch);
    }//也可以用出队几次来操作,注意范围即可
    queue_show(p);
    queue_clear(p);
    queue_show(p);
    queue_destory(&p);
    if(NULL == &p)
        printf("this NULL.\n");
    return 0;
} 

结果

root@ubuntu:/mnt/U_share/DATA/queue/sequeue# make
gcc ./src/*.c -o queue -I ./inc/
root@ubuntu:/mnt/U_share/DATA/queue/sequeue# ./queue 
please queue_in data :a
please queue_in data :d
please queue_in data :c
please queue_in data :w
please queue_in data :z
please queue_in data :x
please queue_in data :g
please queue_in data :1
a d c w z x g 
温馨提示:一个字母代表出队一次.
please queue_out data :a
温馨提示:一个字母代表出队一次.
please queue_out data :c
温馨提示:一个字母代表出队一次.
please queue_out data :w
温馨提示:一个字母代表出队一次.
please queue_out data :1
w z x g 

总结

解决加溢出问题,也可判断队头、队尾重逢了几次,奇数次为空,偶数次为满
缺点:每次出入队列都要判断,一个标志变量等,很麻烦

留出尾指针指向的空间,现在看着很大,但是以后队列中存放数据多了,将是100    1000    10000分之一
对于顺序队列,先++或者后++无影响,不过要一致对应,不过最后一个需要判断,所以可以进行求余判空满,例如:q->rear = (q->rear+1) % q->maxlen;

 链队列

思路:受限制的顺序表,还是队列,先进先出
注意:队头、队尾指针不能被放在一起,也就是在一个结构体内,因为放在一个结构体内,会导致每个结点都有一个队头、队尾,操作不当时会导致对队列内部的结点进行操作,不符合队列的特性,因此要重新定义一个指针,指向队列的头和尾,也就是需要两个结构体。
而且也很浪费空间,比如100个,要浪费99个两个(头尾)结点的空间。
简单:头结点不存数据
复杂:头结点存数据

不牺牲存储空间,设置size,定义一个变量 size用于记录队列此时记录了几个数据元素,初始化 size = 0,进队成功 size++,出队成功size--,根据size的值判断队满与队空

=====================================================================

数据结点:
typedef struct node{
    int data;
    struct node *next;
}Node,*pNode;
队列结点:
typedef struct queue{
    pNode front,rear;//结构体指针,由于使用了pNode,所以不能更换俩结点声明顺序
}Linkqueue,*pLinkqueue;

开辟:先开辟上面,要用一个指针去接受,然后开辟下面时,再赋值进去,这里浪费了一个指针。
先开辟下面,只需要用这个指针来接收上面这个即可

声明:先声明数据结点,再声明队列结点,不然不知道队列结点里面是啥,有个依赖关系。而且需要注意,声明队列结点时是用的数据结点的结构体指针。


声明不能换顺序,开辟可以换顺序

====================================================================

linkqueue.h

#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#include <stdio.h>
#include <stdlib.h>
//数据节点的声明
typedef struct node{
    int data;
    struct node *next;
}Node, *pNode;
//队列节点的声明
typedef struct queue{
    pNode front, rear;
}Linkqueue, *pLinkqueue;
//二者声明不能换顺序,开辟可以换
//参数初始化链队列
int queue_create(pLinkqueue *Q);
//链队列判空
int queue_empty(pLinkqueue q);
//入队
int queue_in(pLinkqueue q, int data);
//出队
int queue_out(pLinkqueue q);
//清空队列
int queue_clear(pLinkqueue q);
//释放队列
int queue_destory(pLinkqueue *Q);
//队列show
int queue_show(pLinkqueue q);

#endif

linkqueue.c:

#include "linkqueue.h"

//参数初始化链队列
int queue_create(pLinkqueue *Q)
{
    //开辟队列结点
    *Q = malloc(sizeof(Linkqueue)); 
    if(NULL == *Q){
        perror("queue_malloc");
        return -1;
    }
    (*Q)->front = NULL;
    (*Q)->rear = NULL;
    return 0;
}
//链队列判空
int queue_empty(pLinkqueue q)
{
    if(NULL == q)
        return -1;
    if(q->front==NULL || q->rear==NULL)//&&
        return 0;
    return -1;
}
//入队
int queue_in(pLinkqueue q, int data)
{
    if(NULL == q)
        return -1;
    //开辟数据结点
    pNode p = malloc(sizeof(Node));
    if(NULL == p){
        perror("data_malloc");
        return -1;
    }
    p->data = data;
    p->next = NULL;
    if(0 == queue_empty(q)){
        q->front = p;
        q->rear = p;
    }else{
        //把 p 接到队列的尾巴上
        q->rear->next = p;
        //把 q->rear 指向新的尾结点
        q->rear = p;
    }
    return 0;
}
//出队
int queue_out(pLinkqueue q)
{
    if(NULL==q || 0==queue_empty(q))
        return 0;
    int data = q->front->data;
    pNode p = q->front;
    //判断要释放的结点是不是队列中的最后一个结点
    if(q->front == q->rear)
        q->front = q->rear = NULL;
    else
        q->front = p->next;
    free(p);
    return data;
}
//清空队列
int queue_clear(pLinkqueue q)
{
    if(NULL==q || 0==queue_empty(q))
        return -1;
    //用一个临时变量 t 指向待释放的结点
    pNode p = q->front,t;
    while(p){
        t = p;
        p = p->next;
        free(t);
    }
    q->front = q->rear = NULL;
    return 0;
}
//释放队列
int queue_destory(pLinkqueue *Q)
{
    if(NULL == *Q)
        return -1;
    //释放除头结点以外的所有结点
    queue_clear(*Q);
    //释放队列结点
    free(*Q);
    *Q = NULL;
    return 0;
}
//队列show
int queue_show(pLinkqueue q)
{
    if(NULL==q || 0==queue_empty(q))
        return -1;
    pNode p = q->front;
    while(p){
        printf("%d ",p->data);
        p = p->next;
    }
    puts("");
    return 0;
}

main.c:

#include "linkqueue.h"

int queue_create(pLinkqueue *Q);
int queue_empty(pLinkqueue q);
int queue_in(pLinkqueue q, int data);
int queue_out(pLinkqueue q);
int queue_clear(pLinkqueue q);
int queue_destory(pLinkqueue *Q);
int queue_show(pLinkqueue q);
int main(int argc, char *argv[])
{ 
    pLinkqueue q = NULL;
    queue_create(&q);
    int data;
    while(1){
        fputs("please queue_in data : ",stdout);
        if(0 == scanf("%d",&data)){
            getchar();
            break;
        }
        queue_in(q,data);
    }
    queue_show(q);
    printf("queue_out data:%d\n",queue_out(q));
    queue_show(q);
    queue_clear(q);
    queue_in(q,101);
    queue_show(q);
    queue_destory(&q);
    if(NULL == q)
        fputs("this NULL.\n",stdout);
    return 0;
} 

结果

root@ubuntu:/mnt/U_share/DATA/queue/linkqueue# make
gcc ./src/*.c -o queue -I ./inc/
root@ubuntu:/mnt/U_share/DATA/queue/linkqueue# ./queue 
please queue_in data : 1
please queue_in data : 1
please queue_in data : 2
please queue_in data : 3
please queue_in data : 3
please queue_in data : q
1 1 2 3 3 
queue_out data:1
1 2 3 3 
101 
this NULL.

总结

修改指针要用二级指针

一个元素再次出队会段错误

创建头结点只需判空,不用询问是否创建头结点,因为创建的多了,用户根本不知道是不是创建的头结点,只需要判断头结点是否为空即可,循环执行的时候会自动先创建头结点.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值