目录
一、栈
介绍
初始准备
栈的初始化与销毁
栈为空
进栈与出栈
栈的有效数据个数
栈顶的数据
全部代码
二、队列
介绍
初始准备
队列的初始化与销毁
队列为空
进队列与出队列
队列的有效数据个数
队头和队尾的数据
全部代码
一、栈
介绍
栈是一种线性结构,类似于链表和顺序表,但也有它的独特之处,只能栈顶入,栈顶出,所以栈也是“先进后出”或者“后进先出”
栈能采用链式结构也能采用数组结构来实现,这里采用数组结构
链式结构
采用单链表,头作栈顶,头插入数据,头删出数据
采用带头双链表,尾作栈顶,尾插入数据,尾删出数据
数组结构
尾部作栈顶,头部作栈底,尾插入数据,尾删出数据
初始准备
为了使存储的数据类型不固定,能够在存储其它类型数据时容易修改,所以用typedef修改类型名
然后得建立一个结构体,有三个成员,一个是动态数组的起始地址(指针),另一个是栈顶变量,用来取出栈顶数据、判断是否还有空间时使用和有效数据个数,还有一个是数组容量
用typedef将其类型名修改,使其简洁方便使用
栈的初始化与销毁
将其三个成员初始化,指针置空,其余两个置为0即可
销毁栈时,和初始化栈类似,只是多加了个free来释放空间,放置内存泄漏
栈为空
这里采用bool类型,判断栈是否为空,为真就是空,为假就是有数据
注意:在C中使用bool类型要引用头文件stdbool.h
进栈与出栈
入数据得先判断是否有空间,没有就增容,一般是扩为原来的2倍,避免频繁扩容
然后再添加数据
删除数据时,得先判断栈是否为空,否则一直出,就容易出错,采用断言较好
栈的有效数据个数
有效数据就是top的大小,因为top从0开始,每次添加数据就+1
栈顶的数据
取出栈顶数据时,得先判断栈是否为空,采用断言较好
由于有效数据个数比其下标大1,所以要-1
全部代码
//头文件
#pragma once
#include
#include
#include
#include
typedef int DataType;
typedef struct Stack
{
DataType* a;
int top;
int capacity;
}ST;
//初始化栈
void STInit(ST* ps);
//入栈
void STPushTop(ST* ps, DataType x);
//出栈
void STPopTop(ST* ps);
//栈顶数据
DataType STTop(ST* ps);
//有效数据个数
int STDataSize(ST* ps);
//销毁栈
void STDestroy(ST* ps);
//栈为空
bool STEmpty(ST* ps);
//定义文件
#include"Stack.h"
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STPushTop(ST* ps, DataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
DataType* ptr = (DataType*)realloc(ps->a, sizeof(DataType) * newcapacity);
if (ptr == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = ptr;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void STPopTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
ps->top--;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
DataType STTop(ST* ps)
{
assert(ps);
assert(!STEmpty(ps));
return ps->a[ps->top - 1];
}
int STDataSize(ST* ps)
{
assert(ps);
return ps->top;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
//测试文件
#include"Stack.h"
void test1()
{
ST st;
STInit(&st);
STPushTop(&st, 1);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STPushTop(&st, 2);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STPushTop(&st, 3);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STPushTop(&st, 4);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STPushTop(&st, 5);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STPushTop(&st, 6);
STPopTop(&st);
printf("%d ", (&st)->a[(&st)->top]);
STDestroy(&st);
}
void test2()
{
ST st;
STInit(&st);
STPushTop(&st, 1);
STPushTop(&st, 2);
STPushTop(&st, 3);
STPushTop(&st, 4);
printf("%d\n", STTop(&st));
STDestroy(&st);
}
int main()
{
test1();
//test2();
return 0;
}
二、队列
介绍
队列也是一种线性结构,类似于链表和顺序表,但也有它的独特之处,只能队尾入,队头出,所以队列也是“先进先出”或者“后进后出”
由于用数组结构实现队列不太方便,效率不高,无论是数组开头作队头,还是数组尾作队头,都要挪动数据
数组开头作队头,数组尾作队尾,入数据方便,但得从数组开头出数据,即头删,需要往前挪动数据,比较麻烦
数组开头作队尾,数组尾作队头,入数据得从数组开头入,即头插,需要往后挪动数据,很麻烦,出数据方便
所以采用链式结构较好,这里采用单链表实现
初始准备
为了使存储的数据类型不固定,能够在存储其它类型数据时容易修改,所以用typedef修改类型名
然后得创建一个结构体,有2个成员,一个用来存储数据,另一个用来存储下一个数据的地址
为了方便进队列和出队列,所以定义了两个指针,一个指向头节点,另一个指向尾节点,用结构体的形式将二者封装在一起
队列的初始化与销毁
将第二个结构体的两个指针置为空即可
销毁队列时,得从头节点遍历,一个一个用free释放,最后再将头指针和尾指针置空即可
队列为空
判断队列是否为空,只需判断头指针是否为空即可,依然采用bool类型
进队列与出队列
入数据进队列时,得先用malloc开辟空间,然后进行判断是否开辟成功,再然后就是对其成员赋值,得分两种情况
第一种是第一次入数据进队列,直接作头节点
第二种则是在队列尾后链接节点
出数据时也得考虑队列是否为空,不能一直出数据,否则取数据时会出现随机值,采用断言判断
出数据时还得考虑一种情况,就是删除最后一个数据时,得将尾指针置空,要不然会出现野指针
队列的有效数据个数
由于队列是单链表实现的,所以只能采用从头节点开始遍历计数的方法
队头和队尾的数据
由于已经定义了头和尾两个指针,所以直接访问即可,只是要判断队列是否为空
全部代码
//头文件
#include
#include
#include
#include
typedef int DataType;
typedef struct QueueNode
{
DataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
//初始化队列
void QueueInit(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
//尾部插入
void QueuePushBack(Queue* pq, DataType x);
//头部删除
void QueuePopFront(Queue* pq);
//头部数据
DataType QueueTop(Queue* pq);
//尾部数据
DataType QueueTail(Queue* pq);
//队列为空
bool QueueEmpty(Queue* pq);
//链表数据个数
int QueueSize(Queue* pq);
//定义文件
#include"Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueuePushBack(Queue* pq, DataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->next = NULL;
newnode->data = x;
if (pq->head == NULL)
{
pq->head = newnode;
pq->tail = pq->head;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePopFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
QueueNode* Next = pq->head->next;
if (pq->head == pq->tail)
{
pq->tail = NULL;
}
free(pq->head);
pq->head = Next;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
DataType QueueTop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
DataType QueueTail(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
int count = 0;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = NULL;
pq->tail = NULL;
}
//测试文件
#include"Queue.h"
void test1()
{
Queue q;
QueueInit(&q);
QueuePushBack(&q, 1);
QueuePushBack(&q, 2);
QueuePushBack(&q, 3);
QueuePushBack(&q, 4);
QueuePushBack(&q, 5);
QueuePopFront(&q);
QueuePopFront(&q);
QueuePopFront(&q);
QueuePopFront(&q);
//QueuePopFront(&q);
//QueuePopFront(&q);
QueueNode* cur = (&q)->head;
while (cur)
{
printf("%d ", cur->data);
cur = cur->next;
}
QueueDestroy(&q);
}
void test2()
{
Queue q;
QueueInit(&q);
QueuePushBack(&q, 1);
QueuePushBack(&q, 2);
QueuePushBack(&q, 3);
QueuePushBack(&q, 4);
DataType top = QueueTop(&q);
printf("%d ", top);
QueuePopFront(&q);
top = QueueTop(&q);
printf("%d ", top);
printf("\n");
DataType tail = QueueTail(&q);
printf("%d ", tail);
printf("\n");
int sz = QueueSize(&q);
printf("%d\n",sz);
QueuePopFront(&q);
sz = QueueSize(&q);
printf("%d\n", sz);
QueueDestroy(&q);
}
int main()
{
test1();
//test2();
return 0;
}