数据结构_栈和队列

4、链表的优缺点:

线性结构的两种常见应用之一 栈(静态内存的分配、动态内存在堆里面分配的)

	int i = 10;//局部变量在栈的区域分配的
	int * p = (int *)malloc(100);//p是在栈去中分配的,(100)在堆区分配的

定义:一种可以实现“先进后出”的存储结构,也是一种操作受限的线性表

分类

​ (1)静态栈 (例子01234 删除2 要把43都删了)

​ (2)动态栈(内部是链表,只能从栈顶插入,不能从栈底插入)

​ 算法:

​ (1)出栈

void pushStack(PSTACK pStack, int val)
{
	PNODE pNew = (PNODE)malloc(sizeof(NODE));//造出一个新节点
	pNew->data = val;//把新节点的数据域放给val
	pNew->pNext = pStack->pTop;//新的结点的指针域要指向下一个节点
	pStack->pTop= pNew; 
}

​ (2)压栈

// 把pStack所指的元素出栈一次,并把出栈元素存入pVal形参所指的变量中,如果出栈失败,返回false,否则返回true
bool popStack(PSTACK pstack, int * pVal)
{
	PNODE p = (PNODE)malloc(sizeof(Node));

	if (is_emptyStack(pstack))//所有的平Stack都存放的是主函数中s的地址
	{
		printf("该栈为空栈");
		return false;
	}
	else
	{
		PNODE r = pstack->pTop;
		*pVal = r->data;

		//pstack->pTop = pstack->pTop->pNext;
		pstack->pTop = r->pNext;		
		free(r);
		r = NULL;
		return true; 		
	}
}

代码

#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>

typedef struct Node
{
	int data;
	struct Node * pNext;//指针域
}NODE,*PNODE;

typedef  struct Stack
{
	PNODE pTop;//永远指向栈顶元素
	PNODE pBottom;//永远指向栈底元素
}STACK,*PSTACK;

void initStack(PSTACK pStack);
void pushStack(PSTACK pStack, int val);
int traverseStack(PSTACK);
bool popStack(PSTACK pstack,int * val);
bool is_emptyStack(PSTACK pStack);
void clear(PSTACK pStack);

int main()
{
	STACK s;//等价于 struct Stack
	int val;

	initStack(&s);//目的是造出一个空战
	pushStack(&s,1);//压栈
	pushStack(&s, 2);
	pushStack(&s, 3);
	pushStack(&s, 4);
	pushStack(&s, 5);
	pushStack(&s, 6);

	//popStack();
	traverseStack(&s);//遍历输出
	if (popStack(&s, &val))
	{
		printf("出栈成功,该出栈元素是%d\n",val);
	}
	else
	{
		printf("出栈失败\n");
	}
	traverseStack(&s);	
	
	clear(&s);
	printf("清空完毕");
	traverseStack(&s);

	return 0;
}

//初始化
void initStack(PSTACK pStack)
{
	pStack->pTop = (PNODE)malloc(sizeof(NODE));//造出一个节点,把地址给pTop
	if (NULL == pStack->pTop)
	{
		printf("内存分配失败");
		exit(-1);
	}
	else
	{
		pStack->pBottom = pStack->pTop;
		pStack->pTop->pNext = NULL;
	}
}

void pushStack(PSTACK pStack, int val)
{
	PNODE pNew = (PNODE)malloc(sizeof(NODE));//造出一个新节点
	pNew->data = val;//把新节点的数据域放给val
	pNew->pNext = pStack->pTop;//新的结点的指针域要指向下一个节点
	pStack->pTop= pNew; 
}

//遍历
int traverseStack(PSTACK pStack)
{
	PNODE p = pStack->pTop;
	while(p!=pStack->pBottom)
	{
		printf("%d \t",p->data);
		p = p->pNext;
	}
	printf("\n");
	return 0;
}

bool is_emptyStack(PSTACK pStack)
{
	/*
	PNODE p = (PNODE)malloc(sizeof(NODE));
	if (NULL == p->pNext)
		return  true;
	else
		return false;
	*/
	
	if (pStack->pTop == pStack->pBottom)
		return true;
	else
		return false;
}

// 把pStack所指的元素出栈一次,并把出栈元素存入pVal形参所指的变量中,如果出栈失败,返回false,否则返回true
bool popStack(PSTACK pstack, int * pVal)
{
	PNODE p = (PNODE)malloc(sizeof(Node));

	if (is_emptyStack(pstack))//所有的平Stack都存放的是主函数中s的地址
	{
		printf("该栈为空栈");
		return false;
	}
	else
	{
		PNODE r = pstack->pTop;
		*pVal = r->data;

		//pstack->pTop = pstack->pTop->pNext;
		pstack->pTop = r->pNext;		
		free(r);
		r = NULL;
		return true; 		
	}
}

//清空
void clear(PSTACK pStack)
{

	if (is_emptyStack(pStack))
	{
		return ;
	}
	else
	{
		PNODE p = pStack->pTop;
		//PNODE q = p->pNext;
		PNODE q = NULL;
		while (p != pStack->pBottom)
		{
			q = p->pNext;
			free(p);
			p = q;
		}
		pStack->pTop = pStack->pBottom;

	}

}

应用:

​ 函数调用

​ 中断

​ 表达式求值

​ 内存分配

​ 缓冲处理

​ 迷宫

线性结构的两种常见应用之二 队列(先进先出)

定义:一种可以实现“先进先出”的存储结构

分类:

​ 链式队列:用链表实现的

​ 静态队列:用数组实现的

​ 静态队列通常都必须是循环队列

​ (1)静态队列为什么必须是循环队列、

​ 避免空间浪费

​ (2)循环队列需要几个参数来确定

​ 两个参数:front和rear.

​ (3)循环队列各个参数的含义

​ ①.队列初始化,front和rear的值都是零

​ ②.队列非空。

​ front代表的是队列的第一个元素

​ rear代表的是队列的最后一个有效元素的下一个元素

​ ③.队列空

​ front和rear的值相等,但不一定是零。

​ (4)循环队列入队伪算法的讲解

​ 入队两步完成:

​ ①.讲值存入r所代表的的位置

​ ②.错误的写法是r=r+1;

​ 正确的写法是:r=(r+1)%数组的长度

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ih7otm7P-1626770786985)(D:\数据结构截图\入队为算法.PNG)]

​ (5)循环队列出队伪算法讲解

​ front=(front+1)%数组的长度

​ (6)如何判断循环队列是否伪空

​ 如果front与rear是否相等

​ (7)如何判断循环队列是否已满

​ 预备知识:front可能比rear大,front可能比rear小,front可 能和rear相等

​ ①:少用一个元素

​ 如果rear和front的值挨着,则队列已满

​ if((rear+1)%数组长度==front)

​ 队满

​ else

​ 不满

​ ②:多增加一个标识参数,数组长度


#include<stdio.h>
#include<malloc.h>

typedef struct Queue
{
	int * pBase;
	int rear;
	int front;

}QUEUE,*pQUEUE;


void init(QUEUE * pQu);
bool en_queue(QUEUE * pQu, int val);
void traverse_queue(QUEUE * pQu);
bool full_queue(QUEUE * pQu);
bool out_queue(QUEUE * pQu,int * pVal);
bool empty_queue(QUEUE * pQu);

int main()
{
	
	QUEUE Q;
	int  val;
	
	init(&Q);
	en_queue(&Q, 2);
	en_queue(&Q, 3);
	en_queue(&Q, 4);
	en_queue(&Q, 5);
	en_queue(&Q, 6);
	en_queue(&Q, 7);
	
	traverse_queue(&Q);
	if (out_queue(&Q, &val))
	{
		printf("出队列的元素是%d\n",val);
	}
	else
	{
		printf("出队失败\n");
	}
	traverse_queue(&Q);
	return 0;




}

void init(QUEUE *pQu)
{
	pQu->pBase = (int *)malloc(sizeof(int) * 6);
	pQu->front = 0;
	pQu->rear = 0;

}

bool full_queue(QUEUE *pQu)
{
	if ((pQu->rear + 1) % 6 == pQu->front)
		return true;
	else
		return false;
}

bool en_queue(QUEUE *pQu, int val)
{
	if (full_queue(pQu))
	{
		printf("队列已满");
		return false;
	}
		
	else
	{
		pQu->pBase[pQu->rear] = val;
		pQu->rear = (pQu->rear + 1) % 6;
		return true;
	}
}
void traverse_queue(QUEUE *pQu)
{
	int i = pQu->front;
	while (i != pQu->rear)
	{
		printf("%d \t", pQu->pBase[i]);
		i = (i + 1) % 6;
	}
	printf("\n");
	return;
}

bool empty_queue(QUEUE * pQu)
{
	if (pQu->front == pQu->rear)
		return true;
	else
	{
		return false;
	}
}

bool out_queue(QUEUE * pQu, int *pVal)
{
	if (empty_queue(pQu))
	{
		printf("当前为空");
		return false;
		
	}
	else
	{
		*pVal =pQu->pBase[ pQu->front];
		pQu->front = (pQu->front + 1) % 6;
		return true;
	}
}


队列的应用

​ 入队

bool en_queue(QUEUE *pQu, int val)
{
	if (full_queue(pQu))
	{
		printf("队列已满");
		return false;
	}
		
	else
	{
		pQu->pBase[pQu->rear] = val;
		pQu->rear = (pQu->rear + 1) % 6;
		return true;
	}
}

​ 出队

bool out_queue(QUEUE * pQu, int *pVal)
{
	if (empty_queue(pQu))
	{
		printf("当前为空");
		return false;
		
	}
	else
	{
		*pVal =pQu->pBase[ pQu->front];
		pQu->front = (pQu->front + 1) % 6;
		return true;
	}
}

队列的具体应用:

​ 所有和时间有段的操作,都有队列的影子。

专题:递归

​ 定义:一个函数直接或者简介调用自己

​ 函数的调用:

​ 当一个函数的运行期间调用另一个函数时,在运行被调函数之前,系统需要完成三件事

​ (1)将所有的实际参数,返回地址等信息传递给被调函数保存

​ (2)为被调函数的局部变量(包括形参)分配存储空间

​ (3)将控制转移到被调函数的入口

​ 从被调函数返回主函数时,系统也需要完成三件事

​ (1)保存被调函数的返回结果

​ (2)释放被调函数所占的存储空间

​ (3)依照被调函数保存的返回地址将控制转移到调用函数

​ 递归满足的几个条件:

​ (1)递归必须得有一个明确的中止条件

​ (2)该函数所处理的数据规模必须在递减

​ (3)这个转换必须是可以理解的

​ 循环和递归的关系

​ 所有的循环都可以用递归实现,所有的递归不一定用循环实现

​ 递归的循环的优缺点

​ 优点:易于理解、速度慢、存储空间大

​ 循环的优缺点:不易理解、速度快、存储空间小

递归的例子:

​ 1.1+2+3+4+5+……100

int sum_num(int n)
{
	if (1 == n)
		return 1;
	else
		return sum_num(n - 1) + n;
}

​ 2.求阶乘

long  factorial(long n)
{
	int result;
	if (n <= 1)
		return 1;
	else	
		return factorial(n - 1)*n;
}

​ 3.汉诺塔

//汉诺塔问题
/*
	
		如果是一个盘子:
			直接将A柱子上的盘子从A移动到C
		否则:
			先把柱子A上的n-1个盘子从A借助C移动到B
			将柱子上的第n个盘子直接移到C
			再将B柱子上的n-1个盘子借助A移动到C

	
	
*/


void HanuoTower(int n, char A, char B, char C)
{

	if (1 == n)
	{
		printf("将编号为%d的盘子直接从%c柱子移动到%c\n",n,A,C);
	}
	else
	{
		//在某一次是:再将B柱子上的n-1个盘子借助A移动到C,此时形参中的charA ,应该是B
		HanuoTower(n-1,A,C,B);//把n-1个盘子从A借助C移动到B
		printf("将编号为%d的盘子直接从%c柱子移动到%c\n", n, A, C);
		HanuoTower(n-1,B,A,C);//B柱子上的n-1个盘子借助A移动到C
	}
}

​ 4.走迷宫

递归的应用:

​ 树和森林就是以递归的方式定义的

​ 树和图很多算法都是以帝归来实现的

​ 很多数学公式就是以递归的方式来定义的

​ 斐波拉契序列

​ 3.汉诺塔

//汉诺塔问题
/*
	
		如果是一个盘子:
			直接将A柱子上的盘子从A移动到C
		否则:
			先把柱子A上的n-1个盘子从A借助C移动到B
			将柱子上的第n个盘子直接移到C
			再将B柱子上的n-1个盘子借助A移动到C

	
	
*/


void HanuoTower(int n, char A, char B, char C)
{

	if (1 == n)
	{
		printf("将编号为%d的盘子直接从%c柱子移动到%c\n",n,A,C);
	}
	else
	{
		//在某一次是:再将B柱子上的n-1个盘子借助A移动到C,此时形参中的charA ,应该是B
		HanuoTower(n-1,A,C,B);//把n-1个盘子从A借助C移动到B
		printf("将编号为%d的盘子直接从%c柱子移动到%c\n", n, A, C);
		HanuoTower(n-1,B,A,C);//B柱子上的n-1个盘子借助A移动到C
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值