0
点赞
收藏
分享

微信扫一扫

C#WPF动态资源和静态资源应用实例

jjt二向箔 2023-09-29 阅读 38

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

 一.前言

二.前文回顾

三.栈

3.1 栈的概念及结构

3.2 栈的实现

3.2.1 初始化函数

3.2.2 销毁函数

3.2.3 入栈函数

3.2.4 出栈函数

3.2.5 计算大小函数

3.2.6 空栈函数

3.2.7 获取栈顶函数

 3.2.8 小测试

3.3 全部代码

四.栈的练习

4.1 有效的括号

五.队列

5.1队列的概念及结构

5.2 队列的实现

5.2.1 初始化函数

5.2.2 入队列函数

5.2.3 出队列函数

5.2.4 获取头队列函数

5.2.5 获取尾队列函数

5.2.6 判空(队列)函数

5.2.7 计算队列大小函数

5.2.8 销毁函数

 5.2.9 小测试

5.3 全部代码 

六.队列的练习

6.1选择题

6.2用队列实现栈

6.2.1 整体思路:

6.2.2 Push函数(入队列):

6.2.3 Pop函数(出队列):

6.2.4 Top函数(取栈顶):

6.2.5 Empty函数(判空):

6.2.6 Free(释放空间)函数:

6.2.7 全部代码

6.3用栈实现队列

6.3.1 整体思路:

6.3.2 Push(入栈)函数

6.3.3 Peek(取头)函数

6.3.4 Pop(出栈)函数

6.3.5 Empty(判空)函数

6.3.6 Free(释放空间)函数

6.3.7 全部代码

6.4 设计循环队列

6.4.1 整体思路:

6.4.2 判空与判满函数(Is(Empty)/(Full))

6.4.3 EnQueue(插入)函数

6.4.4 DeQueue(删除)函数

6.4.5 Front(取头)函数

6.4.6 rear(取尾)函数

6.4.7 Free(释放空间)函数

6.4.8 全部代码

七.结语


8fb442646f144d8daecdd2b61ec78ecd.png 一.前言

二.前文回顾

三.栈

3.1 栈的概念及结构

  • 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
  • 压栈: 栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
  • 出栈:栈的删除操作叫做出栈,出数据也在栈顶。

3.2 栈的实现

其实数组或者链表都是可以实现栈的,二者区别无法就是实现的方式有所不同,效率各有千秋。

3.2.1 初始化函数

3.2.2 销毁函数

3.2.3 入栈函数

3.2.4 出栈函数

3.2.5 计算大小函数

3.2.7 获取栈顶函数

 3.2.8 小测试

3.3 全部代码

其实写到最后我们可以发现,栈其实就是顺序表的简化版,相当于定义了部分适用于栈特征的接口罢了~ 

四.栈的练习

4.1 有效的括号

链接:力扣——有效的括号

基本思路:

  • 数量上最后再进行判断~
  • 类型上:遇到右括号就进行匹配,看有没有对应的左括号。
  • 顺序上:当遇到右括号时判断距离最进的括号是否为对应左括号。

那如何找到最近的括号呢?

  1. 左括号,入栈
  2. 右括号,出栈顶括号,进行匹配

备注:千万不要想着去用switch,千万不要,除非你想要头脑风暴。。。。

五.队列

5.1队列的概念及结构

  • 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)。
  • 入队列:进行插入操作的一端称为队尾。
  • 出队列:进行删除操作的一端称为队头。

5.2 队列的实现

队列也可以用数组或链表的结构实现,使用链表的结构表现更优一些,因为数组在实现出队列(头删)效率比较低~

5.2.1 初始化函数

5.2.2 入队列函数

在我们处理好空间问题后,要进行入队列(尾插)就得先找到尾节点,不过这里还有一些小问题需要注意。

如果链表是空的呢?——赋值给它们2个

5.2.3 出队列函数

5.2.4 获取头队列函数

5.2.5 获取尾队列函数

5.2.6 判空(队列)函数

5.2.7 计算队列大小函数

5.2.8 销毁函数

 5.2.9 小测试

5.3 全部代码 

六.队列的练习

6.1选择题

学习完队列和栈我们可以知道,入队列是1 2 3 4 5那出队列就是1 2 3 4 5,那栈呢?也入栈1 2 3 4 5,出栈就一定是5 4 3 2 1吗?

6.2用队列实现栈

链接:力扣——用队列实现栈

6.2.1 整体思路:

  • 入队列:不为空的队列
  • 出队列:不为空队列前N-1个出队列,插入空队列。删除剩余的数据。

 代码解析:

MyStack* myStackCreate() {
    MyStack st;

    //......
    return &st
}

这个接口的作用仅仅是返回&st吗?——这个局部作用域一出来那么里面的变量都会销毁,这时候传st的地址已经是没意思的了,它已经变成一个野指针了。为了保证MyStack的对象还在,我们可以改用malloc来创造节点,这样出作用域时,malloc出来的空间还是在的。

MyStack* myStackCreate() {
	MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
	QueueInit(&pst->q1);
	QueueInit(&pst->q2);

	return pst;
}

6.2.2 Push函数(入队列):

由于我们不知道哪个为空,哪个不为空,那么我们直接用判空条件来判断再进行入队列

void myStackPush(MyStack* obj, int x) {
	if (!QueueEmpty(&obj->q1))//如果q1为空
	{
		QueuePush(&obj->q1, x);
	}
	else
	{
		QueuePush(&obj->q2, x);
	}

}

6.2.3 Pop函数(出队列):

还是一样的逻辑,不为空的队列往空队列导入。在这里我们假设q1为空,q2不为空。如果q1不为空(判空函数)那交换即可。

int myStackPop(MyStack* obj) {
	QNode* empty = &obj->q1;//假设q1为空
	QNode* nonEmpty = &obj->q2;//假设q2不为空
	if (!QueueEmpty(&obj->q1))//如果qi不为空
	{
		empty = &obj->q2;//交换
		nonEmpty = &obj->q1;

	}
	//前size-1个导入空队列
	while (QueueSize(nonEmpty) > 1)
	{
		QueuePush(empty, QueueFront(nonEmpty));
		//进行入队列,在空队列中存入在不为空队列中的首个数据
		QueuePop(nonEmpty);
		//再Pop掉该数据,使下一次循环是首数据后面的数据

	}//入一次出一次,直达剩下一个
	//这时候只剩下一个数据,获取栈顶元素
	int top = QueueFront(nonEmpty);//存储不为空队列中的元素
	QueuePop(nonEmpty);//再Pop掉该元素

	return top;//返回所谓的栈顶数据
	//至此,该函数完全实现在2个队列入好数据后,
	//结果返回的是后入队列的数据,实现了后进后出。
}

到这里大家是不是有疑惑,为什么都是&obj,到了empty传输时反而不用取地址了呢?

我们需要把类型认识清楚,q1与q2是队列的结构体,该结构体包含了头指针(head),尾指针(tail),和size。对于我们所要实现的接口(Pop,Push,Creat)而言,它们想要改变的都是结构体的指针(tail,head等)来实现自身功能。所以这里需要&obj,而emptynonEmpty已经是结构体指针了,就相当于&obj,也就不需要&了。

6.2.4 Top函数(取栈顶):

这个函数要实现的功能是取栈顶元素,也就是取最后入队列的数据。这里我们可以直接复用前面所写的获取队列尾部数据函数( QueueBack)来轻松实现。

int myStackTop(MyStack* obj) {
	//哪个队列不为空就去取该队列的尾部数据
	if (!QueueEmpty(&obj->q1))
	{
		return QueueBack(&obj->q1);
	}
	else
	{
		return QueueBack(&obj->q2);
	}
}

6.2.5 Empty函数(判空):

在这里的判空应该是两个队列都为空,那才是空。一个为空的不算空。

bool myStackEmpty(MyStack* obj) {
	return  QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

6.2.6 Free(释放空间)函数:

这里我们不能直接freeobj,因为在obj里面有两个队列q1,q2而这两个队列的底层是链表,所以我们在free之前记得使用销毁函数(QueueDestroy)(销毁链表)

void myStackFree(MyStack* obj) {
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);
	free(obj);
}

6.2.7 全部代码

注:想要写题解或者测试,请自行添加上文关于Queue.c与Queue.h的代码段(下面代码能够复用的前提)


typedef struct {
	Que q1;
	Que q2;
} MyStack;


MyStack* myStackCreate() {
	MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
	QueueInit(&pst->q1);
	QueueInit(&pst->q2);

	return pst;
}

void myStackPush(MyStack* obj, int x) {
	if (!QueueEmpty(&obj->q1))//如果q1为空
	{
		QueuePush(&obj->q1, x);
	}
	else
	{
		QueuePush(&obj->q2, x);
	}

}

int myStackPop(MyStack* obj) 
{
	QNode* empty = &obj->q1;//假设q1为空
	QNode* nonEmpty = &obj->q2;//假设q2不为空
	if (!QueueEmpty(&obj->q1))//如果qi不为空
	{
		empty = &obj->q2;//交换
		nonEmpty = &obj->q1;

	}
	//前size-1个导入空队列
	while (QueueSize(nonEmpty) > 1)
	{
		QueuePush(empty, QueueFront(nonEmpty));
		//进行入队列,在空队列中存入在不为空队列中的首个数据
		QueuePop(nonEmpty);
		//再Pop掉该数据,使下一次循环是首数据后面的数据

	}//入一次出一次,直达剩下一个
	//这时候只剩下一个数据,获取栈顶元素
	int top = QueueFront(nonEmpty);//存储不为空队列中的元素
	QueuePop(nonEmpty);//再Pop掉该元素

	return top;//返回所谓的栈顶数据
	//至此,该函数完全实现在2个队列入好数据后,
	//结果返回的是后入队列的数据,实现了后进后出。
}

int myStackTop(MyStack* obj) {
	//哪个队列不为空就去取该队列的尾部数据
	if (!QueueEmpty(&obj->q1))
	{
		return QueueBack(&obj->q1);
	}
	else
	{
		return QueueBack(&obj->q2);
	}
}

bool myStackEmpty(MyStack* obj) {
	return  QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);
	free(obj);
}

最后附上结构图,帮助大家理解~

这也是我们为什么不能直接free掉obj的原因。

6.3用栈实现队列

链接:力扣——用栈实现队列

typedef struct {

} MyQueue;


MyQueue* myQueueCreate() {

}

void myQueuePush(MyQueue* obj, int x) {

}

int myQueuePop(MyQueue* obj) {

}

int myQueuePeek(MyQueue* obj) {

}

bool myQueueEmpty(MyQueue* obj) {

}

void myQueueFree(MyQueue* obj) {

}

6.3.1 整体思路:

老规矩我们先创造两个栈,其中一个依次输入1 2 3 4,要想实现队列的先进先出,那最终要Pop的数就是1.至此,我们可以沿用与上一题差不多的思路(先入栈,再出栈导到另一个栈,最后再处理1.)。

当我们把1Pop掉时再尝试Push5 6,把2 3 4导回去再入栈5 6继续重复上述操作是没问题的,那能不能不把2 3 4导回原来的栈呢?——这时候的情况就发生了变化:可以把2 3 4全部Pop掉,这样第二个栈就空了,再把5 6导入第二个栈最终可以Pop出5.

这时候我们就可以制定规则了

  • pushst栈: 这个栈只能用来入栈。
  • popst栈:这个栈只能用来出栈。

跟上题一样先搞出两个栈出来;

MyQueue* myStackCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	STInit(&obj->pushst);
	STInit(&obj->popst);

	return obj;
}

6.3.2 Push(入栈)函数

void myQueuePush(MyQueue* obj, int x) {
	STPush(&obj->pushst, x);
}

6.3.3 Peek(取头)函数

我们先来写Peek(获取头队列数据函数),要想获取首先得判断popst中是否为空,如果是空的我们需要从pushst中导出数据到popst里。这样才可以获取头队列数据。如果有友友对这里复用的函数命名不熟悉可以去目录对照查看。

int myQueuePeek(MyQueue* obj) {
	//哪个队列不为空就去取该队列的尾部数据
	if (STEmpty(&obj->popst))
	{
		//判断popst为空,开始导入数据
		while (!STEmpty(&obj->pushst))
		{
			STPush(&obj->popst, STTop(&obj->pushst));//导入数据后再进行删除头队列数据
			STPop(&obj->pushst);//导进一个删除一个,清空pushst
		}
	}
	return STTop(&obj->popst);
}

6.3.4 Pop(出栈)函数

到这一步我们就可以发现先写Peek的好处了,因为Peek已经先判断popst是否为空,为空就把pushst的数据搬过来,所以我们只需要处理好popst里面的数据就好了。

int myQueuePop(MyQueue* obj)
{
	int front = myQueuePeek(obj);//存储队列首个数据
	STPop(&obj->popst);//pop掉队列的头数据
	return front;//返回首个数据,达成用两个栈实现先进先出的队列
}

6.3.5 Empty(判空)函数

bool myQueueEmpty(MyQueue* obj) {
	return  QueueEmpty(&obj->pushst) && QueueEmpty(&obj->popst);
}

6.3.6 Free(释放空间)函数

void myQueueFree(MyQueue* obj) {
	STDestroy(&obj->pushst);
	STDestroy(&obj->popst);
	free(obj);
}

6.3.7 全部代码

注:想要写题解或者测试,请自行添加上文关于Stack.h与Stack.c的代码段(下面代码能够复用的前提)

typedef struct {
	ST pushst;
	ST popst;
} MyQueue;


MyQueue* myQueueCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	STInit(&obj->pushst);
	STInit(&obj->popst);

	return obj;
}

void myQueuePush(MyQueue* obj, int x) {
	STPush(&obj->pushst, x);
}



int myQueuePeek(MyQueue* obj) {
	//哪个队列不为空就去取该队列的尾部数据
	if (STEmpty(&obj->popst))
	{
		//判断popst为空,开始导入数据
		while (!STEmpty(&obj->pushst))
		{
			STPush(&obj->popst, STTop(&obj->pushst));//导入数据后再进行删除头队列数据
			STPop(&obj->pushst);//导进一个删除一个,清空pushst
		}
	}
	return STTop(&obj->popst);
}

int myQueuePop(MyQueue* obj)
{
	int front = myQueuePeek(obj);//存储队列首个数据
	STPop(&obj->popst);//pop掉队列的头数据
	return front;//返回首个数据,达成用两个栈实现先进先出的队列
}




bool myQueueEmpty(MyQueue* obj) {
	return  STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj) {
	STDestroy(&obj->pushst);
	STDestroy(&obj->popst);
	free(obj);
}

6.4 设计循环队列

链接:力扣——设计循环队列

typedef struct {

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {

}

int myCircularQueueFront(MyCircularQueue* obj) {

}

int myCircularQueueRear(MyCircularQueue* obj) {

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {

}

bool myCircularQueueIsFull(MyCircularQueue* obj) {

}

void myCircularQueueFree(MyCircularQueue* obj) {

}

图像示例:

这里用数组实现比较轻松,用链表反而会很复杂。

  • 第一缺陷:用rear取队尾数据不好取,因为rear是每次插入数据后才往后移,如果想取队尾部那就得用到rear->pre了,这就变成了双向链表了。又或者再多给一个指针,变成3个指针。
  • 第二缺陷:不好判满,链表为空rearfront指向同一处,而只有一个数据时,还是指向同一处。
  • 第三缺陷:当链表满时rear又指向了与front一致的地方,与链表为空的情况一致,空与满判断不了。

所以为了解决这个问题,很多人选择多开一处空间,该空间不存储数据就为了让rear指到最后。

所以用链表始终存在一些问题,故而我们选择比链表适合一些的数组来实现。

6.4.1 整体思路:

这里判定满的条件就是rear+1,当rear下标是4时可以想象+1就回到front的指向位置。所以最终的规律就是(rear+1)%(k+1)==front.

我们先来模拟一下增删:

准备插入7时,只要我们的判满条件达成理论上是不会继续插入滴~我们继续删除数据~

当我们把6给删除后链表为空链表了,不能再继续删除了,而这时候我们发现frontrear相等,也满足了判空条件。

总结:实现循环链表的两个关键点

  • 判空:front==rear
  • 判满:(rear+1)%(k+1)==front

6.4.2 创造函数(Creat)

typedef struct {
	int* a;//定义数组
	int front;//起点
	int rear;//终点
	int k;//有效数字
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	//多开一个空间方便区分空和满
	obj->a = (int*)malloc(sizeof(int) * (k + 1));
	obj->front = obj->rear = 0;
	obj->k = k;
    return obj;
}

6.4.2 判空与判满函数(Is(Empty)/(Full))

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->rear + 1)%(obj->k+1) == obj->front;
}

6.4.3 EnQueue(插入)函数

在三种插入情况中我们需要注意第三种:rear循环绕到开头。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	if (myCircularQueueIsFull(obj))//如果已经满了
	{
		return false;
	}
	//没满情况下
	obj->a[obj->rear] = value;
	obj->rear++;

	obj->rear %= (obj->k + 1);
	//针对第三种情况当rear在下标4插入7时rear++变成5需要循环到头部

	return true;

}

6.4.4 DeQueue(删除)函数

删除完数据front++,front也会遇到跟上面rear一样的问题,当指向尾时再++需要循环绕到头部。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return false;
	}

	obj->front++;
	obj->front %= (obj->k + 1);
	return true;
}

6.4.5 Front(取头)函数

int myCircularQueueFront(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return -1;
	}
	else
	{
		return obj->a[obj->front];
	}
}

6.4.6 rear(取尾)函数

取队尾函数就不像上面取队头那么好取了。正常的队尾取到rear-1就行,但如果是这种情况呢?取到-1该怎么办。

虽然可以通过if条件来判断并修改,但这里我们来引用一种巧妙的方法;

int myCircularQueueRear(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return -1;
	}
	else
	{
		return obj->a[(obj->rear+obj->k)%(obj->k+1)];
	}
}

6.4.7 Free(释放空间)函数

void myCircularQueueFree(MyCircularQueue* obj) {
	free(obj->a);
	free(obj);
}

6.4.8 全部代码




typedef struct {
	int* a;//定义数组
	int front;//起点
	int rear;//终点
	int k;//有效数字
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	//多开一个空间方便区分空和满
	obj->a = (int*)malloc(sizeof(int) * (k + 1));
	obj->front = obj->rear = 0;
	obj->k = k;
	return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->rear + 1)%(obj->k+1) == obj->front;
}


bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	if (myCircularQueueIsFull(obj))//如果已经满了
	{
		return false;
	}
	//没满情况下
	obj->a[obj->rear] = value;
	obj->rear++;

	obj->rear %= (obj->k + 1);
	//针对第三种情况当rear在下标4插入7时rear++变成5需要循环到头部

	return true;

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return false;
	}

	obj->front++;
	obj->front %= (obj->k + 1);
	return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return -1;
	}
	else
	{
		return obj->a[obj->front];
	}
}

int myCircularQueueRear(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))//如果已经是空的
	{
		return -1;
	}
	else
	{
		return obj->a[(obj->rear+ obj->k)%(obj->k+1)];
	}
}



void myCircularQueueFree(MyCircularQueue* obj) {
	free(obj->a);
	free(obj);
}

4b12323f94834afd9ec146a3c10df229.jpeg七.结语

举报

相关推荐

0 条评论