2019年7月30日星期二
一. 数据结构-链表学习大纲
1. 链表结构,链表存储方式,链表与数组区别、单向链表。
2. 单向循环链表,双向链表。
3. 双向循环链表,内核链表。
4. 学习链表
增 -> 添加节点到链表中
删 -> 从链表删除节点
改 -> 改变链表中某个节点的值
查 -> 查找某个特征的数据
二. 链表结构
1. 什么是链表?
链表是存储数据方式,这种链表的储存方式叫做链式存储。链表存储方式是使得零碎的堆空间使用地址联系在一起。
2. 链表与数组区别?
1)从地址上区别
数组就是在栈空间中连续申请空间,使用变量间接访问空间。
例子: int A[3]; -> 申请空间
A[0] A[1] -> 使用变量A间接访问空间, 由于地址是连续的,A[0]不需要保存A[1]的地址,"&A[0]+1"就是&A[1]的地址。
链表就是使用保存下一个节点地址的方式使得零碎的堆空间联系在一起。
节点1 ---> 节点2 ---> 节点3
数据域 无效 int int
指针域 节点2的地址 节点3的地址 NULL -> 由于地址不连续,所以每一个节点都必须保存下一个节点的地址!
2)从存放数据大小范围区别
数组的空间必须前提声明好,并且访问数组时,不能越界,否则就警告!
链表的空间不需要提前声明,如果堆内存足够大,则链表的空间可以无限延伸下去。
三. 链表的种类
1. 什么是传统链表?
传统链表: 单向链表,单向循环链表,双向链表,双向循环链表
需要用户自己写增删该查的过程。
2. 什么是非传统链表?
非传统链表: 内核链表
增删改查过程已经在linux头文件中写好的了,用户直接调用即可。
四. 单向链表
1. 设计链表节点
由于链表节点需要数据域以及指针域(存放着不同类型的数据),所以将每一个节点设计成一个结构体。
结构体模型:
struct data{
/* 数据域 */
...
/* 指针域 */
...
};
例子1: 每一个节点都存放着一个整型数据,那么结构体如何定义?
struct list_node{
int a;
struct list_node *next;
};
例子2: 每一个节点都存放一个同学的信息,姓名,年龄,电话号码
struct list_node{
char name[20];
int age;
char tel[20];
struct list_node *next;
};
2. 初始化链表 -> 搞一个链表头
struct list_node *init_list_head(struct list_node *head) //head = NULL
{
//为头节点申请空间
head = (struct list_node *)malloc(sizeof(struct list_node));
if(head == NULL)
printf("head malloc error!\n");
//为头节点的指针域赋值
head->next = NULL;
return head;
}
3. 尾插数据 -> 在链表末尾增加一个新的节点
int tail_add_list(struct list_node *head,int num)
{
//为新节点申请空间
struct list_node *Node = NULL;
Node = (struct list_node *)malloc(sizoef(struct list_node));
//为新节点赋值
Node->a = num;
Node->next = NULL;
//寻找最后一个节点,并尾插
struct list_node *p = NULL;
for(p=head;p->next!=NULL;p=p->next);
//从循环出来时,p->next=NULL,也就是说,p指向最后一个节点!
p->next = Node;
return 0;
}
4. 遍历链表
int show_list_node(struct list_node *head)
{
struct list_node *p = NULL;
for(p=head->next;p!=NULL;p=p->next)
{
printf("%d\n",p->a);
}
return 0;
}
练习1: 做一个通讯录,里面存放三个同学信息,每一个同学包含: 姓名,年龄,电话号码 -> 使用链表方式
每一个注册完的同学就尾插到链表的后面,先注册3个同学的信息,然后输出全部同学的信息。
参考答案: p1.c
5. 头插 -> 在头节点之后插入一个新的节点。
int head_add_list(struct list_node *head,int num)
{
struct list_node *Node = NULL;
Node = (struct list_node *)malloc(sizeof(struct list_node));
Node->a = num;
Node->next = head->next;
head->next = Node;
return 0;
}
6. 根据特征值来寻找节点
int search_list_node(struct list_node *head,int num)
{
struct list_node *p = NULL;
for(p=head->next;p!=NULL;p=p->next)
{
if(p->a == num)
{
show_node(p);
return 0;
}
}
printf("Not Found:%d\n",num);
return -1;
}
7. 删除节点
int delete_list_node(struct list_node *head,int num)
{
struct list_node *p = NULL;
struct list_node *q = NULL;
for(q=head,p=head->next;p!=NULL;q=p,p=p->next)
{
if(p->a == num)
{
q->next = p->next;
free(p);
return 0;
}
}
return -1;
}
8. 释放整条链表的空间。
int delete_list(struct list_node *head)
{
struct list_node *p = NULL;
struct list_node *q = NULL;
for(p=q=head;p!=NULL;p=q)
{
q=p->next;
free(p);
}
return 0;
}
练习2: 在练习1基础上添加通过电话号码搜索,注销用户。
参考:p1.c
练习3: 在一个目录下有几张BMP格式图片,点击屏幕任意位置,切换到下一张图片,实现单向链表完成。
参考: p3.c(剩余代码今晚完成)
练习4: 完成单向循环链表接口。
练习5: 使用单向循环链表完成练习3,要求点击最后一张回到第一张。