【C语言】动态数组实现循环队列


前言

队列是一种“先进先出的数据结构”,可分为静态队列和链式队列。静态队列一般使用数组实现,数组需要预先定义内存大小,为了避免内存浪费,一般使用循环队列。接下来讲述循环队列的原理以及实现代码。
循环队列数据结构定义:
int front; //指向队列头,指向第一个数据节点
int tail; //指向队列尾(并不是指向最后一个数据节点,而是最后一个数据节点后面的位置)
int data[];//节点数据,根据实际需要可以是不同的数据类型,但是因为是数组,需要在声明时指定大小


一、实现过程

1、定义结构体结构

  • ①、定义循环队列的最大数据个数
  • ②、定义结构体结构
#define MAX_SIZE 5

typedef struct CircQueue {
	int front;
	int tail;
	int data[MAX_SIZE];
}Circ_Queue_t;

2、创建一个结构体指针

  • ①、创建一个结构体指针
  • ②、分配一块 sizeof(Circ_Queue_t) 【结构体类型所占的字节大小】字节大小的地址空间,并且将其首地址传给queue,使结构体指针queue指向这块地址。
Circ_Queue_t *queue;
queue = (Circ_Queue_t *)malloc(sizeof(Circ_Queue_t));
if (queue == NULL) {
	perror("malloc");
	return -1;
} else {
	printf("Create Success\n");
	printf("Circ Queue Size: %d\n\n", MAX_SIZE);
}

3、初始化循环队列

  • ①、将front、tail都设为0,表明这时循环队列的数据节点个数为0,队列为空
  • ②、从queue->data首地址开始,将MAX_SIZE*sizeof(int)字节大小的内存块填充为0
/*
 * Init Circ_Queue
 */
void Circ_Queue_Init(Circ_Queue_t *queue) {
	queue->front = 0;
	queue->tail = 0;
	memset(queue->data, 0, MAX_SIZE * sizeof(int));
}

4、释放分配给循环队列的内存

  • ①、判断结构体指针queue是否指向非空
  • 是则说明malloc动态分配给循环队列的地址还没有被释放掉,需要进行释放
/*
 * Deinit Circ_Queue
 */
void Circ_Queue_Deinit(Circ_Queue_t *queue) {
	if (queue != NULL) {
		free(queue);
	}
}

5、判断循环队列是否为满

  • ①、判断循环队列是否为满
  • 循环队列为满的条件是 (tail+1)%MAX_SIZE == front
  • 为什么使用 (tail+1)%MAX_SIZE == front 作为队列满的条件呢?
  • 因为如果使用 tail == front 作为判断条件的话,那么就和循环队列判断为空的条件重复了,无法知道该循环队列是满还是空。因此需要预留一个空间,当 tail 的下一位是 front 的话,说明队列已经满了。
/*
 * Is the Circ_Queue full?
 * return: 
 * 0 - is not full
 * 1 - full
 */
bool is_full(Circ_Queue_t *queue) {
	return (queue->tail+1)%MAX_SIZE == queue->front;
}

6、判断循环队列是否为空

  • ①、判断循环队列是否为空
  • 循环队列为空的条件是 tail == front
  • 当循环队列的 front 和 tail 重叠在一起的时候,说明该循环队列中已经没有数据了,为空。
/*
 * Is the Circ_Queue empty?
 * return: 
 * 0 - is not empty
 * 1 - empty
 */
bool is_empty(Circ_Queue_t *queue) {
	return queue->front == queue->tail;
}

7、添加数据到循环队列中

  • ①、先判断该循环队列是否已满,已满则跳出该函数
  • ②、该循环队列未满,则进行数据填充。↓
  • ③、将数据填充到queue->data的第queue->tail位(将数据填充到循环队列中下标为tail的位置中)
  • ④、将下标 queue->tail 修改为 (queue->tail +1)%MAX_SIZE
  • 但是因为当queue->tail为4时,按照正常queue->tail+1,会导致queue->tail为5,超出该循环队列中数组下标的可取范围【因为该循环队列的长度是5,下标的正常取值是0-4】,所以要%MAX_SIZE,将queue->tail 变为0,回归到正常的循环中
/*
 * Add tail data to Circ_Queue
 */
void Circ_Queue_Add(Circ_Queue_t *queue, int value) {
	if (is_full(queue)) {
		printf("Circ Queue is Full\n");
	} else {
		queue->data[queue->tail] = value;
		queue->tail = (queue->tail+1)%MAX_SIZE;	
	}
}

8、从循环队列中删除数据

  • ①、先判断该循环队列是否为空,为空则跳出该函数
  • ②、该循环队列不为空,则进行数据删除。↓
  • ③、将 queue->front 设置为 (queue->front + 1) % MAX_SIZE
  • 原理和添加数据时queue->tail的操作类似,正常情况下,queue->front 是从 0 开始,当经过删除数据, queue->front +1
  • 但是当 queue->front 为4的时候删除数据,会导致queue->front 为5,超出该循环队列中数组下标的可取范围【因为该循环队列的长度是5,下标的正常取值是0-4】,所以要%MAX_SIZE,将queue->front 变为0,回归到正常的循环中)
/*
 * Delete front data from Circ_Queue
 */
void Circ_Queue_Del(Circ_Queue_t *queue) {
	if (is_empty(queue)) {
		printf("Circ Queue is Empty\n");
	} else {
		queue->front = (queue->front+1)%MAX_SIZE;
	}
}

9、获取循环队列第一个数据

  • ①、判断该循环队列是否存在,不存在则打印信息
  • ②、存在则判断是否为空,为空则打印信息
  • ③、不为空则返回首个数据 queue->data[queue->front]
/*
 * Get the front of the Circ_Queue data 
 */
int get_front(Circ_Queue_t *queue) {
	if (queue == NULL) {
		printf("There is no Circ Queue\n");
	} else {
		if (is_empty(queue)) {
			printf("Circ Queue is NULL\n");
		} else {
			return queue->data[queue->front];	
		}
	}
}

10、获取循环队列最后一个数据

  • ①、判断该循环队列是否存在,不存在则打印信息
  • ②、存在则判断是否为空,为空则打印信息
  • ③、不为空则返回最后一个数据 queue->data[(queue->tail-1+MAX_SIZE)%MAX_SIZE]
  • 因为 queue->data[queue->tail] 是不存放数据的,所以返回的是其前一个位置存放的数据,但如果当 queue->tail 为0时, queue->tail - 1 为-1 ,不在该循环队列中数组下标的可取范围【因为该循环队列的长度是5,下标的正常取值是0-4 】,所以要将 queue->tail - 1 后 + MAX_SIZE,再 %MAX_SIZE,将 queue->tail 变为4,回归到正常的循环中
/*
 * Get the tail of the Circ_Queue data 
 */
int get_tail(Circ_Queue_t *queue) {
	if (queue == NULL) {
		printf("There is no Circ Queue\n");
	} else {
		if (is_empty(queue)) {
			printf("Circ Queue is NULL\n");
		} else {
			return queue->data[(queue->tail-1+MAX_SIZE)%MAX_SIZE];	
		}
	}
}

二、具体代码

#include 
#include 
#include 
#include 

#define MAX_SIZE 5

typedef struct CircQueue {
	int front;
	int tail;
	int data[MAX_SIZE];
}Circ_Queue_t;

/*
 * Init Circ_Queue
 */
void Circ_Queue_Init(Circ_Queue_t *queue) {
	queue->front = 0;
	queue->tail = 0;
	memset(queue->data, 0, MAX_SIZE * sizeof(int));
}

/*
 * Deinit Circ_Queue
 */
void Circ_Queue_Deinit(Circ_Queue_t *queue) {
	if (queue != NULL) {
		free(queue);
	}
}

/*
 * Is the Circ_Queue full?
 * return: 
 * 0 - is not full
 * 1 - full
 */
bool is_full(Circ_Queue_t *queue) {
	return (queue->tail+1)%MAX_SIZE == queue->front;
}

/*
 * Is the Circ_Queue empty?
 * return: 
 * 0 - is not empty
 * 1 - empty
 */
bool is_empty(Circ_Queue_t *queue) {
	return queue->front == queue->tail;
}

/*
 * Add tail data to Circ_Queue
 */
void Circ_Queue_Add(Circ_Queue_t *queue, int value) {
	if (is_full(queue)) {
		printf("Circ Queue is Full\n");
	} else {
		queue->data[queue->tail] = value;
		queue->tail = (queue->tail+1)%MAX_SIZE;	
	}
}

/*
 * Delete front data from Circ_Queue
 */
void Circ_Queue_Del(Circ_Queue_t *queue) {
	if (is_empty(queue)) {
		printf("Circ Queue is Empty\n");
	} else {
		queue->front = (queue->front+1)%MAX_SIZE;
	}
}

/*
 * Get the front of the Circ_Queue data 
 */
int get_front(Circ_Queue_t *queue) {
	if (queue == NULL) {
		printf("There is no Circ Queue\n");
	} else {
		if (is_empty(queue)) {
			printf("Circ Queue is NULL\n");
		} else {
			return queue->data[queue->front];	
		}
	}
}

/*
 * Get the tail of the Circ_Queue data 
 */
int get_tail(Circ_Queue_t *queue) {
	if (queue == NULL) {
		printf("There is no Circ Queue\n");
	} else {
		if (is_empty(queue)) {
			printf("Circ Queue is NULL\n");
		} else {
			return queue->data[(queue->tail-1+MAX_SIZE)%MAX_SIZE];	
		}
	}
}

/*
 * print Circ_Queue information
 */
void print(Circ_Queue_t *queue) {
	if (queue == NULL) {
		printf("There is no Circ Queue\n");
	} else {
		printf("Circ Queue Data is: ");
		if (is_empty(queue)) {
			printf("Circ Queue is NULL\n");
		} else {
			int i = queue->front;
			while (i != queue->tail) {
				printf("%d ", queue->data[i]);	
				i = (i+1)%MAX_SIZE;
			}
			printf("\n");
			printf("Front: %d\tTail: %d\n", get_front(queue), get_tail(queue));
			if (is_full(queue)) {
				printf("Circ Queue is Full\n");
			}
		}
	}
	printf("\n");
} 

/*
 * The main function
 */
int main(int argc, char **argv) {
	Circ_Queue_t *queue;
	queue = (Circ_Queue_t *)malloc(sizeof(Circ_Queue_t));
	if (queue == NULL) {
		perror("malloc fail\n");
		return -1;
	} else {
		printf("Create Success\n");
		printf("Circ Queue Size: %d\n\n", MAX_SIZE);
	}
	//初始化队列
	printf("Circ Queue Init:\n");
	Circ_Queue_Init(queue);
	print(queue);
	//将0,2,4加入队列
	printf("Circ Queue Add:\n"); 
	Circ_Queue_Add(queue, 0);
	Circ_Queue_Add(queue, 2);
	Circ_Queue_Add(queue, 4);
	print(queue);
	//删除队列一个数
	printf("Circ Queue Del:\n");
	Circ_Queue_Del(queue);
	print(queue);
	//将6,8加入队列
	printf("Circ Queue Add:\n");
	Circ_Queue_Add(queue, 6);
	Circ_Queue_Add(queue, 8);
	print(queue);
	//销毁队列
	printf("Destory Circ Quee\n");
	Circ_Queue_Deinit(queue);
	queue = NULL;
	print(queue);
} /*-----End of main function-----*/

三、测试结果

【C语言】动态数组实现循环队列_第1张图片


总结

以上是对动态分配的数组实现循环列表的一些理解,如有写的不好的地方,还请各位大佬不吝赐教

你可能感兴趣的:(C语言,c语言,队列,数据结构,指针)