フォロー、スター付きパブリックアカウント、エキサイティングなコンテンツへの直接アクセス
ID:テクノロジーが夢を大きくする
著者:李暁八尾
スタックのコンセプト
スタックは、テーブルの一方の端でのみ動作するように制限されたデータ構造であり、スタックは先入れ先出しのデータ構造です。次の図に示すように、操作を許可する端をスタックのトップと呼び、許可されない端をスタックのボトムと呼びます。公演:
リンクリストについて説明する前に、リンクリストの終了ノードでのみ操作でき、新しいノードの挿入と最後のノードの削除のみが可能であり、このような限定的な「リンクリスト」 'は、スタックと呼ばれるものです。
行き止まりのように、図に示すように、出口は1つだけあり、概念があります。
スタックのノード設計
スタックは、配列スタックとリンクリストスタックに分かれていますが、次のような違いがあります。
数组栈
配列を使用して関数をシミュレートします。これはより高速で便利です。链表栈
リンクされたリストのアイデアを使用して設計することは、実装するのが比較的面倒ですが、それは安定していて、間違いを犯すことは容易ではありません。
リンクリストスタックは、静的リンクリストスタックと動的リンクリストスタックに分かれていますが、次のような違いがあります。
静态链表栈
特定のスタックのスペースサイズは、特定のデータサイズを超える要素のストレージを超えることはできません。动态栈
スペースを自動で作成する方法で作成されており、マシンのハードウェア要件やコンパイラの制御を満たしていれば理論的には優れています。
配列スタック
スタックにデータ要素を格納するために、実際には連続ストレージスペースを使用します。これには、次の特性があります。
ここでの連続性とは、物理的な連続性ではなく、論理的な連続性を指します。
記憶域内の要素の場所は、論理的な順序で格納されます
例を挙げましょう。C言語の配列添え字はすべて0で始まり、スタックの使用に必要なスペースのサイズを見積もることが難しいため、空のスタックを初期化するときは、スタックの最大容量を設定しないでください。
最初にスタックの基本容量を設定します。STACK_プロセス中に、スタックスペースが足りない場合は、徐々に拡張されます。
STACK_INIT_SIZE
(ストレージスペース割り当ての初期化)とSTACK_INCREMENT
(ストレージスペース割り当ての増分)の2つの定数を設定します。マクロ定義は次のとおりです。
#define STACK_INIT_SIZE 1000 //数值可以根据实际情况确定
#define STACK_INCREMENT 10 //数值可以根据实际情况确定
スタックの定義は次のとおりです
typedef struct
{
void *base;
void *top;
int stackSize;
} SqSTACK;
baseはスタックの一番下のポインタを表します
topはスタックのトップポインターを表します
stackSizeは、スタックで現在使用可能な最大容量を表します
baseの値がNULLの場合、それはスタック構造が存在しないことを意味します; topの初期値はスタックの一番下を指しています、つまりtop = base;
新しい要素が挿入されるたびに、ポインターの先頭は1ずつインクリメントされます。それ以外の場合は、1ずつデクリメントされます。空でないスタックのトップポインターは、常にスタックのトップエレメントの次のポインターの上にあります。
次の図に、データ要素とスタックの最上部にあるポインターの関係を示します。
リンクリストスタック
スタックを設計する例として、リンクリストスタックの動的リンクリストスタックを取り上げます。
最初はスタックのノードです。2つの構造が設計されています。構造Node は、図に示すように、data
ドメインとnext
ポインタを含むノードを表します。
その中で、データはデータを表し、次のポインターは次のノードを指す次のポインターを表し、各ノードは次のポインターを介してリンクされます。
次は私たちのデザインの焦点となっています。この制限の設計のために、私たちが含まれ、追加構造、追加する必要がポインタのトップということは常にスタックの先頭を指すとするカウンタのカウント要素の数を記録します。
その主な機能は、図に示すように、許可された操作要素のポインタを設定し、スタックが空になるタイミングを決定することです。
ここでは、topとcountの組み合わせを使用しています。コードは次のように表現できます。
//栈的结点设计
//单个结点设计,数据和下一个指针
typedef struct node
{
int data;
struct node *next;
} Node;
上記のノードを使用してスタックを作成します。スタックは、ヘッドノードへのトップポインターとカウントのためにカウントされます。
typedef struct stack
{
Node *top;
int count;
} Link_Stack;
スタックへのスタックの基本操作(スタックのプッシュ)
積み重ねの基本的な順序を次の図に示します。
入栈(push)操作时,我们只需要找到top所指向的空间,创建一个新的结点,将新的结点的next指针指向top指针指向的空间,再将top指针转移,并且指向新的结点,这就是是入栈操作。
其代码可以表示为:
//入栈 push
Link_Stack *Push_stack(Link_Stack *p, int elem)
{
if (p == NULL)
return NULL;
Node *temp;
temp=(Node*)malloc(sizeof(Node));
//temp = new Node;
temp->data = elem;
temp->next = p->top;
p->top = temp;
p->count++;
return p;
}
栈的基本操作—出栈
出栈(pop)操作,是在栈不为空的情况下,重复说一次,一定要进行判空操作,将栈顶的元素删除,同时top指针,next向下进行移动即可的操作。
其代码可以表示为:
//出栈 pop
Link_Stack *Pop_stack(Link_Stack *p)
{
Node *temp;
temp = p->top;
if (p->top == NULL)
{
printf("错误:栈为空");
return p;
}
else
{
p->top = p->top->next;
free(temp);
//delete temp;
p->count--;
return p;
}
}
栈的基本操作—遍历
这个就很常见了,也是我们调试必须的手段。
栈的遍历相对而言比较复杂,由于栈的特殊性质,其只允许在一端进行操作,所以遍历操作操作永远都是逆序的。
简单一点描述,其过程为,在栈不为空的情况下,一次从栈顶元素向下访问,直到指针指向空(即到栈尾)为结束。
其代码可以表示为:
//遍历栈:输出栈中所有元素
int show_stack(Link_Stack *p)
{
Node *temp;
temp = p->top;
if (p->top == NULL)
{
printf("");
printf("错误:栈为空");
return 0;
}
while (temp != NULL)
{
printf("%d\t", temp->data);
temp = temp->next;
}
printf("\n");
return 0;
}
栈数组与栈链表的代码实现
最后呢,我们使用代码来帮助我们了解一下:
栈数组
数组栈是一种更为快速的模拟实现栈的方法,这里我们不多说。
模拟,就是不采用真实的链表设计,转而采用数组的方式进行模拟操作。
也就是说这是一种仿真类型的操作,其可以快速的帮助我们构建代码,分析过程,相应的实现起来也更加的便捷。
其代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxn 10000
//结点设计
typedef struct stack{
int data[maxn];
int top;
}stack;
//创建
stack *init(){
stack *s=(stack *)malloc(sizeof(stack));
if(s==NULL){
printf("分配内存空间失败");
exit(0);
}
memset(s->data,0,sizeof(s->data));
//memset操作来自于库文件string.h,其表示将整个空间进行初始化
//不理解可以查阅百度百科https://baike.baidu.com/item/memset/4747579?fr=aladdin
s->top=0; //栈的top和bottom均为0(表示为空)
return s;
}
//入栈push
void push(stack *s,int data){
s->data[s->top]=data;
s->top++;
}
//出栈pop
void pop(stack *s){
if(s->top!=0){
s->data[s->top]=0; //让其回归0模拟表示未初始化即可
s->top--;
}
}
//模拟打印栈中元素
void print_stack(stack *s){
for(int n=s->top-1;n>=0;n--){
printf("%d\t",s->data[n]);
}
printf("\n"); //习惯性换行
}
int main(){
stack *s=init();
int input[5]={11,22,33,44,55}; //模拟五个输入数据
for(int i=0;i<5;i++){
push(s,input[i]);
}
print_stack(s);
/
pop(s);
print_stack(s);
return 0;
}
其编译结果如下:
栈链表
#include <stdio.h>
#include <stdlib.h>
//栈的结点设计
//单个结点设计,数据和下一个指针
typedef struct node
{
int data;
struct node *next;
} Node;
//利用上面的结点创建栈,分为指向头结点的top指针和计数用的count
typedef struct stack
{
Node *top;
int count;
} Link_Stack;
//创建栈
Link_Stack *Creat_stack()
{
Link_Stack *p;
//p = new Link_Stack;
p=(Link_Stack*)malloc(sizeof(Link_Stack));
if(p==NULL){
printf("创建失败,即将退出程序");
exit(0);
}
else
{printf("创建成功\n");
}
p->count = 0;
p->top = NULL;
return p;
}
//入栈 push
Link_Stack *Push_stack(Link_Stack *p, int elem)
{
if (p == NULL)
return NULL;
Node *temp;
temp=(Node*)malloc(sizeof(Node));
//temp = new Node;
temp->data = elem;
temp->next = p->top;
p->top = temp;
p->count++;
return p;
}
//出栈 pop
Link_Stack *Pop_stack(Link_Stack *p)
{
Node *temp;
temp = p->top;
if (p->top == NULL)
{
printf("错误:栈为空");
return p;
}
else
{
printf("\npop success");
p->top = p->top->next;
free(temp);
//delete temp;
p->count--;
return p;
}
}
//遍历栈:输出栈中所有元素
int show_stack(Link_Stack *p)
{
Node *temp;
temp = p->top;
if (p->top == NULL)
{
printf("");
printf("错误:栈为空");
return 0;
}
while (temp != NULL)
{
printf("%d\t", temp->data);
temp = temp->next;
}
printf("\n");
return 0;
}
int main()
{ //用主函数测试一下功能
int i;
Link_Stack *p;
p = Creat_stack();
int n = 5;
int input[6] = {10,20,30,40,50,60};
/以依次入栈的方式创建整个栈//
for(i=0;i<n;i++){
Push_stack(p, input[i]);
}
show_stack(p);
出栈///
Pop_stack(p);
show_stack(p);
return 0;
}
编译结果如下:
关于栈的总结
栈-它是一种运算受限的线性表,在数制转换,括号匹配的检验,表达式求值等方面都可以使用,并且较为简便的解决问题。
今天栈基础就讲到这里,下一期,我们再见!
推荐阅读:
嵌入式编程专辑Linux 学习专辑C/C++编程专辑
关注微信公众号『技术让梦想更伟大』,后台回复“m”查看更多内容,回复“加群”加入技术交流群。
长按前往图中包含的公众号关注