【栈】
![](https://img-blog.csdnimg.cn/img_convert/12f3a7391f654d7692e0a8cdfbe9b921.png)
栈(stack)是一个特殊的线性表,只能在表尾(一端)进行插入和删除操作的线性表,先进后出(FILO)
栈的基本概念:
表尾称为栈顶,表头称为栈底。
插入元素到栈顶(表尾)的操作,称为入栈。从栈顶删除元素的操作,称为出栈。
例子: 1.比如子弹压入弹夹,先放进的子弹会后打出来。后放进的先打出来。
2.生活中用的碗、盘子,摞在一块,后摞上去的,会先使用。
假设有3个元素a,b,c,入栈顺序是a,b,c,则它们的出栈顺序有几种可能?
![](https://img-blog.csdnimg.cn/img_convert/df7c50a2b37c4d618ac1f898296e2657.png)
【顺序栈】
利用一组地址连续的存储单元存放在栈底到到栈顶的数据元素。
附设一个指针(top)指向当前栈顶的位置
一、顺序栈的实现
顺序栈的结构表示
typedef struct
{
int data[MAX];
int top; //指向栈顶元素的指针
}seq_stack,*seq_t;
2.顺序栈的基本操作
creat_stack(); //创建顺序栈
full_stack(seq_t S); //判断S栈是否已满
empty_stack(seq_t S); //判断S栈是否为空
push_stack(seq_t S,int data); //入栈操作
pop_stack(seq_t S); //出栈操作
clean_stack(seq_t S); //清空栈
destory_stack(seq_t S); //销毁栈
print_stack(seq_t S); //打印栈中元素
main函数
int main(int argc, const char *argv[])
{
seq_t S=creat_stack(); //seq_t是结构体指针类型
push_stack(S,66);
push_stack(S,77);
push_stack(S,88);
printf("压栈后数据:%d\n",S->top);
pop_stack(S);
printf("%d\n",S->top);
print_stack(S);
return 0;
}
2.1创建顺序栈
![](https://img-blog.csdnimg.cn/img_convert/adb593ce0d604d2e80ad37bd39e10ac7.png)
//创建顺序栈
seq_t creat_stack()
{
seq_t p = (seq_t)malloc(sizeof(seq_stack));
if(p==NULL)
{
printf("内存申请失败\n");
return NULL;
}
printf("内存申请成功\n");
p->top=-1; //最开始指向-1
memset(p->data,0,sizeof(p->data));
return p;
}
2.2判断顺序栈是否已满
![](https://img-blog.csdnimg.cn/img_convert/717642e30ea7412dadb94c77281f06e4.png)
//判满
int full_stack(seq_t S)
{
if(S==NULL)
{
printf("入参为空\n");
return -1;
}
return S->top==MAX-1?1:0; //判断栈顶指针是否已经指向栈顶元素下标
}
2.3判断顺序栈是否为空
int empty_stack(seq_t S)
{
if(S==NULL)
{
printf("入参为空\n");
return -1;
}
return S->top==-1?1:0; //指向-1则为空
}
2.4入栈
void push_stack(seq_t S,int data)
{
if(S==NULL)
{
printf("入参为空\n");
return;
}
if(!full_stack(S)) //栈不满,才可入栈
{
S->data[++(S->top)]=data; //指针先上移,将数据放入 <=> S->top++; S->data[S->top]=data;
}
}
2.5出栈
void pop_stack(seq_t S)
{
if(S==NULL)
{
printf("入参为空\n");
return;
}
if(!empty_stack(S)) //栈不为空,则可出栈
{
printf("出栈元素为:%d\n",S->data[(S->top)--]);
}
}
2.6打印栈中数据
栈是从栈顶开始输出元素。因此遍历直接从栈顶指针所指向下标(s->top)打印即可
//打印
void print_stack(seq_t S)
{
if(S==NULL)
{
printf("入参为空\n");
return;
}
int i;
for(i=S->top;i>=0;i--) //栈是从栈顶开始输出元素。因此遍历直接从栈指针所指向下标打印即可
{
printf("%d\n",S->data[i]);
}
//S->top=S->top-1;
}
2.7清空栈
//清空栈
void clean_stack(seq_t S)
{
if(S==NULL)
{
printf("入参为空\n");
return;
}
if(empty_stack(S))
{
printf("栈为空\n");
return;
}
S->top=-1; //顺序栈因为用数组实现,清空栈直接指向-1就行
}
2.8销毁栈
//销毁栈
void destory_stack(seq_t *S)
{
if(S==NULL||*S==NULL) //seq_t是一个结构体指针,因此对于S来说是一个二级指针,*S是解引用操作
{
printf("入参为空\n");
return;
}
free(*S);
*S=NULL;
}
上述 if(S==NULL || *S==NULL) 当中,S和*S位置不能互换。因为在对S解引用(*s)之前,要保证有二级指针才行。
这里用二级指针是因为main函数当中,P是一个指针,要对它销毁,要把它的地址传进销毁函数。(值传递和地址传递的区别)
【链栈】
栈的链式存储结构简称为链栈.。链栈是通过单链表来实现的。
采用链式存储,便于结点的插入和删除操作。
每次入栈一个元素,向链表中加入一个结点(相当于头插法),出栈一个元素,删除一个结点
![](https://img-blog.csdnimg.cn/img_convert/5869605ee1074f398ae57cf6dab54aa2.png)
栈顶应该放在链首还是链尾合适?
因为栈具有“后进先出”的特点,所以每次在链表的尾部进行插入和删除,就要遍历整个链表来找到尾节点。
而在头部进行插入和删除时,只需根据头指针即可找到链表的首元素结点。不需要遍历链表。
所以链栈的出,入栈通过对链表进行头删和头插来实现。
二、链栈的实现
1、结构的结构表示
typedef struct node
{
int data;
struct node* next;
}node,*node_t;
//头结点和单独定义拿出来
typedef struct
{
int count;
node_t top; //栈顶指针
}top_t;
2.链栈的基本操作:
create_links(); //创建链栈
create_node(int data); //创建结点
empty_links(top_t *P); //判空
push_links(top_t *P,int data); //入栈
pop_links(top_t *P); //出栈
print_link(top_t *P); //打印栈中元素
clean_links(top_t *P); //清空栈
destory_links(top_t **P); //销毁栈
main函数
int main(int argc, const char *argv[])
{
top_t *P=create_links();
push_links(P,66);
push_links(P,77);
push_links(P,88);
pop_links(P);
print_link(P);
return 0;
}
2.1创建链栈
//创建链栈,创建一个栈顶指针
top_t *create_links()
{
top_t * P=(top_t *)malloc(sizeof(top_t));
if(P==NULL)
{
return NULL;
}
P->count=0;
P->top=NULL;
return P;
}
2.2创建结点
node_t create_node(int data)
{
node_t n=(node_t)malloc(sizeof(node));
if(n==NULL)
{
return NULL;
}
n->data=data; //赋予新结点数据
return n;
}
2.3判空
//判空
int empty_links(top_t *P)
{
if(P==NULL)
{
printf("入参为空\n");
return -1;
}
return P->top==NULL?1:0; //头结点是否指向空
}
2.4入栈
void push_links(top_t *P,int data)
{
if(P==NULL)
{
printf("入参为空\n");
return ;
}
node_t new=create_node(data); //创建新结点
new->next=P->top; //头插
P->top=new;
P->count++;
}
2.5出栈
//头删,弹栈
void pop_links(top_t *P)
{
if(P==NULL)
{
printf("入参为空\n");
return;
}
node_t dele = P->top; //dele指向第一个结点
if(!empty_links(P)) //保证栈中有元素
{
P->top=dele->next; //头结点指向第一个结点的后继结点
free(dele); //删除第一个结点
}
P->count--;
}
2.6打印链栈中数据
//打印
void print_link(top_t *P)
{
if(P==NULL)
{
printf("入参为空\n");
return ;
}
if(empty_links(P))
{
printf("栈为空\n");
return;
}
node_t p=P->top; //从哪个结点开始打印
while(p!=NULL)
{
printf("%d\n",p->data);
p=p->next;
}
}
2.7清空(只留头结点)
void clean_links(top_t *P)
{
if(P==NULL)
{
printf("入参为空\n");
return;
}
if(empty_links(P))
{
printf("栈为空\n");
return;
}
node_t dele=P->top; //第一个结点
node_t q; //辅助变量
while(dele!=NULL)
{
//q保存要删除结点的下一个结点
q=dele->next;
free(dele);
//让dele往后走一位
dele=q;
}
P->count=0;
}
2.8销毁
//销毁
void destory_links(top_t **P)
{
if(P==NULL)
{
printf("入参为空\n");
return;
}
if(empty_links(*P))
{
printf("栈为空\n");
return;
}
clean_links(*P);
free(*P);
*P=NULL;
}
【队列】
【链式队列】
一、链队的实现
1.链式队列的结构表示
//链队的结点类型
typedef struct node
{
int data;
struct node *next; //结点的指针域
}node,*node_t;
//指向链队头和尾的指针
typedef struct
{
node_t front; //头指针
node_t rear; //尾指针
}link_que,*que;
2.链式队列的基本操作
node_t create_linkque(); //创建链表
que create_que(node_t H); //创建队头指针和队尾指针
node_t create_node(int data); //创建结点
int empty_linkque(que Q,node_t H); //判空
void push_linkque(que Q,int data,node_t H); //入队
void pop_linkque(que Q,node_t H); //出队
void print_linkque(que Q,node_t H); //打印
void clean_linkeque(que Q,node_t H); //清空
void deatory_que(que *Q,node_t *H); //销毁
main函数
int main(int argc, const char *argv[])
{
node_t H=create_linkque();
que Q=create_que(H);
push_linkque(Q,23,H);
push_linkque(Q,24,H);
pop_linkque(Q,H);
print_linkque(Q,H);
deatory_que(&Q,&H);
print_linkque(Q,H);
return 0;
}
2.1 创建链表(即链队)
//创建链表
node_t create_linkque()
{
node_t H=(node_t)malloc(sizeof(node));
if(H==NULL)
{
printf("内存申请失败\n");
return NULL;
}
H->data=-1;
H->next=NULL;
return H;
}
2.2 创建队头指针和队尾指针
//创建队头指针和队尾指针
que create_que(node_t H)
{
que Q=(que)malloc(sizeof(link_que));
if(Q==NULL)
{
printf("内存申请失败\n");
return NULL;
}
Q->front=H;
Q->rear=H;
return Q;
}
2.3 创建结点
node_t create_node(int data)
{
node_t p=(node_t)malloc(sizeof(node));
if(p==NULL)
{
printf("内存申请失败\n");
return NULL;
}
p->data=data;
p->next=NULL;
return p;
}
2.4 判空
int empty_linkque(que Q,node_t H)
{
if(Q==NULL)
{
printf("入参为空\n");
return -1;
}
return Q->front==Q->rear&&Q->rear==H?1:0;
}
2.5 入队(尾插)
void push_linkque(que Q,int data,node_t H)
{
if(Q==NULL)
{
printf("入参为空\n");
return;
}
node_t p=create_node(data);
//当链队为空的情况
if(empty_linkque(Q,H))
{
//Q->fornt=H,因为链队为空的时候Q->front和Q->next都指向H
Q->front->next=p;
Q->front=p;
Q->rear=p;
}
else
{
Q->rear->next=p;
Q->rear=p;
}
}
2.6 出队(头删)
void pop_linkque(que Q,node_t H)
{
if(Q==NULL)
{
printf("入参为空\n");
return;
}
if(empty_linkque(Q,H))
{
printf("链队为空\n");
return;
}
if(Q->rear==Q->front&&Q->rear!=H)
{
//当链队中只有一个元素的时候
printf("出队的元素为%d\n",Q->front->data);
Q->front=H;
Q->rear=H;
free(H->next);
H->next=NULL;
return;
}
printf("出队的元素为%d\n",Q->front->data);
//保存要删除的结点
node_t dele =Q->front;
//Q->front指向第二个结点
Q->front=Q->front->next;
//头结点重新指向,现在链队中的第一个结点
H->next=Q->front;
free(dele);
}
2.7 打印
void print_linkque(que Q,node_t H)
{
if(Q==NULL)
{
printf("入参为空\n");
return;
}
if(!empty_linkque(Q,H))
{
node_t p=Q->front;
while(p!=NULL)
{
printf("%d\n",p->data);
p=p->next;
}
}
}
2.8 清空
//清空
void clean_linkeque(que Q,node_t H)
{
if(Q==NULL)
{
printf("入参为空\n");
return;
}
while(!empty_linkque(Q,H))
{
pop_linkque(Q,H);
}
}
2.9 销毁
void deatory_que(que *Q,node_t *H)
{
if(Q==NULL||*Q==NULL||H==NULL||*H==NULL)
{
printf("error\n");
return;
}
clean_linkeque(*Q,*H);
free(*Q);
free(*H);
//实现实参内的Q和H置空
*Q=NULL;
*H=NULL;
}