顺序栈和链式栈

首先来看一下百度百科关于栈的解释:

定义:栈是限定仅在表头进行插入和删除操作的线性表。要搞清楚这个概念,首先要明白”栈“原来的意思,如此才能把握本质。”栈“者,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。

栈满足先进后出,接下来我们分别用顺序表和链表来实现一下栈。
申明:下面的代码是是一个粗糙版本的链式栈,虽然结果正确但存在地址空间的浪费,优化版本在该代码下面给出!!

LinkStack.h

pragma once
#include
#include
#include

#define TestType printf("\n############################# %s #############################\n",__FUNCTION__);

typedef char LinkStackType;

typedef struct LinkStack{
    struct LinkStack* next;
    struct LinkStack* tail;
    LinkStackType data;
} LinkStack;

// 初始化堆
void LinkStackInit(LinkStack** phead);
//打印函数
void LinkStackPrint(LinkStack* head, const char* msg);
//入栈
void LinkStackPush(LinkStack** phead,LinkStackType value);
//出栈
void LinkStackPop(LinkStack** phead);
//取栈顶元素
int LinkStackGetFront(LinkStack* head,LinkStackType* value);
//销毁栈
void LinkStackDestroy(LinkStack** phead);

linkstack.c

#include"linkstack.h"

void LinkStackInit(LinkStack** phead){
    if(phead == NULL){
        //非法输入
        return;
    }
    *phead = NULL;
}

void DestroyNode(LinkStack* to_delete){


    free(to_delete);
}

void LinkStackPrint(LinkStack* head,const char* msg){
    printf("[ %s ]:\n",msg);
    if(head == NULL){
        return;
    }
    LinkStack* cur = head;
    while(cur != NULL){
        printf("[%c][%p]->",cur->data,cur);
        cur = cur->next;
    }
    printf("NULL\n");
    return;
}

//创建新的节点
LinkStack* LinkStackCreateNode(LinkStackType value){
    LinkStack* new_node = (LinkStack*)malloc(sizeof(LinkStack));
    if(new_node == NULL){
    //申请内存失败
        return NULL;
    }
    new_node->data = value;
    new_node->next = NULL;
    return new_node;
}

//入栈
void LinkStackPush(LinkStack** phead,LinkStackType value){
    if(phead == NULL){
        return;
    }
    if(*phead == NULL){
   // 如果栈为空,插入新的元素
        *phead = LinkStackCreateNode(value);
        (*phead)->tail = *phead;
        return;
    }
    //如果栈不为空,让tail的next等于新节点
    (*phead)->tail->next = LinkStackCreateNode(value);
    //tail指针后移
    (*phead)->tail = (*phead)->tail->next;
    return;
}

//出栈
void LinkStackPop(LinkStack** phead){
    if(phead == NULL){
    //非法输入
        return;
    }
    if(*phead == NULL){
    //栈空,无法出栈
        return;
    }
    LinkStack* cur = *phead;
    while(cur->next != (*phead)->tail){
        cur = cur->next;
    }
    DestroyNode((*phead)->tail);
    cur->next = NULL;
    (*phead)->tail = cur;
}

//取栈顶元素,这里的value是输出型参数
int LinkStackGetFront(LinkStack* head,LinkStackType* value){
    if(head == NULL){
    //如果失败返回0
        return 0;
    }
    *value = head->tail->data;
    //如果成功返回1
    return 1;
}

void LinkStackDestroy(LinkStack** phead){
    if(phead == NULL){
        return;
    }
    LinkStack* cur = *phead;
    LinkStack* tmp;
    //遍历释放内存
    while(cur != NULL){
        tmp = cur;
        cur = cur->next;
        DestroyNode(tmp);
    }
    *phead = NULL;
    return;
}

main.c

#include"linkstack.h"

void TestLinkStackPush(){
    TestType;
    LinkStack* head;
    LinkStackInit(&head);
    LinkStackPush(&head,'a');
    LinkStackPrint(head,"a入栈");
    LinkStackPush(&head,'b');
    LinkStackPrint(head,"b入栈");
    LinkStackPush(&head,'c');
    LinkStackPrint(head,"c入栈");
    return;
}

void TestLinkStackPop(){
    TestType;
    LinkStack* head;
    LinkStackInit(&head);
    LinkStackPush(&head,'a');
    LinkStackPush(&head,'b');
    LinkStackPush(&head,'c');
    LinkStackPush(&head,'d');
    LinkStackPrint(head,"abcd入栈");
    LinkStackPop(&head);
    LinkStackPrint(head,"d出栈");
    LinkStackPop(&head);
    LinkStackPrint(head,"c出栈");
    LinkStackPop(&head);
    LinkStackPrint(head,"b出栈");
    return;
}

void TestLinkStackGetFront(){
    TestType;
    LinkStack* head;
    int result;
    LinkStackType value;
    LinkStackInit(&head);
    LinkStackPush(&head,'a');
    LinkStackPush(&head,'b');
    LinkStackPush(&head,'c');
    LinkStackPush(&head,'d');
    LinkStackPrint(head,"abcd入栈");
    result = LinkStackGetFront(head,&value);
    printf("expect:1,actual:%d\n",result);
    printf("expect:d,actual:%c\n",value);
    LinkStackPop(&head);
    result = LinkStackGetFront(head,&value);
    printf("expect:1,actual:%d\n",result);
    printf("expect:c,actual:%c\n",value);
}

void TestLinkStackDestroy(){
    TestType;
    LinkStack* head;
    LinkStackInit(&head);
    LinkStackPush(&head,'a');
    LinkStackPush(&head,'b');
    LinkStackPush(&head,'c');
    LinkStackPush(&head,'d');
    LinkStackPrint(head,"abcd入栈");
    LinkStackDestroy(&head);
    LinkStackPrint(head,"销毁栈结构");
    return;
}

int main(){
    TestLinkStackPush();
    TestLinkStackPop();
    TestLinkStackGetFront();
    TestLinkStackDestroy();
    printf("\n\n");
    return 0;
}

Makefile

LinkStack:linkstack.c main.c
    gcc -o $@ $^ -g
.PHONY:clean
clean:
    rm -f LinkStack

以上就是链式栈的代码,在编写完上面的代码,虽然结果正确,但是我发现这个代码写的有一些不妥之处,比如说把tail指针放到节点中,这种做法真的是很蠢。因为在每次开辟新的节点都会创建一个新的tail指针,但是我们明明可以使用一个tail指针就可以办到的事情,白白浪费了内存空间,一两个节点可能效果不明显,但是如果节点数量达到一定的数量级,那浪费的地址空间就相当大了。还好这是在学习,我们可以随时做优化代码。希望自己以后牢记这个问题!!!!!!!!!!

以下是优化代码
linkstack.h

#pragma once
#include
#include
#include

#define TestType printf("\n################################## %s #################################\n",__FUNCTION__)
#define LinkStackType char

typedef struct LinkNode{
    struct LinkNode* next;
    LinkStackType data;
} LinkNode;

//创建另外一个结构体存放链表的头指针和尾指针
typedef struct LinkStack{
    struct LinkNode* head;
    struct LinkNode* tail;
} LinkStack;

//初始化函数
void LinkStackInit(LinkStack* q);
//入栈函数
void LinkStackPush(LinkStack* q,LinkStackType value);
//打印函数
void LinkStackPrint(LinkStack* q,const char* msg);
//出栈函数
void LinkStackPop(LinkStack* q);
//取栈顶元素
int LinkStackFront(LinkStack* q,LinkStackType* value);

linkstack.c

#include"linkstack.h"

void LinkStackInit(LinkStack* q){
    if(q == NULL){
    //非法输入
        return;
    }
    q->head = NULL;
    q->tail = NULL;
    return;
}

LinkNode* LinkStackCreateNode(LinkStackType value){
//开辟一个新的节点
    LinkNode* new_node = (LinkNode*)malloc(sizeof(LinkNode));
    if(new_node == 0){
    //申请内存失败
        return NULL;
    }
    new_node->next = NULL;
    new_node->data = value;
    return new_node;
}

void DestroyNode(LinkNode* to_delete){
    if(to_delete == NULL){
        return;
    }
    free(to_delete);
    return;
}

void LinkStackPush(LinkStack* q,LinkStackType value){
    if(q == NULL){
        return;
    }
    LinkNode* new_node = LinkStackCreateNode(value);
    if(q->head == NULL){
    //如果栈为空
        q->head = new_node;
        q->tail = new_node;
        return;
    }
    //栈不为空
    q->tail->next = new_node;
    q->tail = q->tail->next;
    return;
}

void LinkStackPrint(LinkStack* q,const char* msg){
    if(q == NULL){
        return;
    }
    printf("[ %s ]:\n",msg);
    LinkNode* cur = q->head;
    while(cur != NULL){
        printf("[%c][%p]->",cur->data,cur);
        cur = cur->next;
    }
    printf("NULL\n\n");
    return;
}

void LinkStackPop(LinkStack* q){
    if(q == NULL){
        return;
    }
    if(q->head == NULL){
        return;
    }
    LinkNode* temp = q->tail;
    LinkNode* cur = q->head;
    while(cur->next != q->tail){
        cur = cur->next;
    }//找到tail前的一个节点
    q->tail = cur;//tail指针前移一个节点
    q->tail->next = NULL;
    DestroyNode(temp);//将最后一个节点释放
    return;
}
//value是输出型参数
int LinkStackFront(LinkStack* q,LinkStackType* value){
    if(q == NULL){
        return 0;
    }
    *value = q->tail->data;
    return 1;
}

main.c

#include"linkstack.h"

void TestLinkStackPush(){
    TestType;
    LinkStack q;
    LinkStackInit(&q);
    LinkStackPush(&q,'a');
    LinkStackPush(&q,'b');
    LinkStackPush(&q,'c');
    LinkStackPush(&q,'d');
    LinkStackPush(&q,'e');
    LinkStackPrint(&q,"abcde入栈");
    return;
}

void TestLinkStackPop(){
    TestType;
    LinkStack q;
    LinkStackInit(&q);
    LinkStackPush(&q,'a');
    LinkStackPush(&q,'b');
    LinkStackPush(&q,'c');
    LinkStackPush(&q,'d');
    LinkStackPush(&q,'e');
    LinkStackPrint(&q,"abcde入栈");
    LinkStackPop(&q);
    LinkStackPrint(&q,"e出栈");
    LinkStackPop(&q);
    LinkStackPrint(&q,"d出栈");
    return;
}

void TestLinkStackFront(){
    TestType;
    LinkStack q;
    LinkStackType value;
    LinkStackInit(&q);
    LinkStackPush(&q,'a');
    LinkStackPush(&q,'b');
    LinkStackPush(&q,'c');
    LinkStackPush(&q,'d');
    LinkStackPush(&q,'e');
    LinkStackPrint(&q,"abcde入栈");
    int ret = LinkStackFront(&q,&value);
    printf("expect: 1, actual: %d\n",ret);
    printf("expext: e, actual: %c\n",value);
    LinkStackPop(&q);
    ret = LinkStackFront(&q,&value);
    printf("expect: 1, actual: %d\n",ret);
    printf("expext: d, actual: %c\n",value);
    return;
}

int main(){
    TestLinkStackPush();
    TestLinkStackPop();
    TestLinkStackFront();
    return 0;
}
这就是链式栈的一些基本操作的介绍,写完之后再来说一下在写的时候遇到的问题吧。
首先就是在第一个版本的链式栈将tail放到了节点中,造成了不必要的资源浪费。
其次,在写的时候同桌墩子有一个问题,我觉得有必要说一下。

linknode要用malloc来开辟空间,而linkqueue为什么在函数中就没有在函数里用malloc来开辟空间呢?
这是因为在主函数里,我先创建了一个linkqueue结构体(linkqueue q),然后传入的函数的参数是取地址q,这样传参在创建结构体的时候已经分配了内存,所以不需要在函数里再malloc开辟空间了。但是如果在主函数里我传的参数是一个指针(linkqueue* q),那么在函数里就有必要手动为malloc指向的空间分配一段空间了。

以上就是关于链式栈的介绍,接下来,我们介绍一下顺序栈。

此次介绍的顺序栈是可以动态开辟空间的顺序栈
顺序栈和链式栈_第1张图片
seqstack.h

#pragma once
#include
#include
#include
#include

#define TestType printf("\n##################################### %s #######################################\n",__FUNCTION__)

typedef char SeqStackType;
//动态开辟内存
typedef struct SeqStack{
    SeqStackType* data;
    size_t size;
    size_t capacity;  //MAX_SIZE的替代品,data这段元素中能容纳的元素个数
} SeqStack;
//初始化函数
void SeqStackInit(SeqStack* stack);
//打印函数
void SeqStackPrint(SeqStack* stack, const char* msg);
//扩容函数
void SeqStackReSize(SeqStack* stack);
//入栈函数
void SeqStackPush(SeqStack* stack, SeqStackType value);
//出栈函数
void SeqStackPop(SeqStack* stack);
//销毁栈函数
void SeqStackDestroy(SeqStack* stack);
//取栈顶元素
int SeqStackGetFront(SeqStack* stack,SeqStackType* value);

seqstack.c

#include"seqstack.h"

void SeqStackInit(SeqStack* stack){
    if(stack == NULL){
        return;
    }
    stack->size = 0;
    stack->capacity = 1000;//初始化capacity
    stack->data = (SeqStackType*)malloc\
    (stack->capacity * sizeof(SeqStackType));
    //给data开辟空间
    return;
}

void SeqStackPrint(SeqStack* stack, const char* msg){
    if(stack == NULL){
        return;
    }
    printf("[ %s ]:\n",msg);
    size_t i = 0;
    for(;i < stack->size;i++){
    //循环打印数组下标0到size-1的元素
        printf("%c ",*(stack->data+i));
    }
    printf("\n");
    return;
}

void SeqStackReSize(SeqStack* stack){
    if(stack == NULL){
        return;
    }
    if(stack->size < stack->capacity){
        return;
    }
    //capacity扩容策略自己定,+1是为了避免capacity为0带来的错误
    stack->capacity = stack->capacity * 2 + 1;
    SeqStackType* new_ptr = (SeqStackType*)malloc\
    (stack->capacity * sizeof(SeqStackType));
    //重新开辟空间
    size_t i = 0;
    for(;i < stack->size;++i){
        new_ptr[i] = stack->data[i];
    }//拷贝数组元素
    free(stack->data);//释放data指向的空间
    stack->data = new_ptr;//data指向新的内存空间
    return;
}

void SeqStackPush(SeqStack* stack,SeqStackType value){
    if(stack == NULL){
        return;
    }
    if(stack->size > stack->capacity){
    //数组元素size超出最大容量,进行扩容
        SeqStackReSize(stack);
    }
    stack->data[stack->size++] = value;
    return;
}

void SeqStackPop(SeqStack* stack){
    if(stack == NULL){
        return;
    }
    if(stack->size == 0){
        return;
    }
    stack->size--;
    return;
}

void SeqStackDestroy(SeqStack* stack){
    if(stack == NULL){
        return;
    }
    free(stack->data);
    stack->capacity = 0;
    stack->size = 0;
    return;
}

int SeqStackGetFront(SeqStack* stack,SeqStackType* value){
    if(stack == NULL){
        return 0;
    }
    *value = stack->data[stack->size -1];
    return 1;
}

main.c

#include"seqstack.h"

void TestSeqStackInit(){
    TestType;
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPrint(&stack,"初始化栈");
}

void TestSeqStackReSize(){
    TestType;
    SeqStack stack;
    SeqStackInit(&stack);
    printf("扩容之前:%zu\n",stack.capacity);
    stack.size = 1001;
    SeqStackReSize(&stack);
    printf("扩容之后:%zu\n",stack.capacity);
    return;
}

void TestSeqStackPush(){
    TestType;
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPush(&stack, 'a');
    SeqStackPush(&stack, 'b');
    SeqStackPush(&stack, 'c');
    SeqStackPush(&stack, 'd');
    SeqStackPrint(&stack, "abcd入栈");
    return;
}

void TestSeqStackPop(){
    TestType;
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPush(&stack, 'a');
    SeqStackPush(&stack, 'b');
    SeqStackPush(&stack, 'c');
    SeqStackPush(&stack, 'd');
    SeqStackPrint(&stack, "abcd入栈");
    SeqStackPop(&stack);
    SeqStackPrint(&stack, "d 出栈");
    SeqStackPop(&stack);
    SeqStackPrint(&stack, "c 出栈");
}

void TestSeqStackDestroy(){
    TestType;
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPush(&stack, 'a');
    SeqStackPush(&stack, 'b');
    SeqStackPush(&stack, 'c');
    SeqStackPush(&stack, 'd');
    SeqStackPrint(&stack, "abcd入栈");
    SeqStackDestroy(&stack);
    SeqStackPrint(&stack, "销毁栈结构");
    return;
}

void TestSeqStackGetFront(){
    TestType;
    SeqStack stack;
    SeqStackType value;
    int result;
    SeqStackInit(&stack);
    SeqStackPush(&stack, 'a');
    SeqStackPush(&stack, 'b');
    SeqStackPush(&stack, 'c');
    SeqStackPush(&stack, 'd');
    SeqStackPush(&stack, 'e');
    SeqStackPrint(&stack, "abcd入栈");
    result = SeqStackGetFront(&stack,&value);
    printf("expect:1,actual:%d\n",result);
    printf("expect:e,actual:%c\n",value);
    SeqStackPop(&stack);
    result = SeqStackGetFront(&stack,&value);
    printf("expect:1,actual:%d\n",result);
    printf("expect:d,actual:%c\n",value);
    SeqStackPop(&stack);
    return;
}

int main(){
    TestSeqStackInit();
    TestSeqStackReSize();
    TestSeqStackPush();
    TestSeqStackPop();
    TestSeqStackDestroy();
    TestSeqStackGetFront();
    return 0;
}

Makefile

seqstack:seqstack.c main.c
    gcc -o $@ $^
.PHONY:clean
clean:
    rm -f seqstack

以上,关于顺序栈和链式栈的基本操作。
由于我的水平有限,上面的介绍可能有不妥或者Bug,欢迎发邮件到我的邮箱([email protected])批评指正!

你可能感兴趣的:(工欲善其事)