数据结构之二维链表、队列和栈 ----- Day19 and Day 20

一. 知识点整理

1. 二维链表

1.1 图解二维链表

  • 创建一个双向链表和一个单链表,其中双向链表中又保存单链表的指针,相当于创建一个歌单,和歌单里面的歌曲一样

在这里插入图片描述

  • 代码如下
//定义的一维链表的结构体
typedef struct node
{
	char *name;                      

	struct node *next;				 //定义结构体的指针,单向指针,保存下一个结点的地址
}NODE;

//定义二维链表的结构体
typedef struct hnode
{
	char *name;						
	int num; 
	NODE *top;
	NODE *tail; 
	                   
	struct hnode *next;				//定义结构体的指针,双向指针
	struct hnode *prior;
}HNODE;

1.2 二维链表的一些操作(以MP3歌单项目为例)

仅展示部分核心代码,望见谅!

  • 初始化二维链表的头结点
HNODE *InitHeadNode()
{
	//给头节点分配内存
	HNODE *head = (HNODE *)malloc(sizeof(HNODE));
	assert(head != NULL);
	//给头节点初始化
	head->name = NULL;
	head->num = -9999;
	//双向节点初始化
	head->next = head;
	head->prior = head;
	return head;
}
  • 向二维链表中添加数据,添加歌单
int addHList(HNODE *head, const char *hname)
{
	HNODE *temp = head->next;  //定义一个中间指针用来判断添加的歌单和存在的歌单是否重复
	//判断是否重复,重复则返回1
	while (temp != head)
	{
		if (!strcmp(temp->name, hname))
		{
			printf("歌单已经存在,添加失败!\n");
			return 1;
		}
		temp = temp->next;
	}
	//分配一个新的内存pnew用来保存要添加的歌单
	HNODE *pnew = (HNODE *)malloc(sizeof(HNODE));
	pnew->name = (char *)malloc(strlen(hname)+1);
	assert(pnew != NULL);assert(pnew->name != NULL);
	//给pnew赋值
	strcpy(pnew->name, hname);
	pnew->num = 0;
	//连接到一起
	pnew->prior = head->prior;
	head->prior->next = pnew;
	pnew->next = head;
	head->prior = pnew;
}
  • 向二维链表中添加链表,也就是向歌单中添加歌曲
int addMusic(HNODE *p, const char *mname)
{
	NODE *pnew = (NODE *)malloc(sizeof(NODE));
	pnew->name = (char *)malloc(strlen(mname)+1);
	assert(pnew->name != NULL);
	assert(pnew != NULL);

	strcpy(pnew->name, mname);

	if (isMPlistEmpty(p))
	{
		p->top = pnew;
	}
	else
	{
		p->tail->next =  pnew;
	}
	p->tail = pnew;
	pnew->next = NULL;
}
  • 删除二维链表中的链表,也就是删除歌单中的歌曲
int  delMusic(HNODE *h, const char *mname)
{
	if (isMPlistEmpty(h))
	{
		return 0;
	}

	NODE *p = h->top;
	NODE *q = NULL;
	
	while (p != NULL) 
	{	
		if (!strcmp(p->name, mname))    //此处需要分类讨论,因为在二维链表中删除链表有多种可能
		{
			if (h->top == p && h->tail == p)    //当删除的结点为第一个结点,同时也为最后一个节点是
			{
				h->top = NULL;
				h->tail = NULL;
			}
			else if (p == h->top)      			 //当删除的结点为第一个结点,但不为最后一个结点
			{
				h->top = p->next;
			}
			else if (p == h->tail)				//当删除的结点为最后一个结点,但不为第一个结点
			{
				q->next = NULL;
				h->tail = q;	
			}
			else							//中间的结点
			{
				q->next = p->next;
			}
			freeNode(p);
			break;
		}	
		q = p;
		p = p->next;
	}
}

  • 清空二维链表,也就是清空歌单内的所有歌曲
void clearList(HNODE *p)
{
	NODE *q = p->top;

	while (q != NULL)
	{
		p->top = q->next;
		freeNode(q);
		q = p->top;
	}

	p->top = NULL;
	p->tail = NULL;
	p->num = 0;
}
  • 销毁链表,也就是彻底删除歌单
void destroyAllList(HNODE **head)
{
	HNODE *p = (*head)->next;

	while (p != *head)
	{
		destroyList(*head, p->name);
		p = (*head)->next;
	}

	free(*head);
	*head = NULL;
}
  • 打印二维链表里的所有内容,也就是打印所有歌单以及每个歌单内的歌曲
void printHList(HNODE *head)
{
	HNODE *p = head->next;

	while (p != head)
	{
		printf("歌单名:%s   歌曲数量:%d\n", p->name, p->num);
		NODE *q = p->top;

		while (q != NULL)
		{
			printf("%s\n", q->name);
			q = q->next;
		}
		p = p->next;
	}
}

以上代码均以展示核心内容为主

1.3 队列

  • 队列的基本知识

队列是一个有序列表,可以用数组或者链表实现,遵循先入先出原则;即先存入队列的数据要先取出,后存入的要后取出,就好比日常生活中我们排队买票一样,正常情况下先到的人可以先买,后到的人只能后买;

在这里插入图片描述

  • 链表实现队列代码ruxai
  • 文件所包含的库文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

  • 创建队列以及队列的操作
//定义链表的结点
typedef struct node
{
	int data;
	struct node *next;

}NODE;

typedef struct queue
{
	NODE *head;
	NODE *tail;

}QUEUE;

//初始化队列的头结点
QUEUE *createQueue()
{
	QUEUE *mqueue = (QUEUE *)malloc(sizeof(QUEUE));
	assert(mqueue != NULL);

	mqueue->head = NULL;
	mqueue->tail = NULL;
	return mqueue;

}
//判断队列是否为空
int isEmptyQueue(QUEUE *queue)
{
	if (queue->head == NULL)
	{
		return 0; //表示为空
	}

	return 1;
}
//向队列中添加数据——入队
void addQueue(QUEUE *queue, int data)
{
	NODE *pnew = (NODE *)malloc(sizeof(QUEUE));
	assert(pnew != NULL);

	pnew->data = data;
	pnew->next = NULL;

	if (!isEmptyQueue(queue))
	{
		queue->head = pnew;
		queue->tail = pnew;
	}
	else
	{
		queue->tail->next = pnew;
		queue->tail = pnew;
	}
	
}
//出队
void delQueue(QUEUE *queue)
{
	NODE *temp  = queue->head;

	if (!isEmptyQueue(queue))
	{
		printf("队列已经为空!\n");
		return ;
	}
	else
	{
		queue->head = temp->next;
		queue->tail = NULL;
	}

	free(temp);
}
//打印队列
void printQueue(QUEUE *queue)
{
	NODE *q = queue->head;

	while (q != NULL)
	{
		printf("%d  \n", q->data);
		q = q->next;
	}
}
//获取队列的第一个元素的值
int getHeadQueue(QUEUE *queue)
{
	int headNum;
	NODE *q = queue->head;

	if (q !=  NULL)
	{
		headNum = q->data;
	}

	return headNum;
}
//清空队列
void clrQueue(QUEUE *queue)
{
	while (isEmptyQueue(queue))
	{
		delQueue(queue);
		if (queue->head == NULL)
		{
			break;
		}
	}	
}
//销毁整个队列
void destroyQueue(QUEUE **queue)
{
	clrQueue(*queue);
	QUEUE *temp = *queue;
	*queue = NULL;
	free(temp);
}
  • 测试函数
int main(int argc, char const *argv[])
{
	QUEUE *mqueue = createQueue();
	printf("-------进入队列打印-------\n");
	addQueue(mqueue, 5);
	addQueue(mqueue, 2);
	addQueue(mqueue, 0);
	printf("\n");
	printQueue(mqueue);

	printf("-------出队列打印-------\n");
	delQueue(mqueue);
	printf("\n");
	printQueue(mqueue);

	printf("-------清空队列打印-------\n");
	clrQueue(mqueue);
	printf("\n");
	printQueue(mqueue);

	printf("-------销毁队列-------\n");
	destroyQueue(&mqueue);
	printf("\n");
	printQueue(mqueue);
	
	return 0;
}

3. 栈

3.1 栈的理解

  • 栈通俗一点其实就是一个后进先出的线性表,就比如你往水杯里面放口径一样大的镜片,先放进去的最后才能拿出来;

在这里插入图片描述

3.2 通过链表的形式来实现对栈的操作

  • 文件所包含的库文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

  • 栈的定义初始化以及操作
typedef struct node
{
	int data;
	struct node *next;

}NODE;

typedef struct stack
{
	NODE *top;

}STACK;

STACK *createStack()
{
	STACK *mstack = (STACK *)malloc(sizeof(STACK));
	assert(mstack != NULL);

	mstack->top = NULL;
	return mstack;
}

int isEmptyStack(STACK *mstack)
{
	if (mstack->top == NULL)
	{
		return 1;
	}

	return 0;
}
void pushStack(STACK *mstack, int data)
{
	NODE *pnew = (NODE *)malloc(sizeof(NODE));
	assert(pnew != NULL);

	pnew->data = data;
	pnew->next = NULL;

	if (isEmptyStack(mstack))
	{
		mstack->top = pnew;
	}
	else
	{
		pnew->next = mstack->top;
		mstack->top = pnew;
	}
}

void popStack(STACK *mstack)
{
	if (isEmptyStack(mstack))
	{
		printf("空栈,无需删除!\n");
	}
	else
	{
		NODE *temp = mstack->top;
		mstack->top = temp->next;
		free(temp);
	}
}

void printStack(STACK *mstack)
{
	NODE *p = mstack->top;

	while (p != NULL)
	{
		printf("%d\n", p->data);
		p = p->next;
	}
}

int getTopStack(STACK *mstack)
{
	NODE *p = mstack->top;
	int top_num;

	if (p != NULL)
	{
		top_num = p->data;
	}

	return top_num;
}

void clearStack(STACK *mstack)
{
	while (isEmptyStack(mstack))
	{
		popStack(mstack);
	}
}

void destroyStack(STACK **mstack)
{
	clearStack(*mstack);
	free(*mstack);
	*mstack = NULL;
}
  • 测试代码
int main(int argc, char const *argv[])
{
	QUEUE *mqueue = createQueue();
	printf("-------进入队列打印-------\n");
	addQueue(mqueue, 5);
	addQueue(mqueue, 2);
	addQueue(mqueue, 0);
	printf("\n");
	printQueue(mqueue);

	printf("-------出队列打印-------\n");
	delQueue(mqueue);
	printf("\n");
	printQueue(mqueue);

	printf("-------清空队列打印-------\n");
	clrQueue(mqueue);
	printf("\n");
	printQueue(mqueue);

	printf("-------销毁队列-------\n");
	destroyQueue(&mqueue);
	printf("\n");
	printQueue(mqueue);


	return 0;
}

二. 重难点问题

1. 如何将四则运算中的中缀如何转换成后缀形式(个位数字的四则运算)?

  • 可以参考一下几个步骤

第一步:对要进行运算的式子进行遍历
第二步:当遇到左括号时,直接将括号压入到栈
第三步:当遇到计算的数字字符时,直接添加到队列;遇到算数运算符号时需要比较优先级:

  1. 当栈中没有运算符号时,直接压入栈中,当栈中栈顶的运算符号的优先级比较低时,也直接压入栈中;
  2. 当栈顶的运算符号的优先级高于时,则需要先将栈中的运算符号出栈,然后加入到队列中;如果栈中还有运算符号则接着进行比较;
    第四步:当遇到右括号时,将栈内的运算符号逐个取出,并加入到队列,直到遇到与之配对的括号,将括号废除;
  • 对后缀形式的式子进行运算处理:

第一步:当遇到数字符号时,将其压入到栈中
第二步:当遇到运算符号时,将栈中与之最近的两个数字取出来进行运算,形式为“左数 运算符号 右数”, 运算结束将结果继续压入栈中;
第三步:将所有符号与数字处理完则剩下结果

三. 自我总结

但是我也收获了很多,对一些更高级的知识有了憧憬,比如链表相比数组的优点和独立性,队列和栈在解决一些现实问题的实用性。
学习完二维链表、栈还有队列之后,怎么说可以说是被打的溃不成军,很多知识点又难又复杂,就像转迷宫一样;

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值