2009年考察关于单链表的相关知识,本篇给出解题思路并较全面梳理单链表相关知识。
题目
(15分)已知一个带有表头结点的单链表,结点结构为:
假设该链表只给出了头指针list
。在不改变链表的前提下,请设计一个尽可能高效的算法:
查找链表中倒数第 k 个位置上的结点(k 为正整数)。
若查找成功,算法输出该结点的 data
域的值,并返回 1;否则,只返回 0。
要求:
⑴ 描述算法的基本设计思想;
⑵ 描述算法的详细实现步骤;
⑶ 根据设计思想和实现步骤,采用程序设计语言描述算法
注意题目所给信息
1.带头结点
2.单链表:不能访问前继节点,只能访问后继节点。
3.未知单链表长度
4.k为正整数,即k>0。不需要做k<=0越界判断。
单链表
单链表(单向链表)数据结构回顾:
单链表是线性表的链式存储。由多个节点组成,每个节点又由数据域和指针域构成。如图:
结点结构
用一个结构体描述节点类型:
struct ListNode {
int data; //节点的数据域,用来存放数值内容
struct ListNode *link; // 节点的指针域,用来指向下一个节点
};
这里的节点结构内容和题目所给的一致。
头节点
关于头节点一些需要的注意的,做出如下总结梳理
说明:
1.头节点不是链表第一个节点,而是头节点随后紧邻的后继节点。
2.头节点是非必须的,可以不设置。
3.在计算链表长度时,头节点不计入总数。
4.头节点的数据域没有意义。
好处:
1.使链表首个位置的插入删除更加方便,和其他位置一样,不需要涉及到头指针的移动。
2.统一空表和非空表的操作处理。当非空时头指针指向的是首个节点的地址,即*ListNode
类型,而对空表处理的时候却是NULL
,因此造成空表和非空表操作不一致。
头指针
其实指向某个节点的地址的指针丢失,也会造成这个节点无法访问。特别是头指针,一旦丢失,导致链表最前面的节点(头节点或者是第一个节点)无法访问,从而导致整个链表无法访问,出现内存泄漏等问题。
作用:具有标识作用,故常用头指针冠以链表的名字
单链表基本操作
链表的基本操作如下:
链表的初始化
/*
单链表的初始化
返回一个ListNode指针类型变量,即链表的头指针
*/
struct ListNode* initLinkList(){
struct ListNode *head; //定义头节点
head = (struct ListNode *)malloc(sizeof(struct ListNode)); //头结点空间申请
//这里做一个判断,判断头节点是否申请成功
if(head == NULL){
printf("内存不足!申请内存空间失败\n");
}
head->link = NULL; //头节点指向的下一个节点位置置为空
return head; //返回头节点地址
}
节点的创建
将创建新节点这个过程封装到一个函数,便于复用。
/*
新创建节点
返回值:新节点地址
*/
struct ListNode* newNode(int data, struct ListNode *link){
struct ListNode *newNode; //定义新节点
newNode = (struct ListNode*)malloc(sizeof(struct ListNode)); //新结点空间申请
newNode->data = data; //新结点数据域初始化
newNode->link = link; //新结点指针域初始化
return newNode;
}
节点的插入
思路:
调用findNodeByIndex函数(查找节点操作),获得第 i-1 节点,然后再进行插入操作。
具体的原理实现如下图所示:
说明:
这里一个数值,以及新节点所在位置来实现单链表中新节点插入操作。
新节点创建在函数内进行,并不是通过真正意义上传入一个节点类型实现插入操作。
时间性能:O(n)
具体步骤:
- 首先需要找到插入位置的前一个节点, 也就是图上节点
preNode
。若找不到,则是越界等问题,返回报错信息。 - 然后需要创建新的节点
newNode
。 - 插入操作实际上就是把
preNode
的后继节点改为新节点newNode
,然后再把新节点newNode
的后继节点改为第 i节点。需要注意顺序,以防出现断链。改动指针操作顺序正如图所示,先①后②。
代码如下:
①:newNode->link = preNode->link;
②:preNode->link = newNode;
对于①表示把新节点newNode
的后继节点改为第 i节点。第 i节点的地址通过它的前继节点来寻找,即:preNode->link
对于②表示把preNode
的后继节点改为新节点newNode
/*
插入节点操作
list*:新节点插入所在的链表
index:新节点插入的位置
data:新节点数据域的值
返回值:
0:插入失败
1:插入成功
*/