--- config: look: handDrawn themeVariables: fontSize: 20px --- mindmap 链表 链表概念 内存布局 结点定义 单向与双向链表 链表操作 单向链表操作 遍历 查找 插入 删除 建立 清空 枚举变量的取值 双向链表操作 插入 删除
《计算机程序设计》
计算机学院计算机研究所编译系统研究室
~/course
~/course/main.cpp
main.cpp
,随堂编程练习的代码请直接在此文件中编辑考考你
阅读程序,判断输出结果是什么?
学习目标
考考你
我们学习过多种数组,包括静态长度数组、动态长度数组、以及动态内存分配数组空间等,请分析数组在数据管理与访问中的优劣势
数组
优势
局限性
什么是链表?
考考你
在上述结构体中,data
成员与next
成员分别表示什么?
明察秋毫
链表结点是一种自引用结构,即结构定义中包含了指向本结构的指针(指向其他结点)
nullptr
,说明达到了链表尾部考考你
与数组相比,链表的存储组织有何种不同,能够提供哪些优势,又有哪些不足?
Try it!
将while
循环改为for
循环,遍历链表的框架会更加清楚,试一试
考考你
请编写代码,计算链表的长度
思路
有两种可行的循环策略:(1)以索引为迭代变量,循环index
次;(2)以结点为迭代变量,直到索引index
为止。
提示
可能存在多个匹配,需要返回第一个匹配的结点
思路
无论在哪个位置插入,都需要完成以下操作
教员箴言
涉及修改链表的操作,务必注意空链表这一特殊情况的处理!
提示
利用已经完成的按序查找结点函数getNodeAt()
考考你
可否交换第8行与第9行代码的位置?
在指定位置插入结点
Node *insertAt(Node *&head, Node *&tail, int index, int value) {
if (index == 0)
return insertHead(head, tail, value);
Node *pre = getNodeAt(head, tail, index - 1);
if (pre) { // 如果 pre==tail,会发生什么?
Node *p = new Node;
p->data = value;
p->next = pre->next;
pre->next = p;
return p;
}
return insertTail(head, tail, value);
}
在指定位置插入结点
Node *insertAt(Node *&head, Node *&tail, int index, int value) {
if (index == 0)
return insertHead(head, tail, value);
Node *pre = getNodeAt(head, tail, index - 1);
if (pre and pre != tail) { // 如果插入位置是尾部,交给第11行处理
Node *p = new Node;
p->data = value;
p->next = pre->next;
pre->next = p;
return p;
}
return insertTail(head, tail, value);
}
考考你
如果链表仅使用head
指针维护(不维护tail
指针),上述两种链表建立方式,哪种更高效?
明察秋毫
考考你
head
与tail
指针维护链表,如何判定空链表与单一节点链表?若只使用head
维护链表呢?明察秋毫
双向链表的引用与维护必须同时使用head
与tail
指针
考考你
补充完成双向链表的节点插入函数
双向链表插入结点
Node *insertAt(Node *&head, Node *&tail, int index, int value) {
Node *p = new Node;
p->data = value;
Node *pre = nullptr, *post = head;
for (int i = 0; i < index; ++i) {
if (post == nullptr)
break;
pre = post;
post = post->next;
}
p->prev = pre;
p->next = post;
if (pre) pre->next = p;
if (post) post->prev = p;
if (pre == nullptr) head = p;
if (post == nullptr) tail = p;
return p;
}
考考你
补充完成双向链表的节点删除函数
双向链表删除结点
Node *removeAt(Node *&head, Node *&tail, int index) {
Node *p = head;
for (int i = 0; i < index; ++i) {
if (p == nullptr)
break;
p = p->next;
}
if (p) {
Node *pre = p->prev;
Node *post = p->next;
if (pre) pre->next = post;
if (post) post->prev = pre;
if (pre == nullptr) head = post;
if (post == nullptr) tail = pre;
}
return p;
}
思考
编写链表处理函数,如何正确维护链表的状态?
教员箴言
考考你
借鉴双向链表结点的插入与删除实现方法(利用prev
与post
结点),重写单向链表的插入与删除函数(不再区别头部、尾部与中间结点)
在指定位置插入结点
Node *insertAt(Node *&head, Node *&tail, int index, int value) {
Node *p = new Node;
p->data = value;
Node *prev = nullptr, *post = head;
for (int i = 0; i < index; ++i) {
if (post == nullptr)
break;
prev = post;
post = post->next;
}
p->next = post;
if (pre) pre->next = p;
if (pre == nullptr) head = p;
if (post == nullptr) tail = p;
return p;
}
考考你
借鉴双向链表结点的插入与删除实现方法(利用prev
与post
结点),重写单向链表的插入与删除函数(不再区别头部、尾部与中间结点)
删除指定位置结点
Node *removeAt(Node *&head, Node *&tail, int index) {
Node *prev = nullptr, *p = head;
for (int i = 0; i < index; ++i) {
if (p == nullptr)
break;
prev = p;
p = p->next;
}
if (p) {
Node *post = p->next;
if (prev) pre->next = post;
if (prev == nullptr) head = post;
if (post == nullptr) tail = prev;
}
return p;
}
--- config: look: handDrawn themeVariables: fontSize: 20px --- mindmap 链表 链表概念 内存布局 结点定义 单向与双向链表 链表操作 单向链表操作 遍历 查找 插入 删除 建立 清空 枚举变量的取值 双向链表操作 插入 删除
学习目标
计算机程序设计