解密栈、队列、链表


栈和队列都属于 线性表,只不过它们的基本操作是线性表操作的子集,他们是操作受限的线性表

一、栈

栈只能在表的一端进行插入与删除操作,称这端为栈顶,另外一段称为栈底。 (像M416弹夹一样)栈元素后进先出(last in first out) 简称LIFO结构。栈分为顺序栈和链栈,链栈操作是线性表的特例,操作易于实现,故此处只讲顺序栈。

一般操作作用
void initStack(seqStack* s)初始化栈
bool stackEmpty(seqStack s)判栈空
bool stackFull(seqStack s)判栈满
bool stackTop(seqStack & S)读栈顶元素
void pushStack(seqStack* s, elementType x)入栈
bool popStack(seqStack & S)弹栈
void cyclepush(seqStack * s)输入循环入栈
void printStack(seqStack& S)打印栈中元素子函数
#include <iostream>
using namespace std;
typedef int elementType;
#define MaxLen 100
typedef struct
{
	elementType  data[MaxLen];
	int top;
}seqStack;

void initStack(seqStack* s)//初始化栈
{
	s->top = -1;
}

bool stackEmpty(seqStack s)//判栈空
{
	if(s.top == -1)
		return true;
	else
		return false;
}

bool stackFull(seqStack s)//判栈满
{
	if (s.top == MaxLen - 1)
		return true;
	else
		return false;
}

bool stackTop(seqStack & S)//读栈顶元素
{
	if (stackEmpty(S))
	{
		cout << endl << "空栈,无栈顶元素" << endl;
		return false; //空栈,返回false
	}
	else
	{
		cout << endl << "取出的栈顶元素为:" << S.data[S.top] << endl;
		return true;  //取得栈顶,返回true;取得的值有 x 传递。
	}
}

void pushStack(seqStack* s, elementType x)//入栈
{
	if (s->top == MaxLen - 1)
		cout<<"栈满"<<endl;
	else
	{
		s->top++;	
	//此处解释了为什么s->top从-1开始,因为存入第一个元素位置应该为0
		s->data[s->top] = x;
	}
}

bool popStack(seqStack & S) //弹栈
{
	elementType x;
	if (stackEmpty(S)) //空栈,没元素出栈,返回false
	{
		cout << "栈空,不能删除!" << endl;
		return false;
	}
	else
	{
		x = S.data[S.top];
		S.top--;
		cout << endl << "弹出栈顶元素:" << x << endl;
		return true;
	}
}

void cyclepush(seqStack * s)//输入循环入栈
{
	elementType x;
	cout << "请输入入栈元素(回车退出):" << endl;
	while (cin.peek() != '\n')	//cin.peek()返回值是指针指向的当前字符
	{
		cin >> x;
		pushStack(s, x);
	}
}

void printStack(seqStack& S)//打印栈中元素子函数
{
	if (stackEmpty(S))
	{
		cout << "当前为空栈。" << endl;
		return;
	}
	else
	{
		cout << endl << "当前栈内元素(栈底到栈顶):" << endl;
		int i = 0;//为什么从i=0开始?因为s->top=0是最先入栈的
		while (i <= S.top)
		{
			cout << S.data[i] << " ";
			i++;
		}
	}
	cout << endl;
}

int main()
{
	seqStack S;
	initStack(&S);  
	printStack(S);
	cyclepush(&S);
	printStack(S);
	stackTop(S);
	popStack(S);
	printStack(S);
	return 0;
}

二、队列

  • 队列即模仿人排队,队首(head)先得到服务,想要得到服务只能从队尾(tail)开始排队,换言之,只能在一端进行删除,另一端进行插入,符合先进先出原则。同样队列也是操作受限的线性表。(C__zhang关于队列基本操作的描述
  • 队列分为顺序队列和循环队列,如下为普通顺序队列的结构,用数组来存储数据,两个整型变量headtail分别表示队头和队尾指针,注意head,tail他们并没有正好指在第一和最后一个元素上,而是其中一个错开,要么head指在第一个元素前一位,要么tail指在最后一个元素后一位,这样方便区分队满和队空。
	#define MAXLEN 100
	typedef struct Queue
	{
		int data[MAXLEN];
		int head;
		int tail;
	};

思考1:队列存储结构和栈类似,那么为什么要一个头指针呢?
答案1:因为考虑到出队操作需要删除data[0]的数据,若没有队头指针需要把所有的元素前移一位,非常耗时,所以设置了队头指针,只需将其后移一位即可。

思考2:用数组存储数据的顺序队列有什么缺点?如何解决?
答案2:可定义一个非常大的数组,但是不管出队、入队都是指针后移,这样数组总会有用完的时候,队头前面有可能也还有在许多剩余的空间,这种情况并不是真的没有存储空间了,称为假溢出。由此引出循环队列解决这一问题。

  • 循环队列的结构与顺序队列相同,但是把data[ ]视为环状结构,如图data[0]紧接着data[Maxlen-1]
一般操作作用
void initQueue(seqQueue* Q)初始化
bool queueEmpty(seqQueue& Q)判队空
bool queueFull(seqQueue& Q)判队满
void queueFront(seqQueue& Q)取队头
void enQueue(seqQueue* Q, elementType x)入队
void outQueue(seqQueue* Q)出队
void cycleenQueue(seqQueue* Q)循环输入入队
void printQueue(seqQueue* Q)打印队列元素
  • !!! 循环队列如何不会溢出?当tail指向数组data[ ]最后一个单元时,即tail=Maxlen-1时,进行入队操作:tail++普通顺序队列一定会溢出,但循环队列会重新指向data[0]单元
    (如果此时data[0]单元为空仍然可以完成入队操作)
    对于front指针也是一样,当front=Maxlen-1时,front++后让其重新指向data[0]单元
  • 那么我们还发现因为循环队列的特性,fronttail可以出现在任何位置,对于front>tail的情况怎么计算如对位置呢?

可以手工判断,如下

	if (tail == Maxlen - 1)
		tail = 0;
	else
		tail++;

或者改为三目运算,如下

tail = (tail + 1 == Maxlen) ? 0 : tail++;

直接使用模运算求得入队位置tail

tail = (tail + 1 == Maxlen) % Maxlen;
  • 还有注意在printQueue函数中为何我多此一举定义了整型变量elementType n;?如果直接使用Q->front++的话当我们在取队头元素时就会错误,因为front的值已经变了。
    其次,注意该函数中的n=Q->front+1能否改为n=Q->front++
    答案是不能,因为n=Q->front++语句会先把Q->front自增1,但不会立即把值赋给Q,也就是说下一次使用n的时候它的值还是原来的值。
#include <iostream>
using namespace std;
typedef int elementType;
#define Maxlen 100
typedef struct sQueue//顺序队列存储结构
{
	elementType data[Maxlen];
	int front, tail;
}seqQueue;

void initQueue(seqQueue* Q) //参数号可用传值、引用
{
	Q->front = 0;  //空a队列
	Q->tail = 0;
}
bool queueEmpty(seqQueue& Q)
{
	if (Q.front == Q.tail)//!!!
	{
		cout << endl << "当前队列为空!" << endl;
		return true;  //队空,返回true
	}
	else
		return false;  //队不空
}

bool queueFull(seqQueue& Q)
{
	if (((Q.tail + 1) % Maxlen) == Q.front)//!!!
	{
		cout << endl << "队列已满!" << endl;
		return true;  //队空,返回true
	}
	else
		return false;  //不满,返回false
}
void queueFront(seqQueue& Q)//取队头
{
	if (queueEmpty(Q))
		cout << endl << "队空,不能取队头元素!" << endl;
	else
	{
		cout << endl << "队头元素为:" << Q.data[(Q.front + 1) % Maxlen] << endl; 
				//front指示的下一个单元才是队头元素
	}
}
void enQueue(seqQueue* Q, elementType x)
{
	if (queueFull(*Q))
		cout << endl << "队列已满,不能完成入队操作!" << endl;
	else
	{
		Q->tail = ((Q->tail) + 1) % Maxlen;  //计算入队位置,此处很重要
		Q->data[Q->tail] = x;  //填入数据x
							 //2行语句可否并未1行呢?
	}
}
void outQueue(seqQueue* Q)
{
	if (queueEmpty(*Q))
		cout << endl << "空队列,没有元素可供出队!" << endl;
	else
	{
		cout << endl << "出队执行!" << endl;
		Q->front = (Q->front + 1) % Maxlen;
	}
}

void cycleenQueue(seqQueue* Q)
{
	cout << endl << "请输入入队元素(回车结束):" << endl;
	elementType x;
	while (cin.peek() != '\n')	//cin.peek()返回值是指针指向的当前字符
	{
		cin >> x;
		enQueue(Q,x);
	}
}

void printQueue(seqQueue* Q)
{
	if (queueEmpty(*Q))
		cout << endl << "当前为空栈!" << endl;
	else
	{
		elementType n;
		n=Q->front+1;					//此处改为n=Q->front++;行不行?
		cout << endl << "当前队内元素(队头-->队尾):" << endl;
		while (n <= Q->tail)
		{
			cout <<Q->data[n]<< " ";
			n++;
		}
		cout << endl;
	}
}

int main()
{
	seqQueue s;
	initQueue(&s);
	queueEmpty(s);
	queueFull(s);
	cycleenQueue(&s);
	printQueue(&s);
	queueFront(s);
	outQueue(&s);
	queueFront(s);
	return 0;
}

三、链表

今天先写这点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值