内核链表,链式栈,队列的学习实现

一、链表的种类。 传统链表: 单向链表、单向循环链表、双向链表、双向循环链表。 非传统链表: 内核链表。

传统链表与非传统链表区别? 传统链表增删改查,都是需要自己实现。 非传统链表增删改查,都是由内核提供,都已经实现好的了,我们只需要直接调用即可。

二、什么是内核链表? 内核链表与传统链表不一样,传统链表数据域与指针域都是用户自己写的,但是内核链表数据域是用户自己写的,但是指针域是内核链表已经实现好的了,用户不能修改指针域。 内核链表本身就是一条双向循环链表,头节点无效,但是与双向循环链表模型不一样。

 

三、内核链表节点模型? 即使是内核链表,节点依然由两部分组成: struct list_node{ /* 数据域 */ -> 用户自己写

/* 指针域 */    -> 是被定义在内核链表头文件: kernel_list.h中。

};

1、指针域长什么样子的? struct list_head { struct list_head *next; struct list_head *prev; };

2、假设每一个节点都是存放int类型的数据,那么内核链表节点怎么写? struct list_node{ int data; //数据域 struct list_head list; //指针域 };

四、内核链表实现增删改查? 1、 初始化头节点。 #define INIT_LIST_HEAD(ptr) do { (ptr)->next = (ptr); (ptr)->prev = (ptr); }while(0)

2、 尾插。 功能:list_add_tail – add a new entry 原型:void list_add_tail(struct list_head *new, struct list_head *head) 参数:

  • @new: new entry to be added --> 新节点的指针

  • @head: list head to add it before --> 头节点的指针

3、 头插。 功能: list_add – add a new entry 原型: void list_add(struct list_head *new, struct list_head *head) 参数:

  • @new: new entry to be added --> 新节点的指针

  • @head: list head to add it after --> 头节点的指针

4、遍历。 功能: * list_for_each_entry - iterate over list of given type 原型: #define list_for_each_entry(pos, head, member)

  • @pos: the type * to use as a loop counter. ---> 指向大的结构体的指针变量,类似于传统链表中的p。

  • @head: the head for your list. ---> 头节点的指针。

  • @member: the name of the list_struct within the struct. ---> 在大的结构体中,小的结构体变量叫什么名字。

5、删除。 功能: * list_for_each_entry_safe – iterate over list of given type safe against removal of list entry 原型: #define list_for_each_entry_safe(pos, n, head, member)

  • @pos: the type * to use as a loop counter. ---> 指向大的结构体的指针变量,类似于传统链表中的p。

  • @n: another type * to use as temporary storage ---> 另外一个指向大的结构体的指针变量,类似于传统链表的q。

  • @head: the head for your list. ---> 头节点的指针。

  • @member: the name of the list_struct within the struct. ---> 在大的结构体中,小的结构体变量叫什么名字。

功能: * list_del – deletes entry from list. 原型: void list_del(struct list_head *entry)

  • @entry: the element to delete from the list. -> 需要删除的那个节点的指针

练习1: 现在有一些关于人物信息文件,存储在一个目录中。 文件名以人物的名字来命名的,例如: zhangsan.txt zhangsan,M,10086,pengyou -> 依次是姓名,性别,电话号码,关系 (使用逗号来分开) 程序执行后,将全部人物的信息初始化到链表中,将所有人物的信息都打印出来。(一个人物等于一个节点) 要求使用内核链表来做。

详细代码:

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

struct list_node{
	int data;
	struct list_head list;  //指针域,是被定义在头文件:kernel_list.h
};

struct list_node *init_list_head()
{
	//1. 为头节点申请空间。
	struct list_node *head = malloc(sizeof(struct list_node));

	//2. 数据域无效、指针域有效。
	INIT_LIST_HEAD(&(head->list));

	return head;
}

void insert_data_to_tail(struct list_node *head,int num)
{
	//1. 为新节点申请空间。
	struct list_node *new = malloc(sizeof(struct list_node));

	//2. 为数据域赋值。
	new->data = num;

	//3. 为指针域赋值。
	list_add_tail(&(new->list),&(head->list));

	return;
}

void insert_data_to_head(struct list_node *head,int num)
{
	//1. 为新节点申请空间。
	struct list_node *new = malloc(sizeof(struct list_node));

	//2. 为数据域赋值。
	new->data = num;

	//3. 为指针域赋值。
	list_add(&(new->list),&(head->list));

	return;
}

void show_list_node(struct list_node *head)
{
	struct list_node *p = NULL;
	list_for_each_entry(p,&(head->list),list)
	{
		printf("data:%d\n",p->data);
	}
	return;
}

int delete_list_node(struct list_node *head,int num)
{
	struct list_node *p = NULL;
	struct list_node *q = NULL;
	list_for_each_entry_safe(p,q,&(head->list),list)
	{
		if(p->data == num)
		{
			//1. 从链表中将节点脱离了
			list_del(&(p->list));

			//2. 释放空间。
			free(p);

			return 0;
		}
	}

	return -1;
}

void delete_list(struct list_node *head)
{
	struct list_node *p = NULL;
	struct list_node *q = NULL;
	list_for_each_entry_safe(p,q,&(head->list),list)
	{
		//1. 从链表中将节点脱离了
		list_del(&(p->list));

		//2. 释放空间。
		free(p);
	}

	free(head);
	return;
}

int main(int argc,char *argv[])
{
	//1. 初始化头节点。
	struct list_node *head = NULL;
	head = init_list_head();

	//2. 尾插。
	insert_data_to_tail(head,10);
	insert_data_to_tail(head,20);
	insert_data_to_tail(head,30);

	//3. 头插。
	insert_data_to_head(head,8);
	insert_data_to_head(head,5);
	insert_data_to_head(head,3);

	//4. 遍历。
	show_list_node(head);

	//6. 删除节点。
	delete_list_node(head,10);
	printf("--------------------\n");
	show_list_node(head);

	//7. 删除整条链表。
	delete_list(head);

	return 0;
}

 

五、链式栈。

1、什么是栈? 在一条储存结构链表中,插入节点与删除节点都是在同一端进行,那么这种结构就是栈。 例如:生活上拿碗例子,上次最后一个放进去的碗,下一次一定是第一个被拿出来,这种逻辑就是后进先出。

往链式栈插入数据:压栈。 从链式栈拿出数据:出栈。 第一个元素:栈顶

2、设计栈管理结构体以及栈节点结构体。 1)栈节点结构体 struct node{ int data; struct node *next; };

2)栈管理结构体。 -> 管理栈顶元素以及统计整个栈元素的个数。 struct stack{ struct node *top; //这个指针就是指向栈顶。 int size; //统计栈中元素个数。 };

3、示例代码。

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

//节点结构体
struct node{
	int data;
	struct node *next;
};

//管理结构体
struct stack{
	struct node *top;  //指向栈顶的元素
	int size;          //统计栈中元素个数
};

struct stack *init_stack()
{
	//1. 为管理结构体申请空间。
	struct stack *s = malloc(sizeof(struct stack));
	if(s == NULL)
		printf("malloc s malloc!\n");

	//2. 为管理结构体赋值。
	s->top = NULL;
	s->size = 0;

	return s;
}

void push_stack(struct stack *s,int num)
{
	//1. 为新节点申请空间。
	struct node *new = malloc(sizeof(struct node));
	if(new == NULL)
		printf("malloc new error!\n");

	//2. 为新节点赋值。
	new->data = num;
	new->next = s->top;  //让new指向原本的栈顶。
	s->top = new;        //让现在栈顶指向new。
	s->size++;

	return;
}

void show_stack(struct stack *s)
{
	struct node *p = NULL;
	for(p=s->top;p!=NULL;p=p->next)
	{
		printf("p->data:%d\n",p->data);
	}
	return;
}

int pop_stack(struct stack *s,int *a)
{
	//1. 在出栈之前,先判断一下栈是否为空。
	if(s->top == NULL)
		return -1;

	//2. 不为NULL,说明有元素,就可以出栈。
	struct node *tmp = s->top;
	*a = tmp->data;

	//3. 更换栈顶。
	s->top = s->top->next;

	//4. 释放原来的栈顶。
	free(tmp);

	//5. 节点的个数-1
	s->size--;

	return 0;
}

int main(int argc,char *argv[])
{
	//1. 初始化一条空栈。(这个栈中一个元素都没有,top指向NULL)
	struct stack *s = NULL;
	s = init_stack();

	//2. 压栈。
	push_stack(s,5);   //最先放进去的,最后打印,最后出栈。
	push_stack(s,8);
	push_stack(s,10);  //最后放进去的,最先打印,最先出栈。

	//3. 遍历栈。
	show_stack(s);
	printf("====================\n");

	//4. 出栈。
	int a;
	while(s->size!=0)  //只要元素个数不为0,都可以出栈。
	{
		pop_stack(s,&a);  //函数作用: 取出栈顶元素,存储在变量a中,然后将栈顶元素删除。
		printf("出栈的元素是:%d\n",a);

		printf("========================\n");
		show_stack(s);
	}

	//5. 释放栈管理结构体空间。
	free(s);

	return 0;
}

六、队列

1、什么是队列? 在一条储存结构中,插入节点与删除节点分别在两端进行。例如:插入数据在队尾插入,删除数据在队头删除,那么这种逻辑叫做队列,其特点:先进先出,后进后出。

插入数据 -> 入队 删除数据 -> 出队

2、队列的模型?

 

3、示例代码

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

struct node{
	int data;
	struct node *next;
};

struct queue{
	struct node *head;
	struct node *tail;
	int size;
};

struct queue *init_queue()
{
	//1. 为管理结构体申请空间。
	struct queue *q = malloc(sizeof(struct queue));
	if(q == NULL)
		printf("malloc queue error!\n");

	//2. 为管理结构体赋值。
	q->head = NULL;
	q->tail = NULL;
	q->size = 0;

	return q;
}

void in_queue(struct queue *q,int num)
{
	//1. 为新节点申请空间。
	struct node *new = malloc(sizeof(struct node));
	if(new == NULL)
		printf("malloc new error!\n");

	//2. 为数据域与指针域赋值。
	new->data = num;
	new->next = NULL;  //新排队的人后面肯定没有人。

	if(q->size==0)  //如果插入的节点是第一个节点,则需要特殊处理。
	{
		q->head = new;  //如果队列中只有你一个人,那么队头队尾都是你
		q->tail = new;
	}
	else{
		q->tail->next = new;  //让现在队尾的那个元素的指针域指向新节点。
		q->tail = new;        //让指向队尾的那个指针指向新节点new。
	}

	q->size++;  //元素的个数+1
	return;
}

int show_queue(struct queue *q)
{
	//1. 判断是否为空队。
	if(q->size == 0)
		return -1;

	//2. 遍历队列。
	struct node *p = NULL;
	for(p=q->head;p!=NULL;p=p->next)
	{
		printf("%d -> ",p->data);
	}
	printf("\n");
	return 0;
}

int out_queue(struct queue *q,int *a)
{
	//1. 判断队列是否为空。
	if(q->size == 0)
		return -1;

	//2. 不为空,就可以正常地出队。
	struct node *tmp = q->head;   //tmp就是指向将来出队的那个人。

	//3. 分情况讨论
	if(q->size == 1)  //整个队列就剩一个节点
	{
		q->head = NULL;
		q->tail = NULL;
	}
	else{  //不止一个节点。
		q->head = q->head->next;
	}

	*a = tmp->data;
	free(tmp);
	q->size--;

	return 0;
}

int main(int argc,char *argv[])
{	
	//1. 初始化一条空队。
	struct queue *q = NULL;
	q = init_queue();

	//2. 入队。
	in_queue(q,10);  //队头
	in_queue(q,5);
	in_queue(q,3);   //队尾

	//3. 遍历队列。
	show_queue(q);

	//4. 出队。
	int a;
	while(q->size!=0)
	{
		out_queue(q,&a);
		printf("出队的元素是:%d\n",a);
		show_queue(q);
		printf("-----------------\n");
	}

	//5. 释放。
	free(q);

	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值