栈 和 队列

栈:

栈是一种特殊的线性表,并且只允许在固定的一端进行插入/删除操作,这一端叫做栈顶,另一端叫做栈底。删除/插入数据遵循LIFO(后进先出)原则。一般涉及两个操作:

压栈:将数据插入栈的操作,数据插入在栈顶;

出栈:将数据删除的操作,删除栈顶的数据。

栈的实现:

同顺序表一样,用数组实现栈的时也分为静态数组和动态数组实现栈,在这里使用动态数组实现。

typedef int MSDataType;
typedef struct MyStack {
	MSDataType* arr;
	int head;
	int capacity;
}Stack;

 栈的基本接口:

// 初始化栈
void StackInit(Stack* ps); 
// 入栈
void StackPush(Stack* ps, MSDataType data); 
// 出栈
void StackPop(Stack* ps); 
// 获取栈顶元素
MSDataType StackTop(Stack* ps); 
// 获取栈中有效元素个数
int StackSize(Stack* ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps); 
// 销毁栈
void StackDestroy(Stack* ps);

各个接口实现如下: 

//初始化栈
void StackInit(Stack* ps) {
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->head = 0;
}

//入栈
void StackPush(Stack* ps, MSDataType data) {
	//判断容量
	if (ps->capacity == ps->head) {
		//如果capacity为0,说明栈没有空间或者说只初始化并没有开辟空间
		//	  而capacity为4,说明已经初始化且开辟空间,只是空间已满。
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->arr = (MSDataType*)realloc(ps->arr, newCapacity * sizeof(MSDataType));
		if (ps->arr == NULL) {
			printf("realloc fail;\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
	}

	//入栈:
	ps->arr[ps->head] = data;
	ps->head++;
}

//出栈
void StackPop(Stack* ps) {
	assert(ps);
	assert(ps->head > 0);
	//这里的打印要注意head下标的问题,数组下标从0开始,
	//而head只是记录数组元素个数
	printf("%d \n", ps->arr[ps->head - 1]);
	ps->head--;	
}

//获取栈顶元素
MSDataType StackTop(Stack* ps) {
	assert(ps);
	assert(ps->head > 0);
	return ps->arr[ps->head-1];
}

//获取有效元素个数
int StackSize(Stack* ps) {
	assert(ps);
	assert(ps->head > 0);
	return ps->head;
}

// 检测栈是否为空,如果为不空返回1,如果为空返回0 
int StackEmpty(Stack* ps) {
	assert(ps);
	if (ps->head == 0)
		return 0;
	else
		return 1;
}

//销毁栈
void StackDestroy(Stack* ps) {
	assert(ps);
	free(ps->arr);
	ps->arr = NULL;
	ps->capacity = ps->head = 0;
}

int main() {
	Stack stack;
	StackInit(&stack);

	printf("入栈:\n");
	StackPush(&stack, 99);
	StackPush(&stack, 88);
	StackPush(&stack, 77);
	StackPush(&stack, 66);
	StackPush(&stack, 55);

	printf("出栈:\n");
	StackPop(&stack);
	StackPop(&stack);
	StackPop(&stack);

	printf("获取栈顶元素:\n");
	printf("%d \n", StackTop(&stack));

	printf("获取有效元素个数:\n");
	printf("%d \n", StackSize(&stack));

	printf("检测栈是否为空:\n");
	if (StackEmpty(&stack))
		printf("栈不为空;\n");
	else
		printf("栈为空;\n");



	return 0;

}

需要注意的是:在涉及到head操作的时候要特别注意,head表示栈当前元素的个数,也表示栈顶元素相对栈的位置,也就是说head要比对应元素下标大一,因此在打印或获取元素时要对head进行减一,防止访问不正确 

 更直观的感受如下,入栈放在数组尾部,出栈也是从数组尾部出。

队列: 

队列是一种只允许在一端进行插入数据,另一端进行删除数据的线性表,队列有FIFO(先进先出)原则,其中入队列的一端叫队尾,出队列一端叫队头。

使用链表实现队列:

如上图,在使用链表实现时,不仅要考虑队列的实现,也要考虑队列内数据的实现,因此就要定义两个结构体:一个实现队列,一个实现链表;

//单个数据的结构
typedef int MQDataType;
typedef struct MyQueueNode {
	MQDataType val;
	struct QueueNode* next;
}QueueNode;

//队列的结构
typedef struct Queue {
	QueueNode* head;
	QueueNode* tail;
}Queue;

 队列的一般接口如下:

// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, MQDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
MQDataType QueueFront(Queue* q);
// 获取队列队尾元素
MQDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);

 队列接口的实现:

// 初始化队列
void QueueInit(Queue* q) {
	assert(q);
	q->head = q->tail = NULL;
}

// 队尾入队列
void QueuePush(Queue* q, MQDataType data) {
	assert(q);
	QueueNode* node = (QueueNode*)malloc(sizeof(QueueNode));
	if (node == NULL) {
		printf("malloc fail;\n");
		exit(-1);
	}
	else {
		node->next = NULL;
		node->val = data;
	}
	if (q->head == NULL) {
		q->head = q->tail = node;
	}
	else {
		q->tail->next = node;
		q->tail = node;
	}
}

// 队头出队列
void QueuePop(Queue* q) {
	assert(q);
	assert(q->head != NULL);
    if(q->head->next==NULL){
        free(q->head);
        q->head=q->tail=NULL;
    }else{
        QueueNode* node = q->head->next;
        free(q->head);
        q->head = node;
    }
}

// 获取队列头部元素
MQDataType QueueFront(Queue* q) {
	assert(q);
	assert(q->head);
	return q->head->val;
}

// 获取队列队尾元素
MQDataType QueueBack(Queue* q) {
	assert(q);
	assert(q->head);
	return q->tail->val;
}

// 获取队列中有效元素个数
int QueueSize(Queue* q) {
	assert(q);
	int size = 0;
	QueueNode* tmp = q->head;
	while (tmp != NULL) {
		size++;
		tmp = tmp->next;
	}
	return size;
}

// 检测队列是否为空,如果为空返回零,如果非空返回1
int QueueEmpty(Queue* q) {
	assert(q);
	if (q->head == NULL)
		return 0;
	else
		return 1;
}

// 销毁队列
void QueueDestroy(Queue* q) {
	assert(q);
	while (q->head != NULL) {
		QueueNode* tmp = q->head->next;
		free(q->head);
		q->head = tmp;
	}
	q->head = q->tail = NULL;
	free(q);
	q = NULL;
}

int main() {

	Queue queue;
	QueueInit(&queue);

	printf("队尾入队列:\n");
	QueuePush(&queue, 99);
	QueuePush(&queue, 88);
	QueuePush(&queue, 77);

	printf("队头出队列:\n");
	QueuePop(&queue);
	QueuePop(&queue);
	QueuePop(&queue);
	QueuePop(&queue);

	printf("获取队头数据:\n");
	printf("%d\n", QueueFront(&queue));

	printf("获取队尾数据:\n");
	printf("%d\n", QueueBack(&queue));

	printf("有效数据个数:\n");
	printf("%d\n", QueueSize(&queue));

	printf("判断队列是否为空:\n");
	if (QueueEmpty(&queue))
		printf("队列不为空\n");
	else
		printf("队列为空\n");

	printf("队列销毁:\n");
	QueueDestroy(&queue);

	return 0;
}

需要注意的是,在队列结构中head和tail分别代表队列的头节点和尾节点,在每次crud的时候需要注意对head和tail的更新。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值