求链表中的倒数第k个节点
思路: 用两个指针,第一个指针先走k-1步,然后两个指针一起走。当第一个指针走到尾节点时,第二个指针指向的就是倒数第k个节点。如图所示:
笔记:
- 链表的概念:由一系列不必在内存中相连的结构组成的,通过指针将一个个两三的内存块链接起来的线性的数据结构。链表的每个内存块称为结点Node;每一个结构均含有表元素和指向包含该元素后继元的结构的指针,称为Next指针;最后一个单元的Next指针指向NULL,该值由C定义且不能与其他指针混淆。
- 类型:单链表、双向联表、循环链表。
- 特性:执行插入、删除等操作不需要移动数据,使用效率高;链表的每个内存块都不是连续的,因此不需要提前计算内存的大小,内存空间可以根据结点的数量而改变。
代码:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
//判断非空指针
if(pListHead == nullptr)
return nullptr;
ListNode *pAhead = pListHead;
ListNode *pBehind = nullptr;
//A先走k-1步
for(unsigned int i=0;i<k-1;i++)
{
pAhead = pAhead->m_pNext;
}
//B指向链表的头结点,此时B与A之间就刚好隔了k-1个节点的长度
pBehind = pListHead;
//同时走,知道A到了链表的结尾处,此时B就在链表的倒数第k-1处
while(pAhead->m_pNext != nullptr)
{
pAhead = pAhead->m_pNext;
pBehind = pBehind->m_pNext;
}
return pBehind;
}
问题: 虽然该代码考虑了非空指针的情况,但1. 当链表中的节点数小于k时,程序会出错;2. 并且当k<=0时,也会出错。
解决方法:
代码处理之前考虑:
//**1.** if (k<=0)特殊处理
//**2.** k大于链表长度,特殊处理
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k)
{
//1. if (k==0)特殊处理
if(k<=0)
return nullptr;
//判断非空指针
if(pListHead == nullptr)
return nullptr;
ListNode *pAhead = pListHead;
ListNode *pBehind = pListHead;//B指向链表的头结点,此时B与A之间就刚好隔了k-1个节点的长度
//A先走k-1步
for(unsigned int i=0;i<k-1;i++)
{
//2. k大于链表长度,特殊处理
if(p->m_pNext == nullptr)
return nullptr;
else
pAhead = pAhead->m_pNext;
}
//同时走,知道A到了链表的结尾处,此时B就在链表的倒数第k-1处
while(pAhead->m_pNext != nullptr)
{
pAhead = pAhead->m_pNext;
pBehind = pBehind->m_pNext;
}
return pBehind;
}
总结: 要想很好的解决这个问题,最好的办法就是在动手写代码之前想好测试用例。只有把各种可能的输入事先都想好了,才能在写代码的时候把各种情况都进行相应的处理。代码写完之后,记得要先在心里默默运行,淡定,再次检查逻辑是否正确。
拓展: 其它的解题思路
思路2:遍历两次数组,第一次先统计出来个数n,第二次再求出n-k+1,将求倒数第k节点的问题转化为正数第n-(k-1)=n-k+1
的问题。
- C++代码:
/*C++中定义链表
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
//定义解决方法的类
class Solution
{
public:
//定义函数
ListNode* FindKthToTail(ListNode* pListHead, unsigned int m)
{
//1. 考虑空链表或者m<=0时进行特殊处理
if(pListHead==NULL||m<=1)
return NULL;
int count = 0;//用来记录链表总长度
ListNode* pFind = pListHead;//定义链表结构体
//记录链表总结点数n,方便后续倒数转正数
while(pFind != NULL)
{
count+=1;
pFind = pFind->next;
}
int pos = count - m + 1;//将倒数位置转化成正数位置
//2. 考虑m的值大于链表长度则进行特殊处理
if(pos < 1)
return NULL;
int flag = 1;
pFind = pListHead;
//总共走pos步
while(pFind != NULL && flag != pos)//我觉得pFind != NULL是多余的
{
pFind = pFind->next;
flag++;
}
return pFind;
}
};
- Python代码(其实就是对C++这个思路的改写):
# -*- coding:utf-8 -*-
#python中定义链表
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#定义类
class Solution:
#函数
def FindKthToTail(self, head, k):
#1. 考虑链表为空或查找值小于等于0的情况,特殊处理
if k<=0 or head==None:
return None
count=0
pFind=head
#统计链表长度n
while pFind != None:
count=count+1
pFind=pFind.next
#2. 考虑k的值大于链表长度的情况,特殊处理
if count<k:
return None
pos=count-k+1
flg=0
pFind=head
#执行查找,总共走pos步
while pFind!=None:
flg=flg+1
if flg==pos:
return pFind
pFind=pFind.next
总的来说其实思路1更加清楚明确好理解。