一、题目描述
已知一个带头结点的单链表,只给出头结点 L
。
结点的结构为:
data
next
在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第 k
个位置上的结点( k
为正整数)。若查找成功,打印输出该节点 data
域的值,并返回 true
;否则,只返回 false
。
二、题目分析
主要算法思想:
① 让两个指针 p, q
同时指向第一个存放数据的结点。
② 先让其中一个指针 q
向后移动 k
个位置。
③ 当 q
指针移动完毕(到达正序第 k+1
个位置)后,让 p, q
同时开始往后移动。
④ 如果 q
指向了表尾之后的 NULL
,则另一个指针 p
就指向了倒数第 k
个结点。
具体分析请看下图:
三、代码示例
依据上方的分析,写出如下代码:
// 获取链表长度
int GetLength(LinkList L) {
int count = 0;
LNode *p = L->next;
while (p) {
p = p->next;
count++;
}
return count;
}
// 查找链表倒数第 k 个结点
bool Find_Last_k(LinkList L, int k) {
if (k>GetLength(L) || k<0) return false; // 判断 k 是否合法
LNode *p = L->next, *q = L->next;
for (int i=0; i<k; i++) {
q = q->next;
}
while (q) {
p = p->next;
q = q->next;
}
cout<<p->data<<'\n';
return true;
}
这样的方式严格按照分析来的,便于理解。但使用了两个循环。
其实可以依据算法思想对代码优化一下,只采用一个循环、单次遍历:
bool Find_k(LinkList L, int k) {
LNode *p = L->next, *q = L->next;
int count = 0;
while (q){
if (count < k) count++;
// count用来记录 q 移动的次数,保证开始只有 q 向后移动 k 个位置
else p = p->next;
// 当 count==k 及之后,p 和 q 一起移动
q = q->next;
}
if (count < k) return false;
// 同时可以简化 k 是否合法的判断语句
cout<<p->data<<'\n';
return true;
}
四、运行验证
头文件
LinkList.h
以及基本操作函数CreateList_TailInsert(L); PrintList(L);
前文详解:
单链表与双链表-线性表的链式表示
#include<LinkList.h>
int GetLength(LinkList L) {
int count = 0;
LNode *p = L->next;
while (p) {
p = p->next;
count++;
}
return count;
}
bool Find_k(LinkList L, int k) {
// 查找倒数第 k 个结点
LNode *p = L->next, *q = L->next;
int count = 0;
while (q){
if (count < k) count++;
else p = p->next;
q = q->next;
}
if (count < k) return false;
cout<<p->data<<'\n';
return true;
}
int main() {
LinkList L;
cout<<"TailInsert: ";
CreateList_TailInsert(L);
PrintList(L);
cout<<"倒数第"<<3<<"个结点:";
Find_k(L, 3);
return 0;
}
运行结果:
更新时间——2023/3/3