链式栈
我们需要明确下边这个事实:我们写的函数都是对外的接口,也就是说,我们只需告诉别的程序员我们程序预留的接口(在这个程序里边是一些函数),别的程序员就可以使用我们的代码片段。不需要了解我们的栈是用什么类型的数据结构实现的。我们可以从我们写的代码中体会到这个事实。
首先我们需要确定我们使用的数据结构,也就是我们链式栈的结构体StackNode,所以开始我们写出了下边的代码:
1: #include <stdio.h>
2:
3: typedef struct StackNode
4: {
5: int data; // 数据域
6: StackNode *next; // 下一结点的指针域
7: }StackNode, *Stack;
8:
9: int main()
10: {
11: return 0;
12: }
问题1:StackNode和Stack分别代表什么?
OK,有了链式栈的数据结构,我们就开始写函数了。所有的程序都是因为有需求才存在的,所以我们需要设想一下链式栈的需求。可能某天有某个程序员需要使用栈了,这时候他就开始看说明文档了(假如这个说明文档是我们自己写的):
1: // 链式栈的用法如下:
2: int main()
3: {
4: // 初始化一个链式栈(这个时候就有链式栈的头结点了,也就是说我们可以操作它了)
5: Stack stack = InitStack();
6: Push(stack, 3); // 3进栈
7: Push(stack, 4); // 4进栈
8: Push(stack, 5); // 5进栈
9:
10: printf("%d\n", Pop(stack));
11: printf("%d\n", Pop(stack));
12: printf("%d\n", Pop(stack));
13: printf("%d\n", Pop(stack)); // 第4次出栈,应该出错
14:
15: return 0;
16: }
OK,有了需求,我们可以根据需求来写代码了,首先我们看第5行,初始化栈,上边的注释写的是初始化一个头结点。OK,我们可以对应写出如下代码:
1: // 初始化一个链式栈(返回一个链式栈的头结点)
2: Stack InitStack()
3: {
4: Stack stack = (Stack)malloc(sizeof(StackNode));
5: stack->next = NULL;
6: return stack;
7: }
问题2:头结点的作用是什么?
初始化结束以后就可以做入栈和出栈的操作了,我们可以写出如下入栈和出栈的代码:
入栈:
1: void Push(Stack stack, int newData)
2: {
3: // 判断是否为空,Just For Safty :-D
4: if(stack == NULL)
5: {
6: printf("栈未初始化,请初始化以后再使用\n");
7: return;
8: }
9:
10: // 找到最后一个节点
11: StackNode *lastNode = stack;
12: while(lastNode->next)
13: {
14: lastNode = lastNode->next;
15: }
16:
17: lastNode->next = (StackNode*)malloc(sizeof(StackNode));
18: lastNode->next->data = newData;
19: lastNode->next->next = NULL;
20:
21: printf("入栈成功!\n");
22: }
问题3:请简述一下11行到19行的思想
出栈:
1: int Pop(Stack stack)
2: {
3: // 判断栈是否为空
4: if(!stack->next)
5: {
6: printf("栈为空,无法出栈\n");
7: return -1; // -1只是一个自定义的错误码, 不必深究
8: }
9:
10: // 找到最后一个节点的前一个节点
11: // tempNode: 最后一个节点的前一个节点
12: StackNode *tempNode = stack;
13: // 问题4
14: while(tempNode->next->next)
15: {
16: tempNode = tempNode->next;
17: }
18:
19: // 问题5
20: int data = tempNode->next->data;
21: free(tempNode->next);
22: tempNode->next = NULL;
23: return data;
24: }
问题4:就该程序来说,Pop的返回值是由谁决定的?
问题5:请自己实现函数:bool IsEmpty(Stack stack),要求:栈为空时返回true,栈非空时返回false
下边是完整代码:
/************************************************************************/ /* 链式栈 */ /************************************************************************/ #include <stdio.h> #include <stdlib.h> // StackNode: 链式栈的一个节点 // Stack: 指向链式栈头结点的指针 typedef struct StackNode { int data; // 数据域 StackNode *next; // 下一结点的指针域 }StackNode, *Stack; // 初始化一个链式栈(返回一个链式栈的头结点) Stack InitStack() { Stack stack = (Stack)malloc(sizeof(StackNode)); stack->next = NULL; return stack; } void Push(Stack stack, int newData) { // 判断是否为空,Just For Safty :-D if(stack == NULL) { printf("栈未初始化,请初始化以后再使用\n"); return; } // 找到最后一个节点 StackNode *lastNode = stack; while(lastNode->next) { lastNode = lastNode->next; } lastNode->next = (StackNode*)malloc(sizeof(StackNode)); lastNode->next->data = newData; lastNode->next->next = NULL; printf("入栈成功!\n"); } int Pop(Stack stack) { // 判断栈是否为空 if(!stack->next) { printf("栈为空,无法出栈\n"); return -1; // -1只是一个自定义的错误码, 不必深究 } // 找到最后一个节点的前一个节点 // tempNode: 最后一个节点的前一个节点 StackNode *tempNode = stack; while(tempNode->next->next) { tempNode = tempNode->next; } int data = tempNode->next->data; free(tempNode->next); tempNode->next = NULL; return data; } // 链式栈的用法如下: int main() { // 初始化一个链式栈(这个时候就有链式栈的头结点了,也就是说我们可以操作它了) Stack stack = InitStack(); Push(stack, 3); // 3进栈 Push(stack, 4); // 4进栈 Push(stack, 5); // 5进栈 printf("%d\n", Pop(stack)); printf("%d\n", Pop(stack)); printf("%d\n", Pop(stack)); printf("%d\n", Pop(stack)); // 第4次出栈,应该出错 return 0; }