1、栈
栈的存储规则是先进后出。学习嵌入式的同学都知道中断时的现场保护,当有中断嵌套时采用的也是先进后出的原则,和栈是一样的。
看过上一节Linux学习(十):数据结构--表的同学对数组表和链表已经有了了解。对于栈来讲,用上一节讲到的知识,做一些简单修改就可以实现栈的功能。这里只做简单介绍。
对于数组形式的栈,只要记住将删除函数的返回值改为data_t类型,函数中返回要用的数。其余都是一样的。
data_t sqstack_pop(sqstack_t *stack)
{
if(sqstack_is_empty(stack))
{
// puts("empty");
return -1;
}
data_t value = 0;
value = stack->a[stack->top];
stack->top--;
return value;
}
对于链表,我们讲到了头插入,尾插入,头删除,尾删除法。我们一般用头插入和头删除结合的方法。因为尾插入和尾删除都需要进行找寻最后节点的过程,相对而言麻烦了些。同样头删除时将数据返回。
data_t linkstack_pop(linknode_t *stack)
{
if(linkstack_is_empty(stack))
{
// puts("empty");
return -1;
}
linknode_t *temp = stack->next;
data_t value = temp->data;
stack->next = temp->next;
free(temp);
temp = NULL;
return value;
}
2、队列
队列与栈正好相反,采用的是先进先出的原则。我们仍使用上一节的知识为基础,来实现数组队列和链式队列。
2.1 数组队列
对于数组队列来讲,由于是先进先出的原则,最先出的前面的数据,而我们之前用的last只能指示最后的数据,
会造成空间的浪费
2.1 创建队列
typedef int data_t;
typedef struct queue{
data_t a[N];
int front;
int rear;
}sqqueue_t;
sqqueue_t *sqqueue_create()
{
sqqueue_t * queue = (sqqueue_t *)malloc(sizeof(sqqueue_t));
queue->front = 0;
queue->rear = 0;
return queue;
}
2.1 压入数据
int sqqueue_is_full(sqqueue_t *queue)
{
return (queue->rear + 1) % N == queue->front ? 1 : 0;
}
这里要注意rear+1后要注意取余操作,这样数组队列才能循环利用
int sqqueue_is_full(sqqueue_t *queue)
{
return (queue->rear + 1) % N == queue->front ? 1 : 0;
}
int sqqueue_input(sqqueue_t *queue, data_t value)
{
if(sqqueue_is_full(queue))
{
printf("full\n");
return -1;
}
queue->a[ queue->rear ] = value;
queue->rear = (queue->rear+1) % N;
return 0;
}
2.2 取数据
int sqqueue_is_empty(sqqueue_t *queue)
{
return queue->rear == queue->front ? 1 : 0;
}
data_t sqqueue_output(sqqueue_t *queue)
{
if(sqqueue_is_empty(queue))
{
printf("empty\n");
return -1;
}
data_t value = queue->a[ queue->front ];
queue->front = (queue->front + 1) % N;
return value;
}
同样要注意front取值后的取余操作。
3、链表实现队列
链表实现队列时,除了定义一个节点变量,再定义一个队列结构体变量,包含一个头节点指针和一个尾节点指针。
typedef int data_t;
typedef struct node
{
data_t data;
struct node * next;
}link_node;
typedef struct queue
{
link_node *head; //指向队列的头节点
link_node *tail; //指向队列的最后一个节点
}queue_t;
3.1 队列创建
队列创建时令头节点指针和尾节点指针相等,这也就是队列为空的情况
queue_t *link_queue_creat()
{
queue_t *queue = (queue_t *)malloc(sizeof(queue_t));
queue->head = (link_node *)malloc(sizeof(link_node));
queue->tail = (link_node *)malloc(sizeof(link_node));
queue->head = queue->tail;
queue->tail->next = NULL;
return queue;
}
3.2 队列添加
队列添加采用尾添加的方式,从尾部入队,移动tail指针即可
int link_queue_add(queue_t *queue,data_t value)
{
link_node *node = (link_node *)malloc(sizeof(link_node));
node->data = value;
node->next = NULL;
queue->tail->next = node;
queue->tail = node;
return 0;
}
3.3 队列弹出数据
队列出数据是采用头删除的方法,也就是从头节点弹出数据。
int is_empty(queue_t *queue)
{
return queue->head ==queue->tail?1:0;
}
data_t link_queue_output(queue_t *queue)
{
link_node *temp;
data_t temp_data;
if(is_empty(queue))
{
return -1;
}
temp = queue->head->next;
queue->head->next = temp->next;
if(temp->next == NULL)
{
queue->tail = queue->head;
}
temp_data = temp->data;
free(temp);
temp = NULL;
return temp_data;
}
要注意15-18行的代码,当弹出最后一个数据并释放其地址空间时,tail指针仍然指向原来的地址,但那块地址空间已经被释放了,此时必须让tail指针指向队列的头节点。从另一个方面思考,返回最后一个数据时,队列已经为空,为空的条件是什么?头节点和尾节点指向同一块地址,自然两者应相等。