一、栈
一、栈的概念:
1、定义
栈是仅限在表尾进行插入删除操作的线性表,又被称为先进后出的线性表,简称LIFO
2、栈顶
我们把栈允许插入删除操作的一端称为栈顶
栈底则为另一端,在栈的运用中我们基本上不用关心栈底的问题
二、接口
1、可写接口
1、数据入栈:
栈的插入操作就做入栈,也可以称为进栈
2、数据出栈:
数据的删除操作叫做出栈,也可以称为弹栈
2、只读接口
1、获取栈顶数据 对于一个栈来说只能获取 栈顶 数据,一般不支持获取 其它数据。
2、获取栈元素个数 栈元素个数一般用一个额外变量存储,入栈 时加一,出栈 时减一。这样获取栈元素的时候就不需要遍历整个栈。通过 O(1) 的时间复杂度获取栈元素个数。
3、栈的判空 当栈元素个数为零时,就是一个空栈,空栈不允许 出栈 操作。
三、栈的顺序表的实现
定义:
#define DataType int //数据类型
#define maxn 100005 //栈能存储的最大元素个数
struct Stack { // 栈的结构体
DataType data[maxn]; //栈元素的存储方式
int top; //top即为指针,data[top-1]表示栈顶元素,top==0,则栈为空
};
入栈:
void StackPushStack(struct Stack *p, DataType dt) { //p为一个指针
p->data[ p->top ] = dt; //把传参数的元素放入栈中
p->top = p->top + 1; //栈顶指针自增
}
/*在调用接口前,一定要保证栈顶指针小于栈元素的个数
出栈:
出栈的操作很简单,只需要在栈顶进行
void StackPopStack(struct Stack* stk) {
--stk->top;
}
四、栈的链表的实现
定义:
typedef int DataType; //数据类型(数据域)
struct StackNode; //声明链表结点
struct StackNode {
DataType data;
struct StackNode *next;
};
struct Stack {
struct StackNode *top; // top为栈顶指针,当栈为空时,top==NULL
int size; // 用size来记录栈中元素的个数
};
入栈:
void StackPushStack(struct Stack *p, DataType dt) {
struct StackNode *insertNode = (struct StackNode *) malloc( sizeof(struct StackNode) ); // 用malloc函数生成一个链表结点insertNode
insertNode->next = p->top; //把当前栈顶作为insertNode的后继结点
insertNode->data = dt; //设置传递参数dt
p->top = insertNode; //将insertNode作为新的栈顶
++ p->size; //栈元素+1
}
出栈:
void StackPopStack(struct Stack* p) {
struct StackNode *temp = p->top; //将指针保存到temp中
p->top = temp->next; //将栈顶指针的后继结点作为新的栈顶
free(temp); //释放之前栈顶指针对应的内存
--p->size; //栈元素减一
}
五、两种实现的优缺点
1、顺序表实现 在利用顺序表实现栈时,入栈 和 出栈 的常数时间复杂度低,且 清空栈 操作相比 链表实现 能做到O(1),唯一的不足之处是:需要预先申请好空间,而且当空间不够时,需要进行扩容。
2、链表实现 在利用链表实现栈时,入栈 和 出栈 的常数时间复杂度略高,主要是每插入一个栈元素都需要申请空间,每删除一个栈元素都需要释放空间,且 清空栈 操作是O(n) 的,直接将 栈顶指针 置空会导致内存泄漏。好处就是:不需要预先分配空间,且在内存允许范围内,可以一直 入栈,没有顺序表的限制。
二、队列
一、概念
定义:队列是仅限在一端插入,另一端删除的线性表(先进先出的线性表)
队首:队首为进行删除元素操作的一端
队尾:队尾则只允许元素的插入操作
二、接口
入队:
队列的插入操作叫做入队,将元素从队尾进行插入操作
出队:
队列的删除操作叫做出队,从队首进行元素删除操作
清空队列
队列的清空操作,就是一直出队,直到队列为空,当队首和队尾重合时队列即为空