3.20
1.顺序结构:物理空间连续,插入删除难
2.链式结构:不连续,单链表,循环,双向链表,单链表为intrusive list侵入式链表
3.在线性表中加入约束(适配器adaptor)
栈
- 栈 == 线性表+栈适配器
- 栈的结构:LIFO
- 栈为空,栈overflow,underflow
- 压栈:先动指针,指针处存储,弹栈:
- 访问栈的时候只能通过栈顶指针,指针之上的数据不可访问,可以通过另一些手段访问
顺栈
判满判空
bool IsEmpty(StackPtr S) {
return S -> top == - 1; //注意,top只是用来存储栈顶元素的下标的,并不是指针
}
bool IsFull(StackPtr S) {
return S -> top == Stack_Size - 1;
}
压栈
bool Push(StackPtr S, StackElementType x) {
if (IsFull(S)) return false;
S->elem[++S->top]=x;
return true;
}
弹栈
bool Pop(StackPtr S, StackElementType *x) {
if(!GetTop(S,x)) return false;
--S->top;//只负责弹出,在gettop里接收
return true;
}
其中gettop为获取栈顶元素
bool GetTop(StackPtr S, StackElementType *x) { //获取栈顶元素
if (IsEmpty(S)) return false;
*x = S->elem[S->top];//*x获取了栈顶的元素
return true;
}
链栈
压栈
bool Push(StackPtr S, StackElementType x) {
LinkStackNodePtr temp = (LinkStackNodePtr)malloc(sizeof(LinkStackNode));
if (temp == NULL) return false;
//链表的头插法,插入先连后面,后入
temp->data=x;
temp->next = S->next;
S->next = temp;
return true;
}
弹栈
bool Pop(StackPtr S, StackElementType *x) {
//在链表头部删除节点
LinkStackNodePtr temp = S->next;
if (temp==NULL) return false; //empty
*x = temp->data;
S->next=temp->next;//删除先断前面,然后直接free即可
free(temp);
return true;
}
代码复用:转发,forward
bool Push(StackPtr S, StackElementType x) {
return InsList(S, 1, x);
}//与链表底层逻辑一致,故将stack作为list的wrapper,调用list里的函数
顺序,选择,循环
栈有什么用
深度优先,栈越走越远------后进先出
先进先出:队列
本地函数:函数名前加_
类型前加static
循环队列
队列
后入队
/*入队操作*/
bool EnterQueue(QueuePtr Q, QueueElementType x) {
/*将元素x入队*/
if (IsFull(Q)) /*队列已经满了*/
return false;
Q->element[Q->rear]=x;
Q->rear = _modinc(Q->rear)
//Q->element 是一个数组,用于存储队列中的元素,而 Q->rear 是指向队尾位置的索引。
return true;
}
前出队
/*出队操作*/
bool DeleteQueue(QueuePtr Q, QueueElementType *x) {
/*删除队列的队头元素,用x返回其值*/
if (IsEmpty(Q)) /*队列为空*/
return false;
*x=Q->element[Q->front];
Q->front = _modinc(Q->front);//单纯把下一个元素的索引设置为队头
//TODO
return true; /*操作成功*/
}
其中对位置取余的函数
int _modinc(int x) //
{
return (x+1)%MAXSIZE
}
链队列
后进队
bool EnterQueue(QueuePtr Q, QueueElementType x) {
/* 将数据元素x插入到队列Q中 */
LinkQueueNode *NewNode = (LinkQueueNodePtr)malloc(sizeof(LinkQueueNode));
NewNode->data = x;
NewNode->next = NULL;
Q->rear->next = NewNode;//连接最后的新节点
Q->rear = NewNode;//将新节点用指针指为rear
return true;
}
前出队
bool DeleteQueue(QueuePtr Q, QueueElementType *x) {
/* 将队列Q的队头元素出队,并存放到x所指的存储空间中 */
if (IsEmpty(Q)) return false;
LinkQueueNodePtr p = Q->front->next;
*x = p->data;
Q->front->next = p->next; //
if (Q->rear == p) Q->rear = Q->front;
free(p);
return true;
}
链表边界条件:链表为空,链表只有一个
队列:广度优先
队列(Queue)和栈(Stack)之所以分别与广度优先搜索(BFS, Breadth-First Search)和深度优先搜索(DFS, Depth-First Search)相关联,主要源于它们各自的操作特性以及搜索策略的本质。
队列与广度优先搜索
队列是先进先出(FIFO)的数据结构。在广度优先搜索中,我们从根节点开始,首先探索所有相邻的节点,然后再探索这些相邻节点的未探索过的相邻节点,以此类推。这种一层一层地向外扩展的搜索方式正好符合队列的FIFO特性。
具体来说,广度优先搜索的步骤如下:
- 将根节点放入队列。
- 从队列中取出一个节点,并访问它。
- 将该节点的所有未访问过的相邻节点加入队列。
- 重复步骤2和3,直到队列为空,即所有可达节点都被访问过。
由于队列的特性,广度优先搜索会优先探索离根节点近的节点,即先探索同一层的所有节点,再探索下一层的节点。
栈与深度优先搜索
栈是后进先出(LIFO)的数据结构。在深度优先搜索中,我们从根节点开始,沿着一条路径尽可能深地搜索,直到达到一个无法继续深入的节点(即叶子节点或已访问过的节点),然后回溯到前一个节点,继续搜索其他路径。这种搜索方式符合栈的LIFO特性。
具体来说,深度优先搜索的步骤如下:
- 将根节点放入栈。
- 从栈中取出一个节点,并访问它。
- 将该节点的所有未访问过的相邻节点加入栈(通常是逆序加入,以便回溯时按正确的顺序访问)。
- 重复步骤2和3,直到栈为空,即所有可达节点都被访问过。
由于栈的特性,深度优先搜索会优先探索一条路径直到尽头,然后再回溯探索其他路径。
总结
队列和栈的操作特性与广度优先搜索和深度优先搜索的搜索策略相契合,使得它们成为实现这两种搜索算法的自然选择。队列的FIFO特性使得广度优先搜索能够一层一层地向外扩展;而栈的LIFO特性使得深度优先搜索能够沿着一条路径尽可能深地搜索。
栈:括号匹配,表达式计算