剑指Offer的学习笔记(C#篇)-- 链表中倒数第K个点
题目描述
输入一个链表,输出该链表中倒数第k个结点。
一 . 数据结构基础概念普及(线性表)。
线性表可分为顺序表与链表,它们是堆栈、队列、树、图等数据结构的实现基础。
顺序表,线性表的顺序存储结构是指:用一块地址连续的存储空间依次存储线性表中的数据元素。他在逻辑上相邻的元素在物理上也是相邻的。什么意思呢,我们可以这样想象,去食堂排队打饭,打饭的队列就是一个顺序表,加粗部分的存储空间指的是我们排队所占据的位置;红色字体所说的概念可以理解为我们打饭的顺序必须要按照从前往后的顺序依次进行,即逻辑顺序=物理顺序,如果此处不好理解,看完下面就好懂了许多。
链表,相比顺序表需要预先占用一块事先分配好的存储空间,链表就灵活一些。链表中逻辑上相邻的元素在物理上可以不相邻。从这里可以看出,链表和顺序表是在相邻元素之间存在差异的,这段红字怎么理解呢,举个例子:我们去银行办理业务,银行都是有取号机的,我们办理业务的顺序并不是顺序表那样的物理顺序,而是依据你手上的号码顺序进行办理,所以呢,我们就不需要排队,在办事大厅随便坐,当号码轮到我们的时候,就可以办理了,这就实现了逻辑相邻但物理顺序不相邻。
回归题目,在链表中,链表是由N个节点链接而成的线性表,每个节点由两部分组成:数据域和指针域。如果其中每个节点只包含一个指针域那么就称为单链表,如果含有两个指针域那么就称为双链表。结合右上图来理解:数据域可以理解成我们要办理的业务(比如取多少钱),指针域可以理解成,取号机里面取出的号码。
代码实现定义一个单链表如下:
public class Node<T> { // 数据域 public T Item { get; set; } // 指针域 public Node<T> Next { get; set; } public Node() { } public Node(T item) { this.Item = item; } }
Node类为单链表的节点,其中包括了一个数据域Item与一个指针域Next(指向后继节点的位置)。
废话又多了,再次回归题目,求链表中的倒数第K个节点,直接使用最高效的方法:即为双指针法。通俗的理解方法:我们让第一个指针先走,当走到第(K-1)个指针时,第二个指针出发,然后同步进行,这样两个指针之间的距离一直保持在(k-1),当第一个指针遍历到最后一点时,那是不是第二个刚好在倒数第K个点呢,不难吧,小学生应该也会。
二 . 代码实现
class Solution { public ListNode FindKthToTail(ListNode head, int k) { // write code here //鲁棒判定 if(head == null || k == 0) { return null; } //定义前后指针数据与链表长度 ListNode a = head; ListNode b = head; int count = 1; //两指针K差距判定 while(a!=null) { count++; a = a.next; if((count - k)>1) { b = b.next; } } //链表长度不可等于或者小于节点k if (count <= k) { return null; } //返回倒数第K个节点数据 return b; } }
但是,该题目的考点是代码的鲁棒性!!!所以,更要细心的去分析,该题目中存在三处,以下一一解决:
1. 空指针问题。解决方案:在代码中加入了判断空指针的代码。
2. 链表长度少于k。解决方案:在for循环中增加判断下一个节点是否是空指针的代码。
3. 输入的参数k为0。解决方案:在代码中加入判断参数k是否为0的代码。