队列的特点
队列是一种线性存储结构,其中的数据遵循先进先出(First In First Out)的原则,简称FIFO;
其添加元素在队尾或者队头添加元素。
队列的存与取
初始化:
typedef struct
{
int data[MAX]; //数据流
int head; //头
int rear; //尾
int size; //队列内容大小
}queue;
//队列初始化
void init_queue(queue *p){
p->head=0;
p->rear=0;
p->size=0;
}
入队(push):
bool push(queue *p, int val)
{
if (p->size == MAX) //如果队列已经存满,则报错
{
return false;
}
p->data[p->rear] = val;
p->rear = (p->rear + 1) % MAX; //尾指针向后移动
p->size++; //队列大小增加
}
出队(pop):
int pop(queue *p)
{
if (p->size == 0) //为空则报错
{
printf("\nqueque is empty!\n");
return 0;
}
int val = p->data[p->head]; //从头指针开始取,因为先进先出
p->head = (p->head + 1) % MAX; //移动指针
p->size--;
return val;
}
队列中,分别在结构体中定义了两个指针(实际是个记号),一个为头一个位尾,插入时,尾指针向后移动头指针不变,出队时尾指针不变,头指针向后移动;通过%来控制指针的循环移动。
此代码只实现了已知大小的队列基础操作,若想要循环使用还需做修改。
栈的特点
栈也是一个线性存储结构,栈中的元素遵循先进后出(First In Last Out)原则,简称FILO结构;
栈限定只能在栈顶进行插入和删除操作。
栈的存与取
初始化:(栈因为只在栈顶操作,因此可以只需要一个指针)
typedef struct
{
int data[MAX];
int fp;
int size;
}stack;
void init_stack(stack *p){
p->size=0;
p->fp=0;
}
入栈(push):
bool push(stack *p,int data){
if (p->size==MAX)
{
printf("FULL");
return false;
}
p->data[p->fp++]=data;
p->size++;
}
出栈(pop):
int pop(stack *p){
if (p->size==0)
{
printf("\nNULL\n");
return -1;
}
int val=p->data[--p->fp];
p->size--;
return val;
}
因为栈的特点,因此比队列相对简单,著名的汉诺塔其实就是在三个栈中进行存取,栈比较符合我们平时对数组的操作(先进后出)。
单链表(有头和无头)
单链表是一种链式存储的数据结构,链表中的数据是以结点来表示的,每个结点的构成:元素+指针(指向后一结点)。
其中有头单链表和无头单链表的区别是,有头单链表会有一个头结点指向第一个结点,而无头单链表只能赋值一个指针指向第一个结点(头结点不是第一个结点),下列为有头单链表的操作。
单链表的插入方式有两种,分别是头插和尾插,顾名思义就是在第一个结点前插入和在末尾插入。
无头单链表的操作
注意
本人在创建无头单链表时,定义了一个全局静态变量尾指针,是为了方便对单链表进行操作,因此在增删查改时,增加跟删除都要对尾指针进行移动;
嫌麻烦的可以不定义此指针,但是每次在进行尾插时,都需要遍历链表。
下列函数其实就是将尾指针移动到最后一位。
dog *mvtail(dog *p) //移动尾插的尾指针
{
while ((p)->next!=NULL)
{
(p)=(p)->next;
}
tail=(p);
return tail;
}
定义链表结构
typedef struct dog
{
int num;
struct dog *next;
} dog;
static dog *tail; //定义一个全局静态变量尾指针
创建新结点
dog *creat(int num) // 创建新节点
{
dog *dg = (dog *)malloc(sizeof(dog));
dg->num = num;
dg->next = NULL;
return dg;
}
头插
void insert_head(dog **p, int num) // 头插
{
dog *dg = creat(num);
dg->next = (*p);
(*p) = dg;
}
有头单链表中,只需用先让新结点指向第一个结点,再用头结点指向新结点。
尾插
void insert_tail(dog **p,int num) //尾插
{
if(*p==NULL) //链表为空,则创建链表
{
tail=NULL;
dog *dg = creat(num);
(*p)=dg;
tail=(*p);
dg->next=NULL;
return;
}
dog *dg = creat(num);
tail->next=dg;
dg->next=NULL;
tail=dg;
}
删除
void myremove(dog **p,int num) //删除
{
dog *re=*p;
dog *pre=NULL;
if(re->num==num) //删除位置在头结点
{
*p=(*p)->next;
re->next=NULL;
free(re); //删除使用free函数释放内存,因为结点是malloc动态分配的内存
return;
}
while (re->next!=NULL)
{
pre=re;
re=re->next;
if(re->num==num)
{
pre->next=re->next;
re->next=NULL;
free(re);
return;
}
}
pre->next=NULL; //走到这一步时,代表删除位置在最后一个结点
free(re);
}
查找
dog *fount(dog *p, int queen) // 查找
{
dog *tmp = king->next;
while ((tmp->num) != queen)
{
tmp = tmp->next;
}
return tmp;
}
销毁
void destroyed(dog **p) //销毁
{
dog *del=(*p);
dog *tmp=NULL;
while (del)
{
tmp=del->next;
del->next=NULL;
free(del);
del=tmp;
}
(*p)->next=NULL;
printf("销毁成功!\n");
}