前言: 本系列是笔者暑假自学数据结构的笔记整理而来,共126页,3w+字。现在正式开学上课,补充老师所讲内容,并且仔细勘误,根据老师的教学进度分章节发布在上。 教材使用的是王红梅等所著的数据结构——从概念到C++实现(第三版)。 暑假自学时期主要参考的网课: 青岛大学-王卓 武汉大学-李春葆
1.栈和队列是两种常用的、重要的数据结构 2.栈和队列是限定插入和删除只能在表的“端点”进行的线性表 3.栈是一种只能在一段进行插入和删除的线性表 4.允许进行插入、删除操作的一段成为栈顶,表的另一段称为栈底 5.当栈中没有数据元素时,称为空栈 6.栈的插入操作通常称为进栈和入栈 7.栈的删除操作通常称为退栈和出栈 8.栈的主要特点是“先进后出”,即后进栈的元素先出栈,栈也称为后进先出表 Eg:
1.队列简称队,也是一种运算受限的线性表 2.队列只能选取一个端点进行插入操作,另一个端点进行删除操作 3.把进行插入的一端称作队尾rear 4.把进行删除的一端称作队首或队头front 5.向队列中插入新元素称为进队或入队,新元素进队后就称为新的队尾元素 6.队列的主要特点是先进先出,所以又把队列称为先进先出表
解决办法靠环形队列或循环队列
/* 环形队列的四要素 队空条件:front = rear 队满条件:(rear + 1)%MAXSIZE = front 进队e操作:rear = (rear + 1)%MAXSIZE;将e放在rear处 出队e操作:front = (front + 1)%MAXSIZE;取出front处元素e 在环形队列中,实现队列的基本运算算法与非环形队列类似 */根据以上推论和题意设计有
typedef struct { ElemType data[MAXSIZE]; int front;//队头指针 int count;//队列中元素个数 }QuType;该环形队列的四要素:
/* 该环形队列四要素: 队空条件:count = 0 队满条件:count = MAXSIZE 进队e操作:rear = (rear + 1)%MAXSIZE;将e放在rear处 出队e操作:front = (front + 1)%MAXSIZE;取出front处元素e 注意:这样的环形队列存放最多MAXSIZE个元素 */对应算法如下:
//初始化队 void InitQueue(QuType *&qu) { qu = new QuType; qu->front = 0; qu->count = 0; } //进队 bool EnQueue(QuType *&qu, ElemType x) { int rear;//临时队尾指针 if (qu->count == MAXSIZE)//队满上溢出 { return false; } else { rear = (qu->front + qu->count)%MAXSIZE;//求队尾位置 rear = (rear + 1)%MAXSIZE;//队尾循环增一 qu->data[rear] = x; qu->count++;//元素个数增一 return true; } } //出队 bool DeQueue(QuType *&qu, ElemType &x) { if (qu->count == 0) { return false; } else { qu->front = (qu->front + 1)%MAXSIZE; x = qu->data[qu->front]; qu->count--; return true; } } //判断空队列 bool QueueEmpty(QuType *qu) { return qu->count == 0; }显然环形队列比非环形队列更有效利用内存空间,即环形队列会重复使用已经出队元素的空间,不会出现假溢出。但如果算法中需要使用所有进队的元素来进一步求解,此时可以使用非环形队列。
//单链表中数据节点类型Qnode定义 typedef struct Qnode { ElemType data;//数据元素 struct Qnode *next; }Qnode; //链队中头结点类型LiQueue定义 typedef struct { Qnode *front;//指向单链表队头结点 Qnode *rear;//指向单链表队尾结点 }LiQueue; /* 链队的四要素 队空条件:front = rear = null 队满条件:不考虑 e进队操作:将包含e的结点插入到单链表表尾 出队操作:删除单链表首数据节点 */ //初始化队列 void InitQueue(LiQueue *&q) { q = new LiQueue; q->front = q->rear = nullptr; } //销毁一个队列 void DestroyQueue(LiQueue *&q) { Qnode *p = q->front, *r;//p指向队头数据节点 if (p != nullptr)//释放数据节点占用空间 { r = p->next; while (r != nullptr) { delete p; p = r; r = p->next; } } delete p, q;//释放链队结点占用空间 } //判断队列是否为空 void QueueEmpty(LiQueue *q) { return q->rear == nullptr; } //进队 /* 包含两种情况 1.原队列为空 2.原队列非空 */ void enQueue(LiQueue *&q, ElemType e) { Qnode *p; p = new Qnode; p->data = e; p->next = nullptr; if (q->rear == nullptr)//若链队为空,新结点是队首节点又是队尾结点 { q->front = q->rear = p; } else { q->rear->next = p;//将*p结点链接到队尾,并将rear指向它 q->rear = p; } } //出队 /* 包含三种情况 1.原队列为空 2.原队列只有一个结点 3.其他情况 */ bool deQueue(LiQueue *&q, ElemType &e) { Qnode *t; if (q->rear == nullptr)//队列为空 { return false; } t = q->front;//t指向第一个数据节点 if (q->front == q->rear)//队列中只有一个结点时 { q->front = q->rear = nullptr; } else { q->front = q->front->next;//队列中有多个结点时 } e = t->data; delete t; return true; }
Eg: 采用一个不带头节点只有一个尾结点指针rear的循环单链表存储队列,设计队列的初始化,进队和出队等算法。
//初始化队列 void initQueue(LinkList *&rear) { rear = nullptr; } //判空 bool queue(LinkList *rear) { return rear == nullptr; } //进队 void enQueue(LinkList *&rear, ElemType x) { LinkList *p; p = new LinkList;//创建新结点 p.data = x; if (rear == nullptr)//原链队为空 { p->next = p;//构成循环链表 rear = p; } else { p->next = rear->next;//将*p结点插入到*rear节点之后 rear->next = p; rear = p;//rear指向新插入的结点 } } //出队 bool deQueue(LinkList *&rear, ElemType &x) { LinkList *q; if (rear == nullptr)//队空 { return false; } else if (rear->next == rear)//原队只有一个结点 { x = rear->data; delete rear; rear = nullptr; } else//原队中有两个或以上的结点 { q = rear->next; x = q->data; rear->next = q->next; delete q; } }栈和队列都是存放多个数据的容器,通常用于存放临时数据。 1.如果先放入的数据先处理,则使用队列 2.如果后放入的数据先处理,则使用栈
一些习题: 1.设循环队列的存储空间为a[0…20],且当前队头指针(f指向队首元素的前一位置)和队尾指针(r指向队尾元素)的值分别为8和3,则该队列中元素个数为? 解:16,这里的MAXSIZE=21,其中的元素个数是(r-f+MAXSIZE)%MAXSIZE= 2.假设用一个不带头结点的单链表表示队列,队头在链表的(?)位置。 解:链头 3.若用一个大小为6的数组来实现循环队列,且当前rear和front的值分别为0和3,当从队列中删除一个元素,再加入两个元素后,rear和front的值分别为? 解:2和4,rear=0,进队2个元素后,rear循环递增2,rear=2;front=3,出队一个元素后,front循环递增1,front=4. 4.一个循环队列中用data[0…n-1]数组保存队中元素,另设置一个队尾指针rear和一个记录队中实际元素个数的变量count,则该队中最多可以存放的元素个数为? 解:n个,队满的条件为count==n 5.已知循环队列存储在一维数组A[0…n-1]中,且队列非空时front和rear分别指向队头元素和队尾元素。若初始时队列空,且要求第一个进入队列的元素存储在A[0]处,则初始时front和rear的值分别为? 解:0和n-1,在循环队列中,进队操作是队尾指针rear循环递增1,再在该处放置进队的元素,本题要求第一个进入队列的元素存储在A[0]处,则rear应为n-1,才能使得(rear+1)%n =0。而队头指针指向队头元素,此时队头位置为0,所以front的初值为0。
以上 如果此篇博客对您有帮助欢迎点赞与转发 有疑问请留言或私信 2020/10/3