链队列

哎,又是大家,都快双11了,打赏一下小编吧。(另外发现多行空格打字好像不能表示,因此这里就把代码涂红了,有点鲜艳,-_-)node

言归正传,今天咱们讲讲链队列,头文件那些上次讲过了,差很少,我就再也不赘述了。数组

咱们先讲讲队列的特性:先进先出数据结构

(这里说一下,此图取自Leetcode)函数

 

在 FIFO 数据结构中,将首先处理添加到队列中的第一个元素。
spa

如上图所示,队列是典型的 FIFO 数据结构。插入(insert)操做也称做入队(enqueue),新元素始终被添加在队列的末尾。 删除(delete)操做也被称为出队(dequeue)。 你只能移除第一个元素。
指针

为了实现插入和删除操做,咱们须要用到一个数组来存储元素和指针移到,另外还须要两个指针front和rear来表示队列的头部和尾部(因此其实咱们能够把它当成循环队列来理解。)code

那么目标明确了,就先定义结构体了。对象

 

typedef struct node
{
  elemtype data;                             //元素
  struct node *next;                         //移到的指针设置
}QNode,*queuenode;                          //QNode是数组,后面那个就是数组的指针形式了,能够这么理解的,名称本身随意了,最好是能表达意思的那种
typedef struct
{
  queuenode front, rear;                  //这里的定义实际上是多变的,可是归根结底仍是要把这两个设置为指针型。
}linkqueue;                                           //这里的定义其实也是多变的,你能够定义为指针型,都OK的,可是数组简单一点,就这样了。blog

1,插入(也就是入队)队列

这里补充一下,为何今天没有定义函数了呢,由于队列的定义相对简单,直接在主函数里实现就行了。(但仍是要尽量的保持主函数的简洁性)

再来剖析一下入队操做:1,基础的分配空间;2,插入必备的赋值,和地址转换

 

status enqueue(linkqueue *Q, elemtype e){                    //可能有同窗会问,为何个人是*Q,由于我上面的第二个数组我只设置了数组类型名,我想用地址来作,因此就加*号了,这里看你们习惯了。除此以外,我这里讲一下,不知道讲过没有,仍是讲一下,数  组的指针的引用区别:数组是加“.” ,而指针则是用“->” 。
queuenode p;
p=(queuenode) malloc (sizeof (QNode));
if(!p) exit (ERROR);
p->data = e;
p->next = NULL;                                                              //你们尚未忘记咱们上面放的那个图吧,队列是先进先出,因此它的插入都是插在后面,也就是说插进来的那个元素的next始终指向NULL。
Q->rear->next = p;                                                           //这里有没有那个小伙伴有问题的啊,好,没有,下一个
Q->rear = p;                                                                      //开玩笑开玩笑,为何这里会用rear呢,按理来讲,插入不是应该要头结点的next吗?你们再好好想想咱们一开始的那个图,队列是先进先出,也就是说只有队尾是在移动的,是可以用了插入的,你一直用front那岂不是每次插入的都是同一个位置,是吧,固然,你能够再中间断开,但那样麻烦的十我是不会作的。
return OK;                                                                          //想必有小伙伴已经发现了一个问题了,嘿嘿,我先不说
}

 

2,删除(也就是出队)

剖析剖析,删除之基本操做:1,弹出去一个元素,其余的补上去,呸,我呸,知不知道那样的时间复杂度为O(n),队列队列,一开始设置那个头结点用来干什么的,好看的吗(固然不包括上面发现问题的同窗们的那个东西),这时咱们就能够经过移动头结点来进行删除操做了,这样的时间复杂度就少了太多了,2,切记要free空间,虽然系统本身会清理掉,但写出来就是加分项哦

 

status dequeue(linkqueue *Q, elemtype *e){                        //Q我无论大家怎么定义了,可是e必定要用指针,其实要做出改变的对象应该都要用*的,也就是指了(好习惯)
queuenode p;
if(Q->front == Q->rear) return ERROR;                                 //好了好了,如今咱们来讲说上面发现的那个问题,我一开始是否是说了这是一个循环队列,循环循 环,那是否是总有一天front会跟rear碰上,这就是一个目前为止最能体现出插人函数分配空间的用处的了,由于每次插入,咱们都会给它分配空间,因此它是能够无限插入直到电脑不行了的。

                                                                                                //那碰上了,怎么办。碰上了就表明到头了,也就是循环一遍了,那删除一开始,还没开始动,就头了,那确定就是一个空队列了,就要退出了。
p = Q->front->next;                                                                  //移移移移动头结点
*e = p->data;
Q->front->next = p->next;                                                        //这里是必需要指一下next的哦,你们还记得链表吗,头结点位于第一个元素前面一点点
if(Q->rear ==p)Q->rear = Q->front;                                          //这里是什么意思,就是表明着队列里只有一个元素
free(p);
return OK;
}

好了好了,插入和删除就到这里结束了,再给你们讲一下print函数吧

剖析again,怎么输出呢,有不少方法:1,从头结点开始遍历,2,咱们一开始不是有定义一个数组吗,能够经过数组输出,3,由于是循环队列,从队尾开始也是OK的。(这里讲讲第一种,比较经常使用的)

 

void printf_Q(linkqueue Q){
int i;
Q.front=Q.front->next;                                          //注意一开始必需要指一下next,缘由上面讲过了
if(Q.front==Q.rear) return ERROR;                      //同样的,空就退出
for(i=0;;i++){
printf("%d ",Q.front->data);                                   //输出就很简单了,由于结点也是定义的数组指针,因此直接值向data就行了
if(Q.front!=Q.rear)                                                 //直到他们碰上,就表明已经循环了一遍了
Q.front=Q.front->next;
else
break;
}

}

老师还有布置一个返回长度的函数吧,那个跟print其实差很少,就不赘述了。

附:主函数:

void main()
{
QNode Q0; linkqueue Q; elemtype x;
Q0.next=NULL;
Q.front=&Q0; Q.rear=&Q0;
enqueue(&Q,3); enqueue(&Q,55); enqueue(&Q,2); enqueue(&Q,9); enqueue(&Q,7);
enqueue(&Q,32); enqueue(&Q,66);
printf("长度为:%d, 元素为:", length_Q(Q));printf_Q(Q);
dequeue(&Q,&x); dequeue(&Q,&x);
printf("\n长度为:%d, 元素为:", length_Q(Q));printf_Q(Q);
enqueue(&Q,77);
printf("\n长度为:%d, 元素为:", length_Q(Q));printf_Q(Q);
}

 

你们,下次再见吧(再说)!!!

相关文章
相关标签/搜索