键指offer——求链表中的倒数第k个节点(p13)

21 篇文章 0 订阅
20 篇文章 0 订阅

求链表中的倒数第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更加清楚明确好理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值