数组、链表、队列和栈----链表实现的队列,C语言实现

上一节,我们用数组实现了队列。这一节,我们用链表实现。

下面看看用链表实现的队列吧:

//声明结点的结构体 
struct Node{
	int num; //具体数据 
	struct Node * next; //指向下一个结点的指针 
};

struct Node head; //普通的结构体变量:头结点 

//结构体的指针变量:指向链表的尾结点。 
struct Node *tail = &head; //初始值为head的地址 

下面我们需要实现几个方法:
0、依次显示所有数据

void show() //遍历,其实队列不需要这个功能 
{
	struct Node *currentNode; //当前结点的指针
	currentNode = head.next; //当前结点的指针指向head结点的下一个结点 
	
	while(currentNode != NULL) //遍历 
	{
		printf("%d, ", currentNode->num); //输出结点信息 
		currentNode = currentNode->next; //跳转到下一个结点 
	}
	printf("\r\n");
}

1、读取队列的首元素,(不出列)

void peek()
{
	if(head.next == NULL)
	{
		printf("队列为空\r\n");
		return;
	}
	
	printf("队列的首元素: %d\r\n", head.next->num);
} 

2、入列:在队列最后面插入一个数,时间复杂度为O(1)

void enqueue(int n) //在链表的指定位置插入新节点
{
	//向操作系统申请一段内存空间,并强制转化成node的指针 
	struct Node * node = (struct Node *)(malloc(sizeof(struct Node)));
	node->num = n; //为node结点赋值 
	node->next = NULL; //为node结点赋值 

	tail->next = node; //更换currentNode的next结点。也就是插入操作 
	tail = node;
	head.num++;//head结点的num表示这个链表一共有几个结点
}

2.1、入列在队列最后面插入一个数,时间复杂度为O(N)。
这个方法时间复杂度高的原因是每次都是从head结点开始找到最后一个结点,然后再插入结点。遍历的过程时间复杂度为O(N),插入结点的时间复杂度为O(1),所以总的时间复杂度为O(N)。

void enqueue2(int n) //在链表的指定位置插入新节点
{
	//向操作系统申请一段内存空间,并强制转化成node的指针 
	struct Node * node = (struct Node *)(malloc(sizeof(struct Node)));
	node->num = n; //为node结点赋值 
	node->next = NULL; //为node结点赋值 
	
	struct Node * currentNode; //当前结点的指针 
	currentNode = &head; //当前结点的指针指向head结点 
	struct Node * temp; //临时结点的指针
	
	while(currentNode->next != NULL) //遍历到最后一个节点 
	{
		currentNode = currentNode->next;
	}
	
	currentNode->next = node; //更换currentNode的next结点。也就是插入操作 
	head.num++;//head结点的num表示这个链表一共有几个结点
}

3、出列:时间复杂度为O(1) 
int dequeue() //删除链表的首元素 (不是head结点)
{
	struct Node * temp; //结点的临时指针 
	
	//用temp指向要删除的index结点
	temp = head.next;
	int num = temp->num;
	
	head.next =  head.next->next; //删除index的结点
	free(temp); //要回收内存
	head.num--; //head结点的num表示这个链表一共有几个结点
	
	return num;
}

4int getSize() //获得链表中的结点个数 
{
	return head.num;
} 

5void release() //释放内存 
{
	struct Node * temp; //结点的临时指针
	
	while(head.next != NULL) //当head结点的next不为NULL时循环 
	{
		temp = head.next; //用temp记录head结点的子节点
		head.next = head.next->next; //删除head结点的子节点
		free(temp); //回收内存 
	}
}

下面是完整的代码:

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

//声明结点的结构体 
struct Node{
	int num; //具体数据 
	struct Node * next; //指向下一个结点的指针 
};

struct Node head; //普通的结构体变量:头结点 

//结构体的指针变量:指向链表的尾结点。 
struct Node *tail = &head; //初始值为head的地址 

//0、依次显示所有数据
void show() //遍历,其实队列不需要这个功能 
{
	struct Node *currentNode; //当前结点的指针
	currentNode = head.next; //当前结点的指针指向head结点的下一个结点 
	
	while(currentNode != NULL) //遍历 
	{
		printf("%d, ", currentNode->num); //输出结点信息 
		currentNode = currentNode->next; //跳转到下一个结点 
	}
	printf("\r\n");
}

//1、读取队列的首元素,(不出列)
void peek()
{
	if(head.next == NULL)
	{
		printf("队列为空\r\n");
		return;
	}
	
	printf("队列的首元素: %d\r\n", head.next->num);
} 

//2、入列:在队列最后面插入一个数,时间复杂度为O(1)
void enqueue(int n) //在链表的指定位置插入新节点
{
	//向操作系统申请一段内存空间,并强制转化成node的指针 
	struct Node * node = (struct Node *)(malloc(sizeof(struct Node)));
	node->num = n; //为node结点赋值 
	node->next = NULL; //为node结点赋值 

	tail->next = node; //更换currentNode的next结点。也就是插入操作 
	tail = node;
	head.num++;//head结点的num表示这个链表一共有几个结点
}

//2.1、入列在队列最后面插入一个数,时间复杂度为O(N) 
void enqueue2(int n) //在链表的指定位置插入新节点
{
	//向操作系统申请一段内存空间,并强制转化成node的指针 
	struct Node * node = (struct Node *)(malloc(sizeof(struct Node)));
	node->num = n; //为node结点赋值 
	node->next = NULL; //为node结点赋值 
	
	struct Node * currentNode; //当前结点的指针 
	currentNode = &head; //当前结点的指针指向head结点 
	struct Node * temp; //临时结点的指针
	
	while(currentNode->next != NULL) //遍历到最后一个节点 
	{
		currentNode = currentNode->next;
	}
	
	currentNode->next = node; //更换currentNode的next结点。也就是插入操作 
	head.num++;//head结点的num表示这个链表一共有几个结点
}

//3、出列:时间复杂度为O(1) 
int dequeue() //删除链表的首元素 (不是head结点)
{
	struct Node * temp; //结点的临时指针 
	
	//用temp指向要删除的index结点
	temp = head.next;
	int num = temp->num;
	
	head.next =  head.next->next; //删除index的结点
	free(temp); //要回收内存
	head.num--; //head结点的num表示这个链表一共有几个结点
	
	return num;
}

//4、获得链表中的结点个数
int getSize()  
{
	return head.num;
} 

//5、释放内存 
void release() 
{
	struct Node * temp; //结点的临时指针
	
	while(head.next != NULL) //当head结点的next不为NULL时循环 
	{
		temp = head.next; //用temp记录head结点的子节点
		head.next = head.next->next; //删除head结点的子节点
		free(temp); //回收内存 
	}
}

int main()
{
	printf("入列1~5\r\n");
	for(int i = 1; i <= 15; i++)
	{
		enqueue(i); //入列
	}
	
	printf("显示所有数据:");
	show(); //依次显示所有数据
		
	printf("出列: %d\r\n", dequeue());
	printf("出列: %d\r\n", dequeue());
	peek(); //查看当前首元素 
	
	enqueue(6); //入列
	printf("入列6, 显示所有数据:");
	show(); 
	
	printf("一共有几个元素: %d\r\n", getSize());
	
	printf("显示所有数据: ");
	show(); //依次显示所有数据
	
	release(); //释放内存 
	return 0;
}

运行结果与用数组实现的队列完全一样:

在这里插入图片描述
我们再尝试一次性输入15个数据会怎么样?结果也没有任何问题。从这里就可以看出链表 (实现的队列) 的优势了,只要总的内存空间足够大,我们就不需要在程序一开始设计很大的一个数组,只需要在使用的时候申请一个结点大小的内存空间即可。

在这里插入图片描述接下来我们讨论一下链表实现的队列的入列、出列的效率。
如果我们每次都需要从头开始寻找最后一个结点,然后进行入列操作,这个过程的时间复杂度为O(N)。但是如果始终放一个指向尾结点的指针,那么入列操作的时间复杂度仅仅为O(1)。出列的操作始终为O(1)。

对比一下数组和链表实现的队列:

在这里插入图片描述队列就介绍到这里,下一节开始我们要讲解栈了。

猜你喜欢

转载自blog.csdn.net/wangeil007/article/details/107514420