数据结构(顺序栈、链式栈、顺序队列、链式队列)笔记-day9

目录

前言

一、线性表(栈)

1.1 顺序栈

1.2 顺序栈实操

1.3 链式栈

1.4 链式栈实操

二、线性表(队列)

2.1 顺序队列

2.2 顺序队列实操(增删改查)

2.3 链式队列

总结



前言

手撕顺序栈、链式栈,拳打顺序队列、链式队列,接下来上菜!!


提示:以下是本篇文章正文内容,下面案例可供参考

一、线性表(栈)

栈和队列都是线性表  线性结构

举例:
  (1)只能在栈顶进行插入和删除的线性表叫栈(堆栈)
  (2)规则
			生活案例: 一个杯子,往杯子里面放大饼,先放进去的大饼,最后一个拿出来  
			先进后出(FILO),后进先出(LIFO) 
			I in 			O out			F first 			L last 
  (3)栈 : 栈顶和栈底
			栈针:指向栈顶的指针(top),始终指向栈顶 //栈针,不是C语言上的 指针的改变
//从实际应用的角度考虑:
//顺序表 存下5个整数 11 22 33 44 55  应用频繁使用查找功能,你就用顺序表  
//链表   存下5个整数 11 22 33 44 55  应用频繁使用插入和删除功能,你就用链表

栈是一种思想 先进后出 
你可以用数组实现栈的思想  ----- 顺序栈 ,在内存当中是连续存储的
也可以用链表去实现栈的思想 ----- 链式栈, 在内存当中是不连续存储的

顺序栈和链式栈的区别是什么??
存储结构不用,顺序栈, 顺序存储结构 链式栈 链式存储结构

1.1 顺序栈

顺序表与顺序栈结构体的对比

/顺序栈  sequence 顺序  stack 栈 
逻辑结构: 线性结构 
存储结构:顺序存储 

//顺序表的结构体
#define N 100
typedef struct 
{
	int data[N]; //用来存储有效元素的数组
	int last; //last的值,时刻代表有效元素的个数
}seqlist_t;
//顺序表的插入位置在哪儿都可以插入 
11 22 33 44 55
1000 11 22 33 44 55  //插入在头
11 22 33 44 55 1000  //插入在尾巴
11 22 33  1000 44 55 //插入在中间
//顺序栈的结构体
#define MAX 5
typedef struct 
{
	int data[MAX]; //用来存储有效数据的数据
	int top; //top栈针,top的值,也时刻代表数组中有效元素的个数
}seqstack_t;

 顺序栈的操作(增删改查)

栈顶(栈顶就是顺序表的尾巴进行插入和删除操作)进行插入和删除
      顺序栈操作                     顺序表操作
	1.创建一个空的栈createEmptyStack 创建一个空的顺序表
	2.入栈pushStack()  在顺序表的尾巴上插入一个数据 有效元素个数+1
	3.出栈popStack()   删除顺序表的尾巴 尾巴里面的数据域返回  有效元素个数-1
	//popStack函数的返回值,就是栈顶的元素(出栈的数据)
	4.判断栈是否为满isFullStack()    判断顺序表是否未满
	5.判断栈是否为空isEmptyStack()   判断顺序表是否为空
	6.获取栈顶元素int getTopValue()  将顺序表的尾巴节点里面的数据域返回,不讲栈顶元素删除
	7.清空栈          clearStack() 清空顺序表

 具体代码举例,上菜!!  慢慢品尝

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

#define MAX 5
typedef struct 
{
	int data[MAX];//用来保存有效元素的数组
	int top;//栈针,top的值时刻代表有效元素的个数,同时也被当做数组的下标来使用
}seqstack_t;

//1.创建一个空的顺序栈
seqstack_t* createEmptyStack()
{
	//申请一个结构体空间大小
	seqstack_t* p = (seqstack_t*)malloc(sizeof(seqstack_t));
	if(p == NULL)
	{
		printf("createEmptyStack malloc failed!!\n");
		return NULL;//返回值是NULL,没用-1,是因为函数的返回值是指针类型
	}
	//因为是空的栈,所以有效元素个数要初始化为0
	p->top = 0;
	return p;
}
//2.入栈
void pushStack(seqstack_t* p, int x)
{
	//容错判断
	if(isFullStack(p))
	{
		printf("isFullStack!!\n");
		return;//提前结束函数
	}
	//有效元素个数,当做下标,正好是栈的尾巴
	p->data[p->top] = x;
	//有效元素个数+1
	p->top++;
}
//3.判断栈是否为满 满返回1,未满返回0
int isFullStack(seqstack_t* p)
{
	//有效元素个数 == 数组的最大长度就是 满
	return p->top == MAX ? 1 : 0;
	//return p->top == MAX;
}
//4.出栈,将栈顶的最后一个有效元素返回
int popStack(seqstack_t*p)
{
	//容错判断
	if(isEmptyStack(p))
	{
		printf("isEmptyStack!!\n");
		return;
	}
	p->top--;//有效元素个数-1,正好的得到即将出栈最后一个有效元素的下标
	return p->data[p->top];
}
//5.判断栈是否为空,空返回1,未空返回0
int isEmptyStack(seqstack_t* p)
{
	//有效元素个数 == 0 栈空
	return p->top == 0 ? 1 : 0;
	//return p->top == 0;
}
//6.获取栈顶数据元素的值,并不是将栈顶的数据元素删除
int getTopValue(seqstack_t* p)
{
	//容错判断
	if(isEmptyStack(p))
	{
		printf("isEmptyStack!!\n");
		return;
	}
	return p->data[p->top-1];//有效元素个数-1 == 最后一个有效元素的下标
}
//7.清空栈,有效元素个数为0,即空的栈
void clearStack(seqstack_t* p)
{
	//有效元素个数为0,就是空栈
	p->top = 0;
}

int main(int argc, const char *argv[])
{
	seqstack_t* p = createEmptyStack();
	pushStack(p,11);
	pushStack(p,22);
	pushStack(p,33);
	pushStack(p,44);
	pushStack(p,55);
	pushStack(p,66);//入栈失败,因为MAX == 5
	printf("栈顶是%d\n",getTopValue(p));
//	clearStack(p);
	while(!isEmptyStack(p))//只要栈不为空,就进入,继续出栈
	{
		printf("%d ",popStack(p));
	}
	//入栈是 11 22 33 44 55 出栈 55 44 33 22 11
	printf("\n");
	//这个while循环结束后,已经是空的栈了
	return 0;
}
#####练习#####
定义一个函数,将其转换为二进制数,将二进制位存储到栈内,再出栈打印输出	

先 % 再 /  

先求出来的是低位,后求出来是高位 

我们打印的时候 先打印高位 后打印低位 

用栈的思想实现:每求出一个二进制位就入栈,所有的二进制位入栈后,再统一输出
void showBin(seqstack_t *p, int num)
{
	//先%再/,一直到0
	while(num != 0)
	{
		pushStack(p,num % 2);	
		num /= 2;
	}
	//将所有的二进制位出栈,打印输出
	while(!isEmptySeqStack(p))
	{
		printf("%d ",popStack(p));
	}
	printf("\n");
}

int main(int argc, const char *argv[])
{
	seqstack_t *p = createEmptySeqstack(100);
	showBin(p,15);
	return 0;
}

1.2 顺序栈实操

练习:使用顺序栈实现计算器,例如:3+5*2-1+8*2=?
char a[] = "3+5*2-1+8*2"
5 * 2 == 10
10 + 3 == 13
13 - 1 == 12
2 * 8 = 16
12 + 16 = 28
上面的表达式可以用栈来实现
创建2个栈 
1 操作数栈(3, 5, 2, 1, 8, 2) 专门存数字 
2 运算符栈(+  * - + *)  专门存运算符'+'也是整数 
自左至右扫描表达式,如果遇到数字,进入操作数栈
如果遇到运算符  判断
				如果当前运算符栈为空,直接将运算符入运算符栈
				如果当前运算符栈不为空,将栈顶的运算符与当前运算符进行优先级比较 
				如果,当前运算符优先级高于栈顶运算符优先级,将当前运算符入运算符栈 
				如果,当前运算符优先级不高于栈顶运算符优先级,将栈顶运算符从栈中取出,再从操作数栈中取出
				两个数,进行运算,运算结果后,将结果数字,再次放入操作数栈
#include <stdio.h>
#include <stdlib.h>

#define MAX 100
typedef struct 
{
	int data[MAX];//用来保存有效元素的数组
	int top;//栈针,top的值时刻代表有效元素的个数,同时也被当做数组的下标来使用
}seqstack_t;

//1.创建一个空的顺序栈
seqstack_t* createEmptyStack()
{
	//申请一个结构体空间大小
	seqstack_t* p = (seqstack_t*)malloc(sizeof(seqstack_t));
	if(p == NULL)
	{
		printf("createEmptyStack malloc failed!!\n");
		return NULL;//返回值是NULL,没用-1,是因为函数的返回值是指针类型
	}
	//因为是空的栈,所以有效元素个数要初始化为0
	p->top = 0;
	return p;
}
//2.入栈
void pushStack(seqstack_t* p, int x)
{
	//容错判断
	if(isFullStack(p))
	{
		printf("isFullStack!!\n");
		return;//提前结束函数
	}
	//有效元素个数,当做下标,正好是栈的尾巴
	p->data[p->top] = x;
	//有效元素个数+1
	p->top++;
}
//3.判断栈是否为满 满返回1,未满返回0
int isFullStack(seqstack_t* p)
{
	//有效元素个数 == 数组的最大长度就是 满
	return p->top == MAX ? 1 : 0;
	//return p->top == MAX;
}
//4.出栈,将栈顶的最后一个有效元素返回
int popStack(seqstack_t*p)
{
	//容错判断
	if(isEmptyStack(p))
	{
		printf("isEmptyStack!!\n");
		return;
	}
	p->top--;//有效元素个数-1,正好的得到即将出栈最后一个有效元素的下标
	return p->data[p->top];
}
//5.判断栈是否为空,空返回1,未空返回0
int isEmptyStack(seqstack_t* p)
{
	//有效元素个数 == 0 栈空
	return p->top == 0 ? 1 : 0;
	//return p->top == 0;
}
//6.获取栈顶数据元素的值,并不是将栈顶的数据元素删除
int getTopValue(seqstack_t* p)
{
	//容错判断
	if(isEmptyStack(p))
	{
		printf("isEmptyStack!!\n");
		return;
	}
	return p->data[p->top-1];//有效元素个数-1 == 最后一个有效元素的下标
}
//7.清空栈,有效元素个数为0,即空的栈
void clearStack(seqstack_t* p)
{
	//有效元素个数为0,就是空栈
	p->top = 0;
}

//比较优先级函数
int getPriority(char oper)
{
	switch(oper)
	{
	case '+':
	case '-':
		return 0;
	case '*':
	case '/':
		return 1;
	}
}

int calcNum(int num1, char oper, int num2)
{
	switch(oper)
	{
	case '+':
		printf("%d + %d = %d\n",num1,num2,num1+num2);
		return num1 + num2;
	case '-':
		printf("%d - %d = %d\n",num1,num2,num1-num2);
		return num1 - num2;
	case '*':
		printf("%d * %d = %d\n",num1,num2,num1*num2);
		return num1 * num2;
	case '/':
		printf("%d / %d = %d\n",num1,num2,num1/num2);
		return num1 / num2;
	}
}

int main(int argc, const char *argv[])
{
	int in,out;//用来保存优先级
	int num1,num2;//用来保存出栈的两个数字
	char oper;//出栈的运算符
	int ret;//用来保存计算结果
	char a[] = "3+5*2-1+8*2";
	seqstack_t* numStack = createEmptyStack();//操作数栈
	seqstack_t* operStack = createEmptyStack();//运算符栈
	//遍历字符串,只要是数字就进操作数栈,运算符,需要判断
	int i = 0;
	while(a[i] != '\0')
	{
		if(a[i] >= '0' && a[i] <= '9')//只要是数字就入操作数栈
		{
			pushStack(numStack, a[i]-'0');// '3' - '0' == 3
		}
		else//运算符 
		{
			if(isEmptyStack(operStack))//如果运算符栈为空,直接将运算符入栈
			{
				pushStack(operStack,a[i]);//运算符入栈
			}
			else//运算符栈不为空,需要将栈外的运算符的优先级与栈内栈顶的运算符优先级做比较
			{
				out = getPriority(a[i]);//获取栈外运算符优先级
				in = getPriority(getTopValue(operStack));//获取栈顶运算符优先级
				if(out > in)//栈外优先级高于栈内,入栈
				{
					pushStack(operStack,a[i]);
				}
				else//栈外的优先级,不高于栈内的优先级
				{
					//将栈内的运算符出栈,再从操作数栈取出两个数
					oper = popStack(operStack);//出栈运算符
					num2 = popStack(numStack);//先出栈的数字,放在运算符的右边
					num1 = popStack(numStack);//后出栈的数字,放在运算符的左边
					ret = calcNum(num1, oper, num2);//得到计算结果
					//将计算的结果入操作数栈
					pushStack(numStack,ret);
					i--;//因为下一次循环,我要继续拿当前的运算符与栈顶的运算符进行优先级比较
				}
			}
		}
		i++;
	}
	//上面的循环结束后,运算符栈内和数字栈内有残留
	while(!isEmptyStack(operStack))//只要运算符栈不为空
	{
		//将栈内的运算符出栈,再从操作数栈取出两个数
		oper = popStack(operStack);//出栈运算符
		num2 = popStack(numStack);//先出栈的数字,放在运算符的右边
		num1 = popStack(numStack);//后出栈的数字,放在运算符的左边
		ret = calcNum(num1, oper, num2);//得到计算结果
		//将计算的结果入操作数栈
		pushStack(numStack,ret);
	}
	printf("%s = %d\n",a,popStack(numStack));
	return 0;
}

1.3 链式栈

/顺序栈  sequence 顺序  stack 栈 
逻辑结构: 线性结构 
存储结构:顺序存储 

//链表的结构体
typedef struct node_t//链表的插入位置在哪儿都可以插入 
{
	int data; //用来存储有效元素的数据域        i<=post-1
	struct node_t *next; //指针next指向的位置,由插入位置的前一个节点
}link_node_tlist_t;
11 22 33 44 55
1000 11 22 33 44 55  //插入在头
11 22 33 44 55 1000  //插入在尾巴
11 22 33  1000 44 55 //插入在中间
//链式栈的结构体的插入位置只能在栈顶位置
typedef struct node_t
{
	int data; //用来存储有效数据的数据
	struct node_t* next; //头插法,指针时刻指向链表中头节点的下一个节点
}link_node_t;

 链式栈的操作思想(增删改查)

栈顶(栈顶就是链表的表头进行插入和删除操作)
1.创建一个空的栈createEmptyLink 创建一个空的链表
2.入栈pushLink() 在链表的表头上插入一个数据,插队到第一个
3.出栈popLink()  删除链表的表头里面的指针域
//popLink函数的返回值,就是栈顶的元素(出栈的数据)
4.判断栈是否为空isEmptyLink() 判断链表头指针域是否为空
5.获取栈顶元素int getTopValue()将表头里的数据域返回,释放刚刚读取的空间
6.清空栈  clearLink() 将指针赋值为NULL

1.4 链式栈实操

链式栈案例代码,上菜!!

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

typedef struct node_t
{
	int data;//数据域
	struct node_t *next;//指针域
}linknode_t;

//1.创建空的链表
linknode_t *createEmptyLinklist()
{
	//创建一个节点,作为有头单向链表的头节点
	linknode_t *p = malloc(sizeof(linknode_t));//malloc函数的返回值
	//申请成功,返回的是申请空间的首地址  失败,返回的是空指针
	if(NULL == p)
	{
		printf("createEmptyLinklist malloc failed!!\n");
		return NULL;
	}
	//给节点赋值
	p->next = NULL;
	return p; 
}

//2.向链表的表头位置插入数据
int insertIntoLinklist(linknode_t *p, int x)
{
	int i;
	//1.将头指针p赋值给指针m,保证p始终指向第一个位置
	linknode_t *m=p;
	//2.创建一个新的节点,保存数据
	linknode_t *pnew = malloc(sizeof(linknode_t));
	if(NULL == pnew)
	{
		printf("pnew's malloc failed!!\n");
		return -1;
	}
	//3.给新节点装数据
	pnew->data = x;
	pnew->next = NULL;
	//将新节点链接到链表中,先连后面,再连前面
	pnew->next = m->next;
	m->next = pnew;
	return 0;

}

//3.遍历打印链表
void showLinklist(linknode_t *p)
{
	while(p->next != NULL)
	{
		p = p->next;
		printf("%d ",p->data);
	}
	printf("\n");
}
//4.求链表的长度
int getLengthLinklist(linknode_t *p)
{
	int count = 0;
	while(p->next != NULL)
	{
		p = p->next;
		count++;
	}
	return count;
}
//5.判断单向链表是否为空 空返回1 非空返回0
int isEmptyLinklist(linknode_t *p)
{
	return p->next == NULL;
}
//6.删除链表中指定位置的数据
int deletePostLinklist(linknode_t *p)
{
	//0.对删除进行容错判断
	if(isEmptyLinklist(p))
	{
		printf("deletePostLinklist failed!!\n");
		return -1;
	}
	//1.将头指针p赋值给指针m,保证p始终指向第一个位置
	linknode_t *m=p;
	//2.定义一个pdel指针,指向被删除的节点
	linknode_t * pdel = m->next;
	//3.跨过被删除节点
	m->next = pdel->next;
	//4.释放被删除的节点
	free(pdel);
	return 0;
}
//7.清空链表
void clearLinklist(linknode_t *p)
{
	linknode_t *pdel = NULL;
	while(p->next != NULL)//只要链表不为空,就进行砍头操作
	{
		//1.将头指针移动到删除位置的前一个位置 第1步可以省略,因为每次删除的头节点的下一个节点
		//也就意味着,头节点是每次删除节点的前一个位置p可以保持不动
		//2.定义一个pdel指向被删除的节点
		pdel = p->next;
		//3.跨过被删除的节点
		p->next = pdel->next;
		//4.释放被删除节点
		free(pdel);
	}
}

//8.查找指定数据出现的位置,出现在第几个位置
//x代表的是被查找的数据
int searchDatePostLinklist(linknode_t *p,int x)
{
	int i = 0;
	while(p->next != NULL)
	{
		p = p->next;
		i++;
		if(p->data == x)
		{
			return i;
		}
	}
	return -1;//-1代表没有找到
}


int main(int argc, const char *argv[])
{
	linknode_t *h = createEmptyLinklist();	
	insertIntoLinklist(h,10);
	insertIntoLinklist(h,20);
	insertIntoLinklist(h,30);
	insertIntoLinklist(h,40);
	showLinklist(h);
	printf("len is %d\n",getLengthLinklist(h));
	printf("30 post:%d\n",searchDatePostLinklist(h,30));
	deletePostLinklist(h);
	showLinklist(h);
	clearLinklist(h);
	showLinklist(h);
	return 0;
}

二、线性表(队列)

队列
(1)只能在两端进行插入和删除操作的线性表(在队列头进行删除,队列的尾进行插入)
	去银行办理业务: 先来的,先办业务,后来的,后办业务
(2)规则	先进先出,后进后出 	FIFO       LILO	
(3)有两个指针
		存数据端(rear) //后面的插入
		取数据端(front)//前面的删除  
		用数组实现的队列称为循环队列 也叫顺序队列
		用链表实现的队列叫链式队列 
		
		顺序队列(顺序存储)和链式队列(链式存储)最大的区别是????
		存储结构不同:
			顺序队列(循环队列):顺序存储结构,在内存当中是连续存储的
			链式队列:链式存储结构,在内存当中是不连续存储的

2.1 顺序队列

循环队列中最多存储N-1个元素,N代表数组的元素个数

//顺序表的结构体
#define MAX 5
typedef struct 
{
	int data[MAX];//用来保存有效元素的数组
	int top;//栈针,top的值时刻代表有效元素的个数,同时也被当做数组的下标来使用
}seqstack_t;
//顺序队的结构体 queue 队列  sequence 顺序
#define N 5
typedef struct
{
	int data[N]; //用来存储数据的 
	int rear; //后面 队列尾 插入 p->data[p->rear] 存数据端 
	int front;//前面 队列头 删除 p->data[p->front] 取数据端
}sequeue_t;		

2.2 顺序队列实操(增删改查)

#include <stdio.h>
#include <stdlib.h>
#define N 5
typedef struct 
{
	int data[N];
	int rear;//后面 队列的尾巴 插入
	int front;//前面 队列的头 删除
}sequeue_t;

//1.创建一个空的队列
sequeue_t *createEmptyQueue()
{
	//1.申请一个结构体空间的大小
	sequeue_t *p = malloc(sizeof(sequeue_t));
	if(NULL == p)
	{
		printf("createEmptyQueue malloc failed!!\n");
		return NULL;
	}
	//2.给结构体成员变量进行赋值
	p->rear = 0;
	p->front = 0;
	//p->front 和 p->rear的初始值只要相等就可以(初始值不能超出数组下标范围)
	return p;
}
//2.入列,插入,用的是rearx代表入列的数据
int inQueue(sequeue_t *p,int x)
{
	//0.容错判断,判断队列是否为满
	if(isFullQueue(p))
	{
		printf("isFullQueue!!\n");
		return -1;
	}
	//1.入列,插入数据
	p->data[p->rear] = x;
	//2.rear++
	p->rear = (p->rear+1) % N;//rear当做下标,不能数组越界,所以%N
	return 0;
}
//3.判断队列是否为空 空返回1,非空返回0
int isEmptyQueue(sequeue_t *p)
{
	return p->rear == p->front;
}
//4.出列,删除,用的是front
int outQueue(sequeue_t *p)
{
	//容错判断 
	if(isEmptyQueue(p))
	{
		printf("isEmptyQueue!!\n");
		return -1;
	}
	int x =  p->data[p->front];
	p->front = (p->front+1)%N;
	return x;
}
//5.判断队列是否为满 满返回1 为满返回0 
int isFullQueue(sequeue_t *p)
{
	return (p->rear+1) % N == p->front ? 1 : 0;
}
//6.求队列的长度
int getLengthQueue(sequeue_t *p)
{
	if(p->rear >= p->front)
		return p->rear - p->front;
	else
		return p->rear - p->front + N;
	/*
	if(p->rear == p->front)
	{
		return 0;
	}
	else if(p->rear > p->front)
	{
		return p->rear - p->front;
	}
	else if(p->rear < p->front)
	{
		return p->rear - p->front + N;
	}
	*/

}

int main(int argc, const char *argv[])
{
	sequeue_t *p = createEmptyQueue();
	inQueue(p,10);
	inQueue(p,20);
	inQueue(p,30);
	inQueue(p,40);
	inQueue(p,50);//入列失败,最多能存N-1个
	while(!isEmptyQueue(p))//打印输出 10 20 30 40 先进先出
	{
		printf("%d\n",outQueue(p));
	}
	return 0;
}

2.3 链式队列

数组和链表都可以实现栈和队列

顺序栈和链式栈最大区别是什么??   存储结构 
顺序队列和链式队列最大的区别是什么?? 存储结构  

数组实现的内存当中是连续存储的 //存储结构  顺序存储结构 
链表实现的内存当中是不连续存储的 //存储结构  链式存储结构


为什么说数组的元素个数为N的循环队列,最多能够存储 N-1 个数据??

因为循环队列 p->rear == p->front 做为队列为空的条件 那么队列为满的条件就要更改 
在入队列之前, 提前对rear的下一个位置,判断是否等于 front,来判断队列是否为满
(p->rear + 1) %  N == p->front

	//方法一 
	if(p->rear >= p->front)
		return p->rear - p->front;
	else 
		return p->rear + N - p->front;
	//方法二 
	return (p->rear + N - p->front) % N;

总结

这里对文章进行总结:手撕顺序栈、链式栈,拳打顺序队列、链式队列,接下来累了,休息消化!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值