一.栈
1.栈的定义
栈(Stack)又称堆栈,仅允许在表的一端进行插入和删除运算。允许插入和删除的一端称为“栈顶”,另一端叫“栈底”。
具有后进后出的特性。
2.栈的顺序存储结构
1.顺序栈
顺序栈的类型描述:
#define MAXSIZE //栈的最大元素数
typedef int ELemType;
typedef struct
{
ELemType elem[MAXSIZE];
int top;
}SepStack;
SepStack *s; //s为指向栈顶的指针
通常将0下标设为栈底,空栈时是s->top = -1,入栈s->top++,出栈s->top--。
2.基本操作:
(1)置空栈
SeqStack *InitStack()
{ SeqStack *s;
s->top = -1;
return s;
}
(2)判空栈
int Empty(SeqStack *s)
{
if(s->top == -1) return 1;
else return 0;
}
(3)入栈
int Push(SeqStack *s,ELemType x)
{
if(s->top == MAXSIZE-1) return 0; //栈满不能入栈
else{ s->top++;
s->elem[s->top] = x;
return 1;
}
}
(4)出栈
int pop(SeqStack *s,ELemType *x)
{
if(Empty(s)) return 0; //栈空不能出栈
else { *x=s->elem[s->top];
s->top--;
return 1;
}
}
(5)取栈顶元素
ElemType GetTop(SeqStack *s)
{
if(Empty(s)) return 0;
else return (s->elem[s->top]);
}
3.栈的链式存储结构
在链栈中,栈底总是链表的最后一个结点,栈顶是链表的第一个结点,链表的表头指针top就作为栈顶指针,top始终指向当前栈顶元素前面的头结点,即 top>next 为栈顶元素,当 top>next == NULL,则代表栈空。
typedef struct Stacknode
{
ELemType data;
struct Stacknode *next;
}slStacktype;
(1)入栈操作
int PushLstack(slStacktype *top,int x)
{
slStacktype *p; //申请一个结点
if((p=(slStacktype*)malloc(sizeof(slStacktype)))==NULL)
return FALSE;
p->data = x;
p->next = top->next;
top->next = p;
return p;
}
(2)出栈操作
int PopLstack(slStacktype *top)
{
slStacktype *p;
int x;
if(top->next == NULL)
return ; //此时栈为空
p = top->next;
top->next = top->next->next;
x = p->data;
free(p);
return x;
}
(3)多个链表的操作
涉及多个链栈的操作,可以将这些链栈的栈顶指针放在一个一维数组中。
即 设 slStacktype *top[M];
多个链表的入栈、出栈操作,只需将上面代码的 top 改为 top[i] 。
4.例题
1.有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1.左括号必须用相同类型的右括号闭合。 2.左括号必须以正确的顺序闭合。
输入:s = "([)]" 输出:false
输入:s = "()[]{}" 输出:true
输入:s = "(]" 输出:false
char pairs(char a)
{
if(a == '(') return ')';
if(a == '{') return '}';
if(a == '[') return ']';
return 0;
}
bool isValid(char *s)
{
int n=strlen(s);
if(n%2 == 1)
return FALSE;
int stk[n],top=0;
for(int i=0;i<n;i++)
{
char ch=pairs(s[i]);
if(ch)
{
if(top == 0 && stk[top-1] != ch )
return FALSE;
top--;
}
else {
stk[top++] = s[i];
}
}
return top == 0; //如果top为0,结果为true,否则为false,即不配对
}
队列
1.概念
队列只允许插入在表的一端进行,而删除在表的另一端进行,允许插入的叫队尾,允许删除的叫队头。满足先入先出。
基本操作:
-
队列的初始化:InitQueue(q)
-
入队操作:InQueue(q,x)
初始条件:队列q已存在。
结果:对于存在的队列q,插入一个元素x到队尾。操作成功,返回值为TRUE,否则为false。
-
出队操作:OutQueue(q,x)
初始条件:队列q存在且非空。
操作结果:删除队首元素,并返回其值,队列不变。
C语言中还约定,初始化队列时,空队列 front = rear = -1,当插入新元素时,尾指示针rear加一,队头元素出队时,头指示针加一。
2.循环队列
为避免假溢出,将队列看成首尾相连的循环结构,即规定最后一个单元的后继为第一个单元。但头尾指针的关系不变,即头指针front总是指向队列中实际队头元素的前面一个位置,而队尾指示器rear总是指向队尾元素。
头尾相接循环表的构造方法可以利用数学上的求余运算。
入队时,队尾指针加一操作修改为:sq->rear = (sq->rear+1) % MAXSIZE
出队时,队头指针加一操作修改为:sq->front = (sq->front+1) % MAXSIZE
此时,队满和队空的条件是一样的,即:rear == front 。
解决方法一:少用一个空间,即:(rear + 1) % MAXSIZE == front .
解决方法二:统计队列元素的个数(num),num == 0,队列为空,num == MAXSIZE,则队满。
用法一解决,循环队列的类型定义及基本运算:
typedef struct
{
ElemType elem[MAXSIZE];
int front,rear;
}CSeQueue;
(1)置空队
CSeQueue * InitQueue()
{
CSeQueue *q;
q = (CSeQueue*)malloc(sizeof(CSeQueue));
q->front = q->rear = MAXSIZE-1;
return q;
}
(2)入队
bool InSeQueue(CSeQueue *q,int x)
{
if(( q->rear+1 ) % MAXSIZE == q->front )
{
printf("队满");
return FALSE;
}
else
{
q->rear = (q->rear+1) % MAXSIZE;
q->elem[q->rear] = x;
return TRUE;
}
}
(3)出队
bool OutQueue(CSeQueue *q,int *x)
{
if(q->rear == q->front)
{
printf("队空");
return FALSE;
}
else
{
q->front = (q->front+1) %MAXSIZE;
*x = q->elem[q->front]; //front永远指向队首元素的前一个
return TRUE;
}
}
(4)判队空
bool EmptyQueue(CSeQueue *q)
{
if(q->front == q->rear) return TRUE;
else return FALSE;
}
3.链队列
如果不知道队列所需的最大空间,可以采用链式结构来存储,又叫做“链队列”。
链队列的数据结构类型描述:
typedef struct node
{
int data;
struct node * next;
}Qnode;
typedef struct
{
Qnode * front;
Qnode * rear;
}LQueue;
-
创建一个带头节点的空队
LQueue *InitQueue()
{
Qnode *p;
LQueue *q;
q = (LQueue*)malloc(sizeof(LQueue));
p = (Qnode*)malloc(sizeof(Qnode));
p->next = NULL;
q->front = q->next = p;
return q;
}
-
入队
void inQueue(LQueue *q,int x)
{
Qnode *p;
p = (Qnode*)malloc(sizeof(Qnode));
p->data = x;
p->next = NULL;
q->rear->next = p;
q->rear = p;
}
-
判队空
int Empty_LQueue(LQueue *q)
{
if(q->rear == q->front) return TRUE;
else return FALSE;
}
-
出队
int Out_LQueue(LQueue *q,int *x)
{
Qnode *p;
if(Empty_LQueue(q))
{
printf("队空"); return FALSE;
}
else
{
p = q->front->next;
q->front->next = p->next;
*x=p->data;
free(p);
if(q->front->next == NULL) //只有一个元素时,出队后队空,修改队尾指针。
q->front = q->rear;
return TRUE;
}
}
(部分参考于《数据结构与算法》by 王曙燕)