数据结构(三)—— 栈和队列

一、栈

1.栈的概念

(1)只允许在一端进行插入和删除的线性表。允许插入和删除的一端叫做栈顶,而不允许插入和删除的另一端叫栈底。当栈中没有任何元素的时候叫空栈。

2.顺序栈

(1)定义:

  • 顺序栈是栈的顺序储存表示。
  • 实际上,顺序栈是指利用一块连续的储存单元作为栈元素的储存空间,只不过在C语言中是借用一维数组实现而已。
  • 因为一维数组的下标从0开始,栈空的时候 S.top < 0,因此空栈时栈顶指针 S.top == 1。

(2)实现

#pragma once
//SeqStack.h

#include<cstdio>
#include<cstdlib>
#define initSize 20
#define increments 10
typedef int SElemType;
typedef struct {
	SElemType* elem;
	int maxSize, top;
}SeqStack;

void overflowProcess(SeqStack& S) {
	S.maxSize += increments;
	SElemType* newArray = (SElemType*)malloc(sizeof(S.maxSize));
	for (int i = 0; i <= S.top; i++) newArray[i] = S.elem[i];
	free(S.elem);
	S.elem = newArray;
}
bool getTop(SeqStack& S, SElemType& x) {
	if (S.top == -1) return false;
	x = S.elem[S.top];
	return true;
}
bool stackEmpty(SeqStack& S) {
	return S.top == -1;
}
bool stackFull(SeqStack& S) {
	return S.top == S.maxSize;
}
int stackSize(SeqStack& S) {
	return S.top + 1;
}
bool Push(SeqStack& S, SElemType x) {
	if (stackFull(S)) overflowProcess(S);
	S.elem[++S.top] = x;
	return true;
}
bool Pop(SeqStack& S, SElemType& x) {
	if (S.top == -1)  return false;
	x = S.elem[S.top--];
	return true;
}
//SeqStack.cpp

#include "SeqStack.h"
void initStack(SeqStack& S) {
	S.elem = (SElemType*)malloc(initSize * sizeof(SElemType));
	S.maxSize = initSize;
	S.top = -1;
}

3.双栈共享同一栈空间

(1)特点,

  • 需要两个栈的时候,可以定义一个足够大的栈空间V[m],并将两个栈设为0号栈和1号栈。
  • 该空间的两端的外侧分别设为两个栈的栈底,用b[0],b[1]表示,让两个栈的栈顶b[0],b[1] 都往中间伸展,直到两个栈的栈顶相遇,才认为发生了溢出。

(2)实现

//DblStack.h

#pragma once
#include<cstdio>
#include<cstdlib>
#define m 20
typedef int SElemType;
typedef struct {
	int top[2], bot[2];
	SElemType V[m];
}DblStack;
//DblStack.cpp

#include "DblStack.h"
void initStack(DblStack& DS, SElemType x, int d) {
	DS.bot[0] = DS.top[0] = -1;
	DS.bot[1] = DS.top[1] = m;
}
bool Push(DblStack& DS, SElemType x, int d) {
	if (DS.top[0] + 1 == DS.top[1] || d != 0 && d != 1) return false;
	if (d == 0) DS.top[0]++;
	else DS.top[1]--;
	DS.V[DS.top[d]] = x;
	return true;
}
bool Pop(DblStack& DS, SElemType& x, int d) {
	if (d != 0 && d != 1 || DS.top[d] == DS.bot[d]) return false;
	x = DS.V[DS.top[d]];
	if (d == 0) DS.top[0]--;
	else DS.top[1]++;
	return true;
}

(3)问题:

  • 如果要求更多的栈共享同一个栈空间,使用顺序存储方式将使得处理十分复杂。
  • 解决的办法是采用链接方式作为栈的储存表示。

3.链式栈

(1)特点

  • 链式栈是栈的链接储存表示。采用链式栈来表示一个栈,表示结点的插入与删除。
  • 在程序中同时使用多个栈的情况下,采用链接表示不仅能够提高效率,还可以达到共享存储空间的目的。
//LinkStack.h

#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef int SElemType;
typedef struct node {
	SElemType data;
	struct node* link;
}LinkNode, *LinkStack;
//LinkStack.cpp

#include "LinkStack.h"
void initStack(LinkStack& S) {
	S = NULL;
}
void clearStack(LinkStack& S) {
	LinkNode* p;
	while (S != NULL) {
		p = S; S = S->link; free(p);
	}
}
bool stackEmpty(LinkStack& S) {
	return S == NULL;
}
bool Push(LinkStack& S, SElemType x) {
	LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
	p->data = x;
	p->link = S; S = p; return true;
}
bool Pop(LinkStack& S, SElemType& x) {
	if (S == NULL) return false;
	x = S->data;
	LinkNode* p = S;
	S = S->link;
	free(p);
	return true;
}
bool getTop(LinkStack& S, SElemType& x) {
	if (S == NULL) return false;
	x = S->data;
	return true;
}
int StackSize(LinkStack& S) {
	int sz = 0;
	LinkNode* p = S;
	while (p != NULL) {
		p = p->link;
		sz++;
	}
	return sz;
}

4.利用栈和队列判断一个字符序列是否回文

其实应该先把数据类型定义为 char 的。

#include<cstring>
#include "SeqStack.cpp"
#include "CircQueue.cpp"
bool ok(char* str) {
	SeqStack S;
	initStack(S);
	CircQueue Q;
	initQueue(Q);
	int ch1, ch2;
	for (int i = 0; i < strlen(str); i++) {
		enQueue(Q, str[i]);
		Push(S, str[i]);
	}
	while (!stackEmpty(S) && !queueEmpty(Q)) {
		Pop(S, ch1);
		deQueue(Q, ch2);
		if (ch1 != ch2) return false;
	}
	return true;

}

 

二、队列

1.循环队列

//CircQueue.h

#pragma once

#include<cstdio>
#include<cstdlib>
#define queSize 30
typedef int QElemType;
typedef struct {
	QElemType elem[queSize];
	int front, rear;
}CircQueue;
//CircQueue.cpp

#include "CircQueue.h"
void initQueue(CircQueue& Q) {
	Q.front = 0;
	Q.rear = 0;
}
bool queueEmpty(CircQueue& Q) {
	return Q.front == Q.rear;
}
bool queueFull(CircQueue& Q) {
	return (Q.rear + 1) % queSize == Q.front;
}
int queueSize(CircQueue& Q) {
	return (Q.rear - Q.front + queSize) % queSize;
}
bool enQueue(CircQueue& Q, QElemType x) {
	if (queueFull(Q)) return false;
	Q.elem[Q.rear] = x;
	Q.rear = (Q.rear + 1) % queSize;
	return true;
}
bool deQueue(CircQueue& Q, QElemType& x) {
	if (queueEmpty(Q)) return false;
	x = Q.elem[Q.front];
	Q.front = (Q.front + 1) % queSize;
	return true;
}
bool getFront(CircQueue& Q, QElemType& x) {
	if (queueEmpty(Q)) return false;
	x = Q.elem[Q.front];
	return true;
}

2.链式队列

(1)这个我真的想吐槽,首先,这个链式队列设计得和链表几乎没什么区别。其次,这个Q.rear,可以说没有任何作用。因为队尾的判断是Q.front == NULL。

//LinkQueue.h

#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef int QElemType;
typedef struct Node {
	QElemType data;
	struct Node* link;
}LinkNode;
typedef struct {
	LinkNode* front, * rear;
}LinkQueue;
//LinkQueue.cpp

#include "LinkQueue.h"
void initQueue(LinkQueue& Q) {
	Q.front = Q.rear = NULL;
}
void ClearQueue(LinkQueue& Q) {
	LinkNode* p;
	while (Q.front != NULL) {
		p = Q.front;
		Q.front = p->link;
		free(p);
	}
	Q.rear = NULL;
}
bool QueueEmpty(LinkQueue& Q) {
	return Q.front == NULL;
}
bool enQueue(LinkQueue& Q, QElemType x) {
	LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
	p->data = x;
	if (Q.front == NULL) { Q.front = Q.rear = p; }
	else { Q.rear = p; }
	return true;
}
bool deQueue(LinkQueue& Q, QElemType &x) {
	if (Q.front == NULL) return false;
	LinkNode* p = Q.front;
	x = p->data;
	Q.front = Q.front->link;
	free(p);
	if (Q.front = NULL) Q.rear = NULL;
	return true;
}
bool getFront(LinkQueue& Q, QElemType& x) {
	if (Q.front == NULL) return false;
	x = Q.front->data; return true;
}
int QueueSize(LinkQueue& Q) {
	LinkNode* p = Q.front;
	int sz = 0;
	while (p != NULL) {
		p = p->link;
		sz++;
	}
	return sz;
}

三、栈的应用

1.数制转换

#include "LinkStack.cpp"
int BaseTrans(int N, int k) {
	int i;
	long result = 0;
	LinkStack S;
	initStack(S);
	while (N != 0) {
		i = N % k;
		N /= k;
		Push(S, i);
	}
	while (!stackEmpty(S)) {
		Pop(S, i);
		result = result * 10 + (long)i;
	}
	return result;
}
void main(void) {
	int N, k;
	printf("请输入一个十进制非负整数:\n");
	scanf("%d", &N);
	printf("要转换为多少进制的数:\n");
	scanf("%d", &k);
	printf("%d转换为%d进制数为%d\n", N, k, BaseTrans(N, k));
}

2.括号匹配

#include "LinkStack.cpp"
void brachetMatch(char expr[]) {
	LinkStack S;
	initStack(S);
	int j, i = 0;
	char ch = expr[i];
	while (ch != '\0') {
		if (ch == '(') Push(S, i);
		else if (ch == ')') {
			if (!stackEmpty(S)) {
				Pop(S, j);
				printf("位置%d的'('与位置%d的')'匹配\n", j, i);
			}
			else printf("没有与位置%d的')'匹配的'('\n", i);
		}
		ch = expr[++i];
	}
	while (!stackEmpty(S)) {
		Pop(S, j);
		printf("没有与位置%d的'('匹配的')'!\n", j);
	}
}
int main(void) {
	char A[] = "(a*(b+c)-d)\0";
	char B[] = "(a+b))(\0";
	printf("%s\n", A);
	brachetMatch(A);
	printf("%s", B);
	brachetMatch(B);
	return 0;
}

3.后缀表示计算表达式的值

#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std;
#define stkSize 20
bool DoOperator(double OPEN[], int& top, char op) {
	double left, right;
	if (top == -1) {
		printf("缺少右操作数!\n");
		return false;
	}
	right = OPEN[top--];
	if (top == -1) {
		printf("缺少左操作数!\n");
		return false;
	}
	left = OPEN[top--];
	switch (op) {
	case'+': OPEN[++top] = left + right; break;
	case'-': OPEN[++top] = left - right; break;
	case'*': OPEN[++top] = left * right; break;
	case'/': 
		if (fabs(right) < 1e-6) {
			printf("Divide by zero!\n");
			return false;
		}
		else {
			OPEN[++top] = left / right;
			break;
		}
	default: return false;
	}
	return true;
}
int main() {
	char A[] = "5964-*+93/-#";
	double OPEN[stkSize], result;
	int i = 0, top = -1;
	char ch = A[i++];
	while (ch != '#') {
		if (ch >= '0' && ch <= '9') OPEN[++top] = (double)(ch - 48);
		else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
			if (!DoOperator(OPEN, top, ch)) {
				printf("运行出错!\n");
				exit(1);
			}
		}
		else printf("输入了非法字符,请重新输入!\n");
		ch = A[i++];
	}
	result = OPEN[top--];
	printf("计算结果是:%g\n", result);
	return 0;
}

4.中缀表示转换为后缀表示

扫描二维码关注公众号,回复: 12974519 查看本文章

五、递归

六、双端队列

//SeqDeque.h

#pragma once
#include<cstdio>
#define maxSize 100
typedef int DQElemType;
typedef struct {
	DQElemType elem[maxSize];
	int end1, end2;
}SeqDeque;
//SeqDeque.cpp

#include "SeqDeque.h"
bool enQueueHead(SeqDeque& Q, DQElemType x) {
	if ((Q.end2 + 1) % maxSize == Q.end1) return false;
	Q.end1 = (Q.end1 - 1 + maxSize) % maxSize;
	Q.elem[Q.end1] = x;
	return true;
}
bool deQueueHead(SeqDeque& Q, DQElemType& x) {
	if (Q.end1 == Q.end2) return false;
	x = Q.elem[Q.end1];
	Q.end1 = (Q.end1 + 1) % maxSize;
	return true;
}
bool getHead(SeqDeque& Q, DQElemType& x) {
	if (Q.end1 == Q.end2) return false;
	x = Q.elem[Q.end1];
	return true;
}
bool enQueueTail(SeqDeque& Q, DQElemType x) {
	if ((Q.end1 + 1) % maxSize == Q.end1) return false;
	Q.elem[Q.end2] = x;
	Q.end2 = (Q.end2 + 1) % maxSize;
	return true;
}
bool deQueueTail(SeqDeque& Q, DQElemType& x) {
	if (Q.end1 == Q.end2) return false;
	Q.end2 = (Q.end2 - 1 + maxSize) % maxSize;
	x = Q.elem[Q.end2];
	return true;
}

七、优先队列

//PriorityQueue.h

#pragma once
#include<cstdio>
#include<cstdlib>
using namespace std;
#define maxPQSize 50
typedef int PQElemType;
typedef struct {
	PQElemType elem[maxPQSize];
	int n;
}PQueue;
//PriorityQueue.cpp

#include "PriorityQueue.h"
bool PQInsert(PQueue& PQ, PQElemType x) {
	if (PQ.n == maxPQSize) return false;
	PQ.elem[PQ.n++] = x;
	return true;
}
bool PQRemove(PQueue& PQ, PQElemType& x) {
	//这里作者又在搞事情,规定数值最小的优先级最大。但是在STL中默认情况下明明是数值越大优先级越高。
	if (PQ.n == 0) return false;
	PQElemType min = PQ.elem[0];
	int k = 0;
	for (int i = 1; i < PQ.n; i++) {
		if (min < PQ.elem[i]) {
			k = i;
			min = PQ.elem[i];
		}
	}
	x = PQ.elem[k];
	PQ.n--;
	PQ.elem[k] = PQ.elem[PQ.n];
	return true;
}

猜你喜欢

转载自blog.csdn.net/qq_45812711/article/details/105782626