4.9.1 链表的引入
1、从数组的缺陷说起
所有元素类型必须一致,通过结构体去解决
元素个数必须事先定制并且一旦指定后不能更改。
如何解决数组的第二个缺陷?
可以对数组进行封装,还可以使用一个新的数据结构。
链表就是用来解决大小不能实时扩展的问题的。
链表:元素个数可以实时变大变小的数组。
2、大学为什么有新校区?
学校扩展有两个思路:1、拆迁 2、搬迁 3、外部扩展
3、链表是什么样的?
顾名思义:链表就是锁链链接起来的表。这里的表指的是一个一个的节点。用量连接2个表的就是指针。
链表是由若干个节点组成的
节点是由有效数据和指针构成。
有效数据用来存储信息。指针区域用来指向链表的下一个节点构成链表。
4、时刻别忘记链表是用来干嘛的
链表就是用来解决数组的大小不能动态扩展的问题,所以链表其实就是当数组用的。
链表就是用来存储数据的。优点就是灵活性,需要多少个就分配多少个,不占用额外的内存。数组的优势就是使用简单。
4.9.2 单链表的实现
1、单链表的节点构成
链表是由节点构成的。
定义的 struct node 只是一个结构体,本身并没有 变量生成,也不占用内存。
2、堆内存的申请和使用
链表的内存要求比较灵活,不能用栈也不能用数据段,只能用堆内存。
1、申请堆内存
2、清零堆内存
3、申请到的堆内存作为一个新节点
4、填充第一个新节点有效数据和指针
3、链表的头指针
头指针是一个普通指针
头指针 并不是节点,而是一个普通指针 struct node *类型的,所以他才指向链表的节点。
4、实际编写代码构建单链表
4.9.3.单链表的算法之节点插入
访问数据的关键只能通过头指针,不能用各个节点的指针
1、将创建的节点代码封装成一个函数
4.9.4 单链表的算法之插入节点续
详解链表头部插入函数
什么是节点头
pHeader = create_node(1);
insert_tail(pHeader, create_node(2));
insert_tail(pHeader, create_node(3));
insert_tail(pHeader, create_node(4));
第一个节点和其他节点添加的方式不同,头节点的一个特点:它紧跟在头指针的后面,第二:头结点的数据部分是空的。
头结点第一个不存数才是头结点
void insert_tail(struct node *pH, struct node *new)
{
int cnt = 0;
struct node *p = pH;
while(NULL != p->pNext)
{
p = p->pNext;
cnt++;
}
p->pNext = new;
pH->data = cnt + 1;
}
struct node * create_node(int data)
{
struct node *p = (struct node *) malloc(sizeof(struct node));
if(NULL == p)
{
printf("malloc error.\n");
return -1;
}
//清理申请到的内存
bzero(p, sizeof(struct node));
p->data = data;
p->pNext = NULL;
return p;
}
4.9.5 从链表头部插入新节点
void insert_head(struct node *pH, struct node *new)
{
new->pNext = pH->pNext;
pH->pNext = new;
pH->data += 1;
}
注意些代码过程中的箭头符号,实质是:指针方式访问和结构体。
链表可以头部插入也可以尾部插入。对链表来说几乎没有差别。
4.9.6 单链表的算法之遍历节点
1、什么是遍历
遍历就是把单链表中的各个节点挨个拿出来,就叫遍历。
遍历的要点:一是不能遗漏,二是不能重复,追求效率。
2、如何遍历单链表
关键是分析这个数据结构本身的特点
点亮由很多个节点组成,头指针+头结点就是链表的开始,最后一个接单的特征是它内部的pNext指针为NULL。从 头到尾中间各个节点内部的pNext指针来挂接。由起始到结尾的路径有且只有一条。
遍历方法:从头指针+头结点开始,一次访问各个节点,然后取出各节点的数据。
3、编程实战
void look_up(struct node *pH)