考研408数据结构(栈与队列)

目录

顺序栈

基本概念

核心特性

代码实现

共享栈

基本概念

核心特性

代码实现

链栈

基本概念

链栈的核心特性

代码实现

带头结点的链栈

不带头结点的链栈


顺序栈

基本概念

顺序栈是一种基于数组实现的栈结构,利用连续的内存空间存储数据,遵循"后进先出"(LIFO)原则。栈顶指针(通常为top)动态指向当前栈顶元素的位置,支持压栈(push)和弹栈(pop)操作。

核心特性

  • 存储结构:通过数组实现,需预先分配固定大小的内存空间。
  • 栈顶指针 :初始值为-1(空栈),插入元素时top递增,删除时递减。
  • 时间复杂度:压栈和弹栈操作的时间复杂度均为O(1)。

代码实现

cpp 复制代码
#include <stdio.h>

// 考研标准:栈的最大容量(可根据题目修改)
#define MaxSize 50

// 顺序栈结构体定义(考研必考写法)
typedef struct {
    // 静态数组存放栈元素
    int data[MaxSize];
    // 栈顶指针:指向栈顶元素(考研最常用定义方式)
    int top;
} SqStack;

// 1. 初始化栈(核心)
void InitStack(SqStack *S) {
    // 栈顶指针置为-1,表示空栈
    S->top = -1;
}

// 2. 判断栈是否为空
int StackEmpty(SqStack S) {
    // 栈顶为-1 → 空栈,返回1;否则返回0
    return S.top == -1;
}

// 3. 判断栈是否为满
int StackFull(SqStack S) {
    // 栈顶指向最后一个元素 → 栈满,返回1
    return S.top == MaxSize - 1;
}

// 4. 入栈操作(进栈)
int Push(SqStack *S, int e) {
    // 栈满,入栈失败
    if (StackFull(*S))
        return 0;

    // 栈顶指针先+1,再赋值
    S->data[++S->top] = e;
    // 入栈成功
    return 1;
}

// 5. 出栈操作(删除栈顶元素,用e返回值)
int Pop(SqStack *S, int *e) {
    // 栈空,出栈失败
    if (StackEmpty(*S))
        return 0;

    // 先取栈顶元素,栈顶指针再-1
    *e = S->data[S->top--];
    // 出栈成功
    return 1;
}

// 6. 取栈顶元素(不删除)
int GetTop(SqStack S, int *e) {
    // 栈空,取元素失败
    if (StackEmpty(S))
        return 0;

    *e = S.data[S.top];
    // 取元素成功
    return 1;
}

// 7. 销毁栈(静态栈无需free,仅重置指针即可)
void DestroyStack(SqStack *S) {
    S->top = -1;
}

// 主函数:测试栈的所有操作(考研答题可写可不写,建议写上)
int main() {
    // 定义一个栈
    SqStack S;
    // 初始化栈
    InitStack(&S);

    // 测试入栈
    Push(&S, 10);
    Push(&S, 20);
    Push(&S, 30);
    printf("入栈元素:10,20,30\n");

    // 测试取栈顶
    int topElem;
    GetTop(S, &topElem);
    printf("当前栈顶元素:%d\n", topElem);

    // 测试出栈
    int e;
    Pop(&S, &e);
    printf("出栈元素:%d\n", e);
    GetTop(S, &topElem);
    printf("出栈后栈顶元素:%d\n", topElem);

    // 测试判空
    if (StackEmpty(S))
        printf("栈为空\n");
    else
        printf("栈不为空\n");

    // 销毁栈
    DestroyStack(&S);
    return 0;
}

共享栈

基本概念

共享栈是一种利用同一块内存空间实现两个栈的数据结构。两个栈的栈底分别位于共享内存空间的两端,栈顶向中间生长。当两个栈顶相遇时,表示栈空间已满。

核心特性

空间高效利用

共享栈通过让两个栈共享同一块内存区域,避免了单独分配两块内存可能导致的浪费。尤其当两个栈的实际使用空间动态变化时,能更灵活地利用内存。

双向生长

两个栈的栈顶分别从数组的两端向中间延伸。栈A从下标0开始向高位生长,栈B从下标n-1开始向低位生长。

溢出条件

共享栈的溢出条件为两个栈顶相遇(即top1 + 1 >= top2)。此时无论向哪个栈压入元素都会导致栈满。

代码实现

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 共享栈最大容量(可根据需求修改)
#define MaxSize 10

// 共享栈结构体定义(408标准写法)
typedef struct {
	// 共享存储空间
	int data[MaxSize];
	// 栈1栈顶指针
	int top1;
	// 栈2栈顶指针
	int top2;
} ShareStack;

//==================== 基本操作 ====================//

// 1. 初始化共享栈
void InitStack(ShareStack *S) {
	// 栈1空栈状态
	S->top1 = -1;
	// 栈2空栈状态
	S->top2 = MaxSize;
}

// 2. 判断共享栈是否为空
// flag=1 判栈1空;flag=2 判栈2空
int StackEmpty(ShareStack S, int flag) {
	if (flag == 1) {
		// 栈1空返回1
		return S.top1 == -1;
	} else if (flag == 2) {
		// 栈2空返回1
		return S.top2 == MaxSize;
	}
	// 非法标记
	return -1;
}

// 3. 判断共享栈是否满
int StackFull(ShareStack S) {
	// 标准判满条件
	return S.top1 + 1 == S.top2;
}

// 4. 入栈操作
// flag=1 入栈1;flag=2 入栈2
int Push(ShareStack *S, int flag, int e) {
	// 栈满,入栈失败
	if (StackFull(*S)) {
		printf("共享栈已满,入栈失败!\n");
		return 0;
	}
	if (flag == 1) {
		// 栈1:先移动指针,再入栈
		S->data[++S->top1] = e;
	} else if (flag == 2) {
		// 栈2:先移动指针,再入栈
		S->data[--S->top2] = e;
	} else {
		printf("栈编号错误!\n");
		return 0;
	}
	return 1;
}

// 5. 出栈操作
// flag=1 出栈1;flag=2 出栈2,e保存出栈元素
int Pop(ShareStack *S, int flag, int *e) {
	if (flag == 1) {
		// 栈1空
		if (S->top1 == -1) {
			printf("栈1为空,出栈失败!\n");
			return 0;
		}
		// 先出栈,再移动指针
		*e = S->data[S->top1--];
	} else if (flag == 2) {
		// 栈2空
		if (S->top2 == MaxSize) {
			printf("栈2为空,出栈失败!\n");
			return 0;
		}
		// 先出栈,再移动指针
		*e = S->data[S->top2++];
	} else {
		printf("栈编号错误!\n");
		return 0;
	}
	return 1;
}

// 6. 获取栈顶元素(不删除)
int GetTop(ShareStack S, int flag, int *e) {
	if (flag == 1) {
		if (S.top1 == -1) return 0;
		*e = S.data[S.top1];
	} else if (flag == 2) {
		if (S.top2 == MaxSize) return 0;
		*e = S.data[S.top2];
	} else return 0;
	return 1;
}

//==================== 测试主函数 ====================//
int main() {
	ShareStack S;
	// 初始化
	InitStack(&S);
	int e;

	// 栈1入栈
	Push(&S, 1, 10);
	Push(&S, 1, 20);
	Push(&S, 1, 30);

	// 栈2入栈
	Push(&S, 2, 100);
	Push(&S, 2, 200);

	// 栈1出栈
	Pop(&S, 1, &e);
	printf("栈1出栈元素:%d\n", e);

	// 栈2出栈
	Pop(&S, 2, &e);
	printf("栈2出栈元素:%d\n", e);

	// 获取栈顶
	GetTop(S, 1, &e);
	printf("栈1栈顶元素:%d\n", e);
	GetTop(S, 2, &e);
	printf("栈2栈顶元素:%d\n", e);

	return 0;
}

链栈

基本概念

链栈是一种基于链表实现的栈结构,采用动态内存分配方式存储数据。与顺序栈不同,链栈无需预先定义固定容量,通过节点间的指针链接实现元素的入栈和出栈操作。链栈的栈顶通常为链表的头节点,操作均在链表头部完成,保证时间复杂度为 O(1)

链栈的核心特性

动态扩展性

链栈的存储空间随需求动态分配,无需担心栈满问题(除非内存耗尽)。

高效操作

入栈和出栈仅需修改头节点指针,无需移动其他元素,操作效率稳定。

内存灵活性

每个节点独立分配内存,可分散存储,但需额外空间存储指针。

实现关键

  • 栈顶指针指向链表头节点。
  • 空栈时,栈顶指针为 NULL
  • 节点结构包含数据域和指向下一节点的指针域。

代码实现

带头结点的链栈

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 链栈结点类型定义
typedef struct StackNode {
	int data;
	struct StackNode *next;
} StackNode, *StackPtr;

// 链栈(仅需栈顶指针,指向头结点)
typedef struct {
	StackPtr top;  // 栈顶指针,指向头结点
} LinkStack;

// 1. 初始化带头结点的链栈
int InitStack(LinkStack *S) {
	// 创建头结点
	S->top = (StackPtr)malloc(sizeof(StackNode));
	if (S->top == NULL) return 0;  // 内存分配失败

	S->top->next = NULL;  // 头结点后继为空
	return 1;
}

// 2. 判断栈空
int StackEmpty(LinkStack S) {
	// 头结点后无元素即为空
	return S.top->next == NULL;
}

// 3. 入栈(头插法)
int Push(LinkStack *S, int e) {
	StackPtr p = (StackPtr)malloc(sizeof(StackNode));
	if (p == NULL) return 0;

	p->data = e;
	p->next = S->top->next;  // 新结点指向原第一个元素
	S->top->next = p;        // 头结点指向新结点
	return 1;
}

// 4. 出栈
int Pop(LinkStack *S, int *e) {
	if (StackEmpty(*S)) return 0;  // 栈空

	StackPtr p = S->top->next;  // p指向待删除结点
	*e = p->data;

	S->top->next = p->next;  // 摘链
	free(p);
	return 1;
}

// 5. 取栈顶元素(不删除)
int GetTop(LinkStack S, int *e) {
	if (StackEmpty(S)) return 0;
	*e = S.top->next->data;
	return 1;
}

// 6. 销毁栈
int DestroyStack(LinkStack *S) {
	StackPtr p, q;
	p = S->top;
	while (p != NULL) {
		q = p->next;
		free(p);
		p = q;
	}
	S->top = NULL;
	return 1;
}

// 测试主函数
int main() {
	LinkStack S;
	int e;

	InitStack(&S);

	Push(&S, 10);
	Push(&S, 20);
	Push(&S, 30);

	GetTop(S, &e);
	printf("栈顶元素:%d\n", e);

	Pop(&S, &e);
	printf("出栈元素:%d\n", e);

	GetTop(S, &e);
	printf("新栈顶:%d\n", e);

	printf("栈是否为空:%s\n", StackEmpty(S) ? "是" : "否");

	DestroyStack(&S);
	return 0;
}

不带头结点的链栈

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct StackNode {
	int data;
	struct StackNode *next;
} StackNode, *StackPtr;

typedef struct {
	StackPtr top;
} LinkStack;

// 初始化
void InitStack(LinkStack *S) {
	S->top = NULL;
}

// 判空
int StackEmpty(LinkStack S) {
	return S.top == NULL;
}

// 入栈
int Push(LinkStack *S, int e) {
	StackPtr p = (StackPtr)malloc(sizeof(StackNode));
	if (!p) return 0;
	p->data = e;
	p->next = S->top;
	S->top = p;
	return 1;
}

// 出栈
int Pop(LinkStack *S, int *e) {
	if (StackEmpty(*S)) return 0;
	StackPtr p = S->top;
	*e = p->data;
	S->top = p->next;
	free(p);
	return 1;
}

// 取栈顶
int GetTop(LinkStack S, int *e) {
	if (StackEmpty(S)) return 0;
	*e = S.top->data;
	return 1;
}

// 销毁栈
void DestroyStack(LinkStack *S) {
	StackPtr p, q;
	p = S->top;
	while (p != NULL) {
		q = p->next;
		free(p);
		p = q;
	}
	S->top = NULL; // 销毁后置空,避免野指针
}

// 测试
int main() {
	LinkStack S;
	int e;

	InitStack(&S);

	Push(&S, 10);
	Push(&S, 20);
	Push(&S, 30);

	GetTop(S, &e);
	printf("栈顶:%d\n", e);

	Pop(&S, &e);
	printf("出栈:%d\n", e);

	DestroyStack(&S); // 用完销毁
	return 0;
}
相关推荐
Brilliantwxx2 小时前
【数据结构】排序算法的神奇世界(下)
c语言·数据结构·笔记·算法·排序算法
进击的荆棘2 小时前
递归、搜索与回溯——二叉树中的深搜
数据结构·c++·算法·leetcode·深度优先·dfs
会编程的土豆2 小时前
【日常做题】栈 中缀前缀后缀
开发语言·数据结构·算法
进击的荆棘2 小时前
递归、搜索与回溯——回溯
数据结构·c++·算法·leetcode·dfs
励志的小陈2 小时前
数据结构--二叉树(链式结构、C语言实现、层序遍历)
c语言·数据结构
自我意识的多元宇宙9 小时前
树与二叉树--二叉树的存储结构
数据结构
自我意识的多元宇宙14 小时前
二叉树的遍历和线索二叉树--二叉树的遍历
数据结构
qq_50242899014 小时前
清华大学与微软亚洲研究院出品:Kronos 本地部署教程
数据结构·python·金融量化·kronos开源模型
C雨后彩虹16 小时前
最多等和不相交连续子序列
java·数据结构·算法·华为·面试