栈的概念与结构:
一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈的实现:
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
void StackInit(SNode* phead)
{
assert(phead);
STDataType* tmp= (STDataType*)malloc(sizeof(STDataType) * 4);
if (tmp == NULL)
{
perror("malloc fail");
return;
}
phead->top = tmp;
tmp = NULL;
phead->size = 0;
phead->capatipy = 4;
}
void StackPush(SNode* phead, STDataType x)
{
assert(phead);
if (phead->capatipy == phead->size)
{
STDataType* tmp = (STDataType*)realloc(phead->top,sizeof(STDataType) * 2*(phead->capatipy));
if (tmp == NULL)
{
perror("raalloc fail");
return;
}
phead->top = tmp;
tmp == NULL;
phead->capatipy *= 2;
}
phead->top[phead->size] = x;
phead->size++;
}
void StackPop(SNode* phead)
{
assert(phead);
assert(!StackEmpty(phead));
phead->size--;
}
void StackDestroy(SNode* phead)
{
assert(phead);
free(phead->top);
phead->top = NULL;
phead->size = 0;
phead->capatipy = 0;
}
int StackSize(SNode* phead)
{
assert(phead);
return phead->size;
}
bool StackEmpty(SNode* phead)
{
assert(phead);
return phead->size == 0;
}
int StackTop(SNode* phead)
{
return phead->top[phead->size - 1];
}
队列的概念及结构队列:
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
void QPush(Que* pq, QDataType x)
{
assert(pq);
QueNode* newnode = (QueNode*)malloc(sizeof(QueNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
newnode = NULL;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
newnode = NULL;
}
pq->size++;
}
void QPop(Que* pq)
{
assert(pq);
assert(!QEmpty(pq));
QueNode* pop = pq->phead;
pq->phead = pop->next;
free(pop);
pop = NULL;
pq->size--;
}
int QSize(Que* pq)
{
assert(pq);
return pq->size;
}
bool QEmpty(Que* pq)
{
assert(pq);
return pq->phead == NULL;
}
void QDestory(Que* pq)
{
while (pq->phead)
{
QPop(pq);
}
pq->phead = pq->ptail = NULL;
}
QueNode* QueFront(Que* pq)
{
assert(pq);
assert(!QEmpty(pq));
return pq->phead->val;
}
QueNode* QueBack(Que* pq)
{
assert(pq);
assert(!QEmpty(pq));
return pq->ptail->val;
}
既然栈的特点是后进先出,而队列的特点是先进先出,那是否可以将俩者相互转换,用栈去实现队列,用队列去实现栈?
用栈去实现队列:
typedef struct Stack
{
int* top;
int capatipy;
int size;
}ST;
typedef struct
{
ST* ps1;
ST* ps2;
} MyQueue;
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
if (obj == NULL)
{
perror("malloc fail");
return NULL;
}
obj->ps1 = (ST*)malloc(sizeof(ST));
if (obj->ps1 == NULL)
{
perror("malloc fail");
return NULL;
}
obj->ps2 = (ST*)malloc(sizeof(ST));
if (obj->ps2 == NULL)
{
perror("malloc fail");
return NULL;
}
obj->ps1->top = (int*)malloc(sizeof(int)*4);
if (obj->ps1->top == NULL)
{
perror("malloc fail");
return NULL;
}
obj->ps2->top = (int*)malloc(sizeof(int)*4);
if (obj->ps2->top == NULL)
{
perror("malloc fail");
return NULL;
}
obj->ps1->size = obj->ps2->size = 0;
obj->ps1->capatipy = obj->ps2->capatipy = 4;
return obj;
}
bool myQueueEmpty(MyQueue* obj)
{
return obj->ps1->size == 0 && obj->ps2->size == 0;
}
void STPush(ST* ps, int x)
{
assert(ps);
if (ps->size == ps->capatipy)
{
int* tmp = (int*)realloc(ps->top, sizeof(int) * 2 * (ps->capatipy));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->top = tmp;
tmp = NULL;
ps->capatipy *= 2;
}
ps->top[ps->size] = x;
ps->size++;
}
void STPop(ST* ps)
{
assert(ps);
ps->size--;
}
void myQueuePush(MyQueue* obj, int x)
{
STPush(obj->ps1, x);
}
int myQueuePop(MyQueue* obj)
{
while (obj->ps1->size != 1)
{
STPush(obj->ps2, obj->ps1->top[obj->ps1->size - 1]);
STPop(obj->ps1);
}
int data = obj->ps1->top[obj->ps1->size - 1];
STPop(obj->ps1);
while(obj->ps2->size)
{
STPush(obj->ps1, obj->ps2->top[obj->ps2->size - 1]);
STPop(obj->ps2);
}
return data;
}
int myQueuePeek(MyQueue* obj)
{
if (myQueueEmpty(obj))
{
return -1;
}
return obj->ps1->size == 0 ? obj->ps2->top[0] : obj->ps1->top[0];
}
void myQueueFree(MyQueue* obj)
{
assert(obj);
free(obj->ps1->top);
free(obj->ps2->top);
free(obj->ps1);
free(obj->ps2);
free(obj);
}
用栈实现队列的思想是:用俩个栈,一个栈实现进队列的功能,一个是实现出队列的功能。
用栈去实现队列应该注意的是:
当pop一次时,每出一次队列,那么可以发现把p1的数据push到p2会发生数据逆向存储,因此需要出完队列后重新入队列。
bool myQueueEmpty(MyQueue* obj)
{
return obj->ps1->size == 0 && obj->ps2->size == 0;
}
其次:判空最好用size判,不要用head判,因此,head在你pop和push一次过后,如过p2->head没有主动置空,会发生非法访问。
用队列去实现栈:
typedef struct QueueNode{
struct QueueNode* next;
int val;
}QNode;
typedef struct Queue{
QNode* head;
QNode* tail;
int size;
}Que;
typedef struct {
Que* p1;
Que* p2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
obj->p1=(Que*)malloc(sizeof(Que));
obj->p2=(Que*)malloc(sizeof(Que));
obj->p1->head=obj->p1->tail=obj->p2->head=obj->p2->tail=NULL;
obj->p1->size=obj->p2->size=0;
return obj;
}
bool myStackEmpty(MyStack* obj) {
return obj->p1->head==NULL&&obj->p2->head==NULL;
}
void QueuePush(Que* pq,int x){
assert(pq);
QNode* newnode=(QNode*)malloc(sizeof(QNode));
if(newnode==NULL){
perror("malloc fail");
return;
}
newnode->val=x;
newnode->next=NULL;
if(pq->head==NULL){
pq->head=pq->tail=newnode;
}
else{
pq->tail->next=newnode;
pq->tail=newnode;
}
pq->size++;
}
void QueuePop(Que* pq){
assert(pq);
QNode* pop=pq->head;
pq->head=pop->next;
free(pop);
pop=NULL;
pq->size--;
if(pq->size==0){
pq->head=pq->tail=NULL;
}
}
void myStackPush(MyStack* obj, int x) {
if (obj->p1->size == 0) {
QueuePush(obj->p2, x);
} else {
QueuePush(obj->p1, x);
}
}
int myStackPop(MyStack* obj) {
assert(!myStackEmpty(obj));
if(obj->p2->size==0&&obj->p1->size!=0){
while(obj->p1->head!=obj->p1->tail){
QueuePush(obj->p2,obj->p1->head->val);
QueuePop(obj->p1);
}
int pdata=obj->p1->head->val;
QueuePop(obj->p1);
return pdata;
}
if(obj->p1->size==0&&obj->p2->size!=0){
while(obj->p2->head!=obj->p2->tail){
QueuePush(obj->p1,obj->p2->head->val);
QueuePop(obj->p2);
}
int pdata=obj->p2->head->val;
QueuePop(obj->p2);
return pdata;
}
return EOF;
}
int myStackTop(MyStack* obj) {
assert(!myStackEmpty(obj));
if (obj->p1->size == 0) {
return obj->p2->tail->val;
} else {
return obj->p1->tail->val;
}
}
void myStackFree(MyStack* obj){
if (myStackEmpty(obj)) {
return;
}
if(obj->p1->head==NULL&&obj->p2->head!=NULL){
while(obj->p2->head){
QNode* pop=obj->p2->head->next;
free(obj->p2->head);
obj->p2->head=pop;
}
}
if(obj->p2->head==NULL&&obj->p1->head!=NULL){
while(obj->p1->head){
QNode* pop=obj->p1->head->next;
free(obj->p1->head);
obj->p1->head=pop;
}
}
free(obj->p1);
free(obj->p2);
free(obj);
}
用队列去实现栈需要注意:
int myStackPop(MyStack* obj) {
assert(!myStackEmpty(obj));
if(obj->p2->size==0&&obj->p1->size!=0){
while(obj->p1->head!=obj->p1->tail){
QueuePush(obj->p2,obj->p1->head->val);
QueuePop(obj->p1);
}
int pdata=obj->p1->head->val;
QueuePop(obj->p1);
return pdata;
}
if(obj->p1->size==0&&obj->p2->size!=0){
while(obj->p2->head!=obj->p2->tail){
QueuePush(obj->p1,obj->p2->head->val);
QueuePop(obj->p2);
}
int pdata=obj->p2->head->val;
QueuePop(obj->p2);
return pdata;
}
return EOF;
}
在出栈的过程中使用俩个队列,存在元素的一个队列把非栈顶元素移到另一个空队列,同时把栈顶元素出栈,这样子的话,可以看出他不像以栈实现队列一样,会把数据存储顺序颠倒,但是,同样也有一个麻烦就是,栈顶元素出栈的过程中,含有当前栈顶元素的队列在栈顶元素出栈后,他的头指针和尾指针依旧指向那段存储空间,如果使用
obj->p2->head==NULL&&obj->p1->head!=NULL
这种以head判断的话,你没有不断置空,就会发生非法访问,因此,使用size判断会更好点。