1.栈
栈是限定仅在表尾进行插入或删除操作的线性表。(受到限制的线性表)
是一种先进后出,后进先出(LIFO)的数据结构
对栈来说,表尾端有其特殊含义,称为栈顶,相应地,表头端称为栈底。不含元素的空表称为空栈。
2.顺序栈
struct Stack
{
int *base;//栈底指针
int top;//既是元素个数,也是下一个插入元素的下标
//int* top;//指针—指针=个数
stacksize;
};
stack.h
#pragma once
//表尾叫栈顶 表头叫栈底
typedef int ELEM_TYPE;
#define INIT_SIZE 10
typedef struct Stack
{
ELEM_TYPE* base;//接收malloc从堆内申请的连续存储单元
int top;//栈顶指针 即表示下一个元素存储的下标,还表示有效数据长度
int stacksize;//当前最大元素个数
}Stack,*PStack;
//初始化函数
void Init_Stack(PStack ps);
//入栈
bool Push(PStack ps,ELEM_TYPE val);
//出栈(如果出栈成功,需要知道出栈的值,如果不成功,则无所谓)
bool Pop(PStack ps,ELEM_TYPE *rtval);//rtval是输出参数,帮助返回其他信息
//获取栈顶元素(成功,则获取该值)
bool Top(PStack ps,ELEM_TYPE *rtval);
//获取有效数据个数
int Get_length(PStack ps);
//判空
bool IsEmpty(PStack ps);
//判满
bool IsFull(PStack ps);
//扩容函数//
void Inc(PStack ps);
//清空
void Clear(PStack ps);
//摧毁
void Destroy(PStack ps);
//打印
void Show(PStack ps);
stack.cpp
#include"stack.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
//初始化函数
void Init_Stack(PStack ps)
{
assert(ps!=nullptr);
ps->base=(ELEM_TYPE*)malloc(INIT_SIZE*sizeof(ELEM_TYPE));
assert(pa->base!=NULL);
ps->top=0;//保存的是有效数据长度,所以初始化时给0
ps->stacksize=INIT_SIZE;
}
//入栈
bool Push(PStack ps,ELEM_TYPE val)
{
assert(ps!=NULL);
if(ps==NULL) return false;
//1.判满 如果满,需要扩容
if(IsFull(ps))
{
Inc(ps);
}
//2.直接入栈
ps->base[ps->top++]=val;
//3.入完栈之后 不要忘了top++ 有效数据个数+1
return true;
}
//出栈(如果出栈成功,需要知道出栈的值,如果不成功,则无所谓)
bool Pop(PStack ps,ELEM_TYPE *rtval)//rtval是输出参数,帮助返回其他信息
{
assert(ps!=NULL);
if(IsEmpty(ps)) return false;
*rtval=ps->base[--ps->top];
return true;
}
//获取栈顶元素(成功,则获取该值)
bool Top(PStack ps,ELEM_TYPE *rtval)
{
assert(ps!=NULL);
if(IsEmpty(ps)) return false;
*rtval=ps->base[ps->top-1];
return true;
}
//获取有效数据个数
int Get_length(PStack ps)
{
assert(ps!=NULL);
return ps->top;
}
//判空
bool IsEmpty(PStack ps)
{
assert(ps!=NULL);
return ps->top=0;
}
//判满
bool IsFull(PStack ps)
{
assert(ps!=NULL);
return ps->top==ps->stacksize;
}
//扩容函数//2倍扩容
//realloc 1.原先空间后面足够,直接续上 2.原先空间后面不足,重新开辟一块内存,再把原先空间有效数据拷贝过去,最后释放原先空间内存 3.原先空间后面不足,内存也不足,报错
void Inc(PStack ps)
{
assert(ps!=NULL);
ps->base=(ELEM_TYPE*)realloc(ps->base,sizeof(ELEM_TYPE)*ps->stacksize*2);
assert(ps->base!=NULL);
if(ps==NULL) return false;
ps->stacksize*=2;
}
//清空
void Clear(PStack ps)
{
assert(ps!=NULL);
ps->top=0;//只需要认为里面空间的元素值都是无效值即可
}
//摧毁
void Destroy(PStack ps)
{
assert(ps!=NULL);
free(ps);
ps->base=NULL;
//ps->top=ps->stacksize=0;
}
//打印
void Show(PStack ps)
{
assert(ps!=NULL);
for(int i=0;i<ps->top;++i)
{
printf("%d ",ps->base[i]);
}
printf("\n");
}
3.链式栈
lstack.h
#pragma once
//链式栈的结构体定义
typedef int ELEM_TYPE;
typedef struct LStack
{
ELEM_TYPE data;//数据域
LStack* next;//指针域
}LStack,*PLStack;
//初始化函数
void Init_LStack(PLStack ps);
//入栈
bool Push(PLStack ps,ELEM_TYPE val);
//出栈(如果出栈成功,需要知道出栈的值,如果不成功,则无所谓)
bool Pop(PLStack ps,ELEM_TYPE *rtval);//rtval是输出参数,帮助返回其他信息
//获取栈顶元素(成功,则获取该值)
bool Top(PLStack ps,ELEM_TYPE *rtval);
//获取有效数据个数
int Get_length(PLStack ps);
//判空
bool IsEmpty(PLStack ps);
//清空
void Clear(PLStack ps);
//摧毁
void Destroy(PLStack ps);
//打印
void Show(PLStack ps);
lstack.cpp
#include"lstack.h"
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
//初始化函数
void Init_LStack(PLStack ps)
{
assert(ps!=NULL);
ps->next=NULL;
}
//入栈//头插
bool Push(PLStack ps,ELEM_TYPE val)
{
assert(ps!=NULL);
//1.创建新节点
LStack* pnewnode=(LStack*)malloc(sizeof(LStack));
assert(pnewnode!=NULL);
pnewnode->data=val;
pnewnode->next=NULL;//一般来说,这行代码可以不用写
//2.找到合适的插入位置
//头插 所以位置已知
//3.直接插入
pnewnode->next=ps->next;
ps->next=pnewnode;
return true;
}
//出栈//头删
bool Pop(PLStack ps,ELEM_TYPE *rtval)//rtval是输出参数,帮助返回其他信息
{
assert(ps!=NULL&&rtval!=NULL);
if(IsEmpty(ps))
{
return false
}
*rtval=ps->next->data;//当确保第一个有效节点存在的话,直接先将一会出栈的值通过trval带出去
LStack *p=ps->next;//指针p指向待删除节点
ps->next=p->next;
free(p);
return true;
}
//获取栈顶元素(成功,则获取该值)
bool Top(PLStack ps,ELEM_TYPE *rtval)
{
assert(ps!=NULL&&rtval!=NULL);
if(IsEmpty(ps))
{
return false;
}
*rtval=ps->next->data;
return true;
}
//获取有效数据个数
int Get_length(PLStack ps)
{
assert(ps!=NULL);
int count=0;
for(LStack *p=ps->next;p!=NULL;p=p->next)
{
count++;
}
return count;
}
//判空
bool IsEmpty(PLStack ps)
{
assert(ps!=NULL);
return ps->next=NULL;
}
//清空
void Clear(PLStack ps)
{
assert(ps!=NULL);
Destroy(ps);
}
//摧毁
void Destroy1(PLStack ps)
{
assert(ps!=NULL);
while(ps->next!=NULL)
{
LStack *p=ps->next;
ps->next=p->next;
free(p);
}
}
void Destroy2(PLStack ps)
{
assert(ps!=NULL);
LStack *p=ps->next;
LStack* q;
while(p!=NULL)
{
q=p->next;
free(p);
p=q;
}
ps->next=NULL;
}
//打印
void Show(PLStack ps)
{
assert(ps!=NULL);
for(LStack *p=ps->next;p!=NULL;p=p->next)
{
printf("%d",p->data);
}
printf("\n");
}
4.队列
队列是一种先进先出(FIFO)的线性表。只允许在表的一端进行插入,而在另一端删除元素。没有有效数据节点的队列称作空队。
在队列中,允许插入的一端叫做队尾,允许删除的一端称为队头。
队列在程序设计中也经常出现,一个最经典的例子就是操作系统中的作业排队。
难点:
1)如何让入队和出队的时间复杂度都为O(1)?
将顺序队列想象成一个环
2)判空和判满条件冲突,都是队头—队尾
a)加个标志,保存有效值个数
b)在队尾加一个空间,不在保存有效值,而是做一个标记【判空:队头=队尾;判满:队尾+1=队头】【判满通用公式:(队尾+1)%MAXSIZE==队头】
3)如何获取循环队列的有效个数
(队尾-队头+MAXSIZE)%MAXSIZE==有效个数
6.顺序队列(循环队列)
queue.h
#pragma once
#define MAXSIZE 100
//循环队列结构体定义:
typedef int ELEM_TYPE;
typedef struct Queue
{
ELEM_TYPE *base;
int front;
int tail;
//int length;
}Queue, *PQueue;
//增删改查
//初始化
void Init_queue(Queue* pq);
//入队
bool Push(PQueue pq, ELEM_TYPE val);
//出队(如果出队操作成功,则还要返回出队的值)
bool Pop(PQueue pq, ELEM_TYPE *rtval);
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool Top(PQueue pq, ELEM_TYPE *rtval);
//获取有效值个数
int Get_length(PQueue pq);
//判空
bool IsEmpty(PQueue pq);
//判满
bool IsFull(PQueue pq);
//清空
void Clear(PQueue pq);
//销毁
void Destroy(PQueue pq);
//打印
void Show(PQueue pq);
queue.cpp
#include "queue.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//增删改查
//初始化
void Init_queue(Queue* pq)
{
pq->base = (ELEM_TYPE *)malloc(sizeof(ELEM_TYPE) * MAXSIZE);
assert(pq->base != NULL);
pq->front = pq->tail = 0;
}
//入队 队尾入
bool Push(PQueue pq, ELEM_TYPE val)
{
assert(pq!=NULL);
if(IsFull(pq))
return false;
pq->base[pq->tail] = val;
//pq->tail += 1; error
pq->tail = (pq->tail + 1)%MAXSIZE;//防止pq->tail+1越界
return true;
}
//出队 队头出 (如果出队操作成功,则还要返回出队的值)
bool Pop(PQueue pq, ELEM_TYPE *rtval)
{
assert(pq!=NULL&&rtval!=NULL);
if(IsEmpty(pq))
return false;
*rtval = pq->base[pq->front];
//pq->front++; //error
pq->front = (pq->front+1)%MAXSIZE;
return true;
}
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool Top(PQueue pq, ELEM_TYPE *rtval)
{
assert(pq!=NULL&&rtval!=NULL);
if(IsEmpty(pq))
return false;
*rtval = pq->base[pq->front];
return true;
}
//获取有效值个数
int Get_length(PQueue pq)
{
assert(pq!=NULL);
return (pq->tail - pq->front + MAXSIZE) % MAXSIZE;
}
//判空 队尾==队头 则空
bool IsEmpty(PQueue pq)
{
return pq->front == pq->tail;
}
//判满 队尾向后跑一步遇到了队头 则满
bool IsFull(PQueue pq)
{
return (pq->tail+1)%MAXSIZE == pq->front;
}
//清空
void Clear(PQueue pq)
{
pq->front = pq->tail = 0;
}
//销毁
void Destroy(PQueue pq)
{
free(pq->base);
pq->base = NULL;
pq->front = pq->tail;
}
//打印
void Show(PQueue pq)
{
for(int i=pq->front; i!=pq->tail; i=(i+1)%MAXSIZE)
{
printf("%d ", pq->base[i]);
}
printf("\n");
}
7.链式队列
当是一个空队的时候,队头指针和队尾指针都指向NULL
当仅有一个有效数据节点的时候,队头指针和队尾指针都指向它
lqueue.h
#pragma once
//链式队列的结构体设计
typedef int ELEM_TYPE;
//有效数据节点结构体设计
typedef struct Lqueue
{
ELEM_TYPE data;//数据域
Lqueue *next;//指针域
}Lqueue, *PLqueue;
//头结点的结构体重新设计一下
typedef struct Head
{
Lqueue *front;//指针域 指向第一个有效数据节点
Lqueue *tail; //指针域 指向最后一个有效数据节点
}Head, *PHead;
//增删改查
//初始化
void Init_lqueue(PHead pq);
//入队
bool Push(PHead pq, ELEM_TYPE val);
//出队(如果出队操作成功,则还要返回出队的值)
bool Pop(PHead pq, ELEM_TYPE *rtval);
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool Top(PHead pq, ELEM_TYPE *rtval);
//获取有效值个数
int Get_length(PHead pq);
//判空
bool IsEmpty(PHead pq);
//清空
void Clear(PHead pq);
//销毁
void Destroy(PHead pq);
//打印
void Show(PHead pq);
lqueue.cpp
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"lqueue.h"
//初始化
void Init_lqueue(PHead pq)
{
assert(pq!=NULL);
pq->front=NULL;
pq->tail=NULL;
}
//入队 //尾插
bool Push(PHead pq, ELEM_TYPE val)
{
assert(pq!=NULL);
//1.创建新节点
Lqueue* pnewnode=(Lqueue*)malloc(sizeof(Lqueue));
assert(pnewnode!=NULL);
pnewnode->data=val;
//2.找到合适的插入位置
//3.插入
if(pq->front==NULL)
{
pq->front=pq->tail=pnewnode;
pnewnode->next=NULL;
}
else
{
pnewnode->next=pq->tail->next;
pq->tail->next=pnewnode;
pq->tail=pnewnode;
}
return true;
}
//出队(如果出队操作成功,则还要返回出队的值) //头删
bool Pop(PHead pq, ELEM_TYPE *rtval)
{
assert(pq!=NULL&&rtval!=NULL);
//1.判空
if(Isemtry(pq))
{
return false;
}
*rtavl=pq->front->data;
//2.找到待删节点的前驱
//3.跨越指向 删除
if(pq->front->next==NULL)//仅有一个有效节点
{
Lqueue *p=pq->front;
free(p);
p=NULL;
pq->front=pq->tail=NULL;
}
else//不止一个有效节点s
{
Lqueue *p=pq->front;
pq->front=p->next;
free(p);
p=NULL;
}
return true;
}
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool Top(PHead pq, ELEM_TYPE *rtval);
{
assert(pq!=NULL&&rtval!=NULL);
//1.判空
if(Isemtry(pq))
{
return false;
}
*rtavl=pq->front->data;
return true;
}
//获取有效值个数
int Get_length(PHead pq)
{
assert(pq!=NULL);
int count=0;
for(Lqueue* p=pq->front;p!=NULL;p=p->next)
{
count++;
}
return count;
}
//判空
bool IsEmpty(PHead pq);
{
return pq->front=NULL;
}
//清空
void Clear(PHead pq)
{
Destroy(pq);
}
//销毁
void Destroy(PHead pq)
{
assert(pq!=NULL);
Lqueue *p=pq->front;
Lqueue *q=NULL;
while(p!=NULL)
{
q=p->next;
free(p);
p=q;
}
pq->front=pq->tail=NULL;
}
//打印
void Show(PHead pq)
{
assert(pq!=NULL);
or(Lqueue* p=pq->front;p!=NULL;p=p->next)
{
printf("%d",pq->data);
}
printf("\n");
}
(1).如何用两个栈实现一个队列
规则:
进:往栈1里进
出:如果栈2不空,则从栈2出
如果栈2空,则先讲栈1全部数据压入栈2,此时栈2不空,则再从栈2出
two_stack_to_queue.h
#pragma once
#include "stack.h"
typedef struct TSTqueue
{
Stack s1;
Stack s2;
}TSTqueue, *PTSTqueue;
//增删改查
//初始化
void my_Init_queue(PTSTqueue pq);
//入队
bool my_Push(PTSTqueue pq, ELEM_TYPE val);
//出队(如果出队操作成功,则还要返回出队的值)
bool my_Pop(PTSTqueue pq, ELEM_TYPE *rtval);
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool my_Top(PTSTqueue pq, ELEM_TYPE *rtval);
//获取有效值个数
int my_Get_length(PTSTqueue pq);
//判空
bool my_IsEmpty(PTSTqueue pq);
//清空
void my_Clear(PTSTqueue pq);
//销毁
void my_Destroy(PTSTqueue pq);
//打印
void my_Show(PTSTqueue pq);
two_stack_to_queue.cpp
#include "two_stack_to_queue.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//增删改查
//初始化
void my_Init_queue(PTSTqueue pq)
{
Init_Stack(&pq->s1);
Init_Stack(&pq->s2);
}
//入队
bool my_Push(PTSTqueue pq, ELEM_TYPE val)
{
//assert
return Push(&pq->s1, val);
}
//出队(如果出队操作成功,则还要返回出队的值)
bool my_Pop(PTSTqueue pq, ELEM_TYPE *rtval)
{
//assert
if(my_IsEmpty(pq))
{
return false;
}
//如果栈2为空 则把栈1的值全部导入栈2 再从栈2出
//如果栈2不为空 则正常从栈2出一个
if(IsEmpty(&pq->s2))
{
while(!IsEmpty(&pq->s1))//如果栈1不空 则循环的形式 从栈1里取值放到栈2
{
int tmp;
Pop(&pq->s1, &tmp);//s1出的值 在tmp里保存着
Push(&pq->s2, tmp);//将tmp的值 再入到栈2
}
//while循环执行完 这时 所有的值都在栈2
return Pop(&pq->s2, rtval);
}
else
{
return Pop(&pq->s2, rtval);
}
}
//获取队头元素值(如果Top函数执行成功,则还要返回队头的值)
bool my_Top(PTSTqueue pq, ELEM_TYPE *rtval)
{
//assert
if(my_IsEmpty(pq))
{
return false;
}
//如果栈2为空 则把栈1的值全部导入栈2 再从栈2获取栈顶元素值
//如果栈2不为空 则正常从栈2获取栈顶元素值
if(IsEmpty(&pq->s2))
{
while(!IsEmpty(&pq->s1))//如果栈1不空 则循环的形式 从栈1里取值放到栈2
{
int tmp;
Pop(&pq->s1, &tmp);//s1出的值 在tmp里保存着
Push(&pq->s2, tmp);//将tmp的值 再入到栈2
}
//while循环执行完 这时 所有的值都在栈2
return Top(&pq->s2, rtval);
}
else
{
return Top(&pq->s2, rtval);
}
}
//获取有效值个数
int my_Get_length(PTSTqueue pq)
{
return Get_length(&pq->s1) + Get_length(&pq->s2);
}
//判空 只有栈1和栈2都为空的时候 我自身才认为 为空
bool my_IsEmpty(PTSTqueue pq)
{
if(IsEmpty(&pq->s1) && IsEmpty(&pq->s2))
{
return true;
}
return false;
}
//清空
void my_Clear(PTSTqueue pq)
{
Clear(&pq->s1);
Clear(&pq->s2);
}
//销毁
void my_Destroy(PTSTqueue pq)
{
Destroy(&pq->s1);
Destroy(&pq->s2);
}
//打印
void my_Show(PTSTqueue pq)
{
int tmp = Get_length(&pq->s2);//3
for(int i=tmp-1; i>=0; i--)
{
printf("%d ", pq->s2.base[i]);
}
Show(&pq->s1);
}
(2).如何用两个队列实现一个栈
规则:
入:向queue2入
出:如果queue2不为空,将queue2里的值全部导入queue1里(仅剩下一个),此时剩下的这个就是我想要的,出出来;如果queue2为空,则代表着值全部在queue1里,则将queue1里的值全部导入queue2里(仅剩下一个),此时queue1剩下的这个就是我想要的,出出来。
two_queue_to_stack.h
#pragma once
#include "queue.h"
typedef struct TQTStack
{
Queue q1;
Queue q2;
}TQTStack, *PTQTStack;
//初始化函数
void my_Init_Stack(PTQTStack ps);
//入栈(相当于顺序表的尾插)
bool my_Push(PTQTStack ps, ELEM_TYPE val);
//出栈(如果出栈操作成功,需要告诉我出栈的值是多少,如果不成功,则无所谓)
bool my_Pop(PTQTStack ps, ELEM_TYPE *rtval);//rtval是输出参数 帮助函数返回其他信息
//获取栈顶元素(不仅需要告诉我此操作是否成功,还要告诉我如果成功,值是多少)
bool my_my_Top(PTQTStack ps, ELEM_TYPE *rtval);
//获取有效数据个数
int my_Get_length(PTQTStack ps);
//判空
bool my_IsEmpty(PTQTStack ps);
//判满
bool my_IsFull(PTQTStack ps);
//清空
void my_Clear(PTQTStack ps);
//销毁
void my_Destroy(PTQTStack ps);
//打印
void my_Show(PTQTStack ps);
two_queue_to_stack.cpp
#include "two_queue_to_stack.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//初始化函数
void my_Init_Stack(PTQTStack ps)
{
Init_queue(&ps->q1);
Init_queue(&ps->q2);
}
//入栈(相当于顺序表的尾插)
bool my_Push(PTQTStack ps, ELEM_TYPE val)
{
return Push(&ps->q2, val);
}
//出栈(如果出栈操作成功,需要告诉我出栈的值是多少,如果不成功,则无所谓)
bool my_Pop(PTQTStack ps, ELEM_TYPE *rtval)//rtval是输出参数 帮助函数返回其他信息
{
//assert
if(my_IsEmpty(ps))
return false;
if(IsEmpty(&ps->q2))//queue2 为空
{
int size = Get_length(&ps->q1);
while(size > 1)
{
int tmp;
Pop(&ps->q1, &tmp);
Push(&ps->q2, tmp);
size--;
}
return Pop(&ps->q1, rtval);
}
else//queue2 不为空
{
int size = Get_length(&ps->q2);
while(size > 1)
{
int tmp;
Pop(&ps->q2, &tmp);
Push(&ps->q1, tmp);
size--;
}
return Pop(&ps->q2, rtval);
}
}
//获取栈顶元素(不仅需要告诉我此操作是否成功,还要告诉我如果成功,值是多少)
bool my_my_Top(PTQTStack ps, ELEM_TYPE *rtval)
{
//assert
if(my_IsEmpty(ps))
return false;
if(IsEmpty(&ps->q2))//queue2 为空
{
int size = Get_length(&ps->q1);
while(size > 1)
{
int tmp;
Pop(&ps->q1, &tmp);
Push(&ps->q2, tmp);
size--;
}
Top(&ps->q1, rtval);
int tmp2;
Pop(&ps->q1, &tmp2);
Push(&ps->q2, tmp2);
}
else//queue2 不为空
{
int size = Get_length(&ps->q2);
while(size > 1)
{
int tmp;
Pop(&ps->q2, &tmp);
Push(&ps->q1, tmp);
size--;
}
Top(&ps->q2, rtval);
int tmp2;
Pop(&ps->q2, &tmp2);
Push(&ps->q1, tmp2);
}
return true;
}
//获取有效数据个数
int my_Get_length(PTQTStack ps)
{
return Get_length(&ps->q1) + Get_length(&ps->q2);
}
//判空
bool my_IsEmpty(PTQTStack ps)
{
if(IsEmpty(&ps->q1) && IsEmpty(&ps->q2))
return true;
return false;
}
//判满
bool my_IsFull(PTQTStack ps)
{
//简单来说 认为q2 满了就整体满了
return IsFull(&ps->q2);
}
//清空
void my_Clear(PTQTStack ps)
{
Clear(&ps->q1);
Clear(&ps->q2);
}
//销毁
void my_Destroy(PTQTStack ps)
{
Destroy(&ps->q1);
Destroy(&ps->q2);
}
//打印
void my_Show(PTQTStack ps)
{
Show(&ps->q1);
Show(&ps->q2);
}