一文解决数据结构——栈(C语言实现)

一文解决数据结构之栈(C语言实现)

  • 何为栈?
  • 栈的状态
  • ADT设计
  • C语言实现两种类型栈
    • 顺序栈
      • 代码实现
    • 非连续栈
      • 代码实现
    • 相关经典题目

何为栈?

栈(Stack):就是只允许在一端(限定只在表尾)进行插入或删除操作的线性表。
所以,对于栈而言,是有所区分栈顶(top)栈底(bottom) 的。具体就如下图:
一文解决数据结构——栈(C语言实现)_第1张图片
所以,在使用栈的存储时,是存在先进后出的一个规律。
当我们将元素依次存储入栈后,最先进栈储存的元素在取出(出栈)时,是需要等到后入栈压在其上的元素被全部取出后,才能出栈。

栈的状态

一个栈它是有着四种不同的状态

  • 空栈
  • 满栈
  • 入栈(进栈、压栈push)
  • 出栈 (弹栈pop)

一文解决数据结构——栈(C语言实现)_第2张图片
这也就对应着最基础的几个栈的操作API。

ADT设计

对于栈的程序设计,我们主要编写这几个对栈的操作

栈方法名 操作
initStack(*S) 初始化栈,建立一个空栈
destoryStack(*S) 若栈S存在,销毁栈
is_Full(S) 栈是否已满
clearStack(*S) 将栈中元素清空
getTop(S,*e) 若栈S非空,用e获取栈顶元素
push(*S,e) 若栈S存在,将元素e压入到栈中
pop(*S,*e) 删除栈S的栈顶元素,并用e返回被删除的值
stackLength(S) 得到栈的长度

C语言实现两种类型栈

顺序栈

顺序栈是利用连续的内存空间(数组)进行存储栈内元素,并实现栈的功能特性。
使用顺序栈的优点:

  • 构造简单,存储地址连续,便于操作

缺点:

  • 内存空间固定,可拓展性差
  • 且需要占用整块内存

代码实现

# include
# include

# define MaxSize 50		// 定义栈的深度
typedef int ElemType;	// 定义栈储元素的类型

// 结构体构造栈的结构
typedef struct {
	ElemType data[MaxSize];		// 构造存储的栈内存结构
	int top;	// 定义栈顶指针
} SeqStack;

// 初始化栈
SeqStack* initStack(SeqStack* s){
	// 上面已经使用结构体构造了栈的结构
	// 这里要给定义好的栈结构进行内存的开辟
	s = (SeqStack*)malloc(sizeof(SeqStack));
	// 将栈顶指针设置到栈底
	// 这里是按照数组进行储存,所以栈首是从0开始的
	// 所以这里的初始化,要设置为-1
	s->top = -1;
	return s;
}

// 判断栈是否为空
int is_Full(s){
	if(s->top == MaxSize-1){
		return 0;	
	}
	return -1;
}

// 入栈
void push(SeqStack* s, Elemtype e){
	if(is_Empty){
		printf("Stack is Full\n");
	}else{
		s->top ++;
		s->data[s->top] = e;
	}	
}

// 出栈
void pop(SeqStack* s){
	int fetchElem;
	//若栈内无元素
	if(s->top == -1){
		printf("Stack is Empty\n");
	}else{
		fetchElem = s->data[s->top];
		s->top --;
		printf("%d\n",fetchElem);
	}
}

// 获取栈顶元素
// 这里是进行查看栈顶元素,而非出栈
void getTop(SeqStack s, Elemtyep e){
	// 若栈为空
	if(s->top == -1){
		printf("Stack is Empty\n");
	}else{
		e = s->data[s->top];
		printf("%d\n", e);
	}
}

// 获取栈的长度
void stackLength(SeqStack s){
	int length;
	length = s->top + 1;
	printf("%d\n", length);
} 

非连续栈

非连续栈是指在栈的结构构造上,不依赖于固定连续的内存空间,而是使用指针将分散的栈储元素进行连接,实现栈的功能。
非连续栈的优点:

  • 存储的元素分散,不限制大小,可任意拓宽深度
  • 不需要使用整块内存

缺点:

  • 元素间使用指针连接,操作复杂

代码实现

# include
# include

typedef int ElemType;

// 定义单个栈储节点
typedef struct Node {
	// 定义数据域,用于存放栈储元素
	int data;
	// 定义指针域,指向下一个栈储元素
	struct Node* next;
} LinkStack;

// 初始化栈
void initStack(LinkStack* s){
	// 一次先开辟一个节点的内存空间
	s = (LinkStack*)malloc(sizeof(LinkStack));
	// 将指针清空
	s->Next = NULL;
}

// 判断是否为空
int is_Empty(LinkStack* s){
	return(s->next == NULL)
}

// 销毁栈
void destroyStack(LinkStack* s){
	Node *h = s;
	// 先遍历到栈的最后一个栈储空间
	// 然后释放掉,再从头遍历到最后一个人栈储空间
	while(s != NULL){
		s = s->next;
		free(p);
		p = s;
	} 
}

// 入栈
void push(LinkStack* s, ElemType e){
	LinkStack* p;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	p->data = e;
	// 将待压栈栈储的指针指向首栈元素
	p->next = s->next;
	// 再使首栈元素为待插入的元素
	s->next = p;
}

// 出栈
void pop(LinkStack* s){
	// 判断栈是否为空
	if(s->next == NULL){
		printf("Stack is Empty\n");
	}else{
		// 暂存待删首栈储
		LinkStack* temp = s->next;
		// 使首栈为原首栈的下一栈
		s->next = temp->next;
		// 释放原首栈的存储空间
		free(temp);
	}
}

相关经典题目

关于数据结构的栈在PTA中有一些经典的习题,这里将进行解决
一文解决数据结构——栈(C语言实现)_第3张图片
将十进制转换为二进制,使用初中学习的短除法,进行循环取2余后,在从底往上书写结果,这一过程恰好是符合我们栈的FILO的原则。所以如下:

#include 
#include 
 
#define M 100
typedef int ElemType;
typedef struct
{
	ElemType data[M];
	int top;
}Stack;
 
//初始化栈
void InitStack(Stack *s)
{
	s->top = -1;
}
int Push(Stack *s,ElemType e)
{
	if (s->top == M-1)
	{
		printf("栈满\n");
		return 0;
	}
	s->top++;
	s->data[s->top]=e;
	return 1;
}
 
//判断是否为空
int Empty(Stack *s)
{
	return(s->top==-1);
}
//出栈
int Pop(Stack *s,ElemType *e)
{
	if(Empty(s))
	{
		printf("\n Stack is free");
		return 0;
	}
	*e=s->data[s->top];
	s->top--;
	return 1;
}
 
void Conversion(int N)
{
	int e;
	Stack *s = (Stack *)malloc(sizeof(Stack));
	InitStack(s);
	while(N)
	{
		Push(s,N%2);
		N=N/2;
	}
	while(!Empty(s))
	{
		Pop(s,&e);
		printf("%d",e);
	}
}
 
int main()
{
	int m;
    scanf("%d",&m);
    Conversion(m);

}

你可能感兴趣的:(数据结构与算法分析,数据结构,c语言,开发语言)