面试题15:链表中倒数第k个结点
//面试题15:链表中倒数第k个结点
//题目:输入一个链表,输出该链表中倒数第K个结点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾结点是
//倒数第1个结点。例如一个链表有6个结点,从头结点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个结//点是值为4的结点。
//要求只遍历链表一次,所以需要使用两个指针
//问题:考虑各种出现的特殊情况,如:头指针为空,K的值大于链表长度,输入的K为0
ListNode类
1 package offer.utilities; 2 3 4 //结点的定义 5 public class ListNode { 6 private Object element; //私有的成员变量 7 private ListNode next ; 8 9 public ListNode(Object ele, ListNode next) { 10 // TODO Auto-generated constructor stub 11 this.element = ele ; 12 this.next = next ; 13 } 14 15 //next 的set,get方法 16 public ListNode getNext() { 17 return next; 18 } 19 20 public void setNext(ListNode next) { 21 this.next = next; 22 } 23 24 //node的get set方法 25 public Object getData() { 26 // TODO Auto-generated method stub 27 return element; 28 } 29 30 public void setData(Object obj) { 31 // TODO Auto-generated method stub 32 element = obj; 33 } 34 35 //连接两个节点 36 public static void connectListNodes(ListNode pCurrent , ListNode pNext) throws Exception{ 37 if(pCurrent == null){ 38 System.out.println("Error to conncet two nodes"); 39 throw(new Exception("当前节点为空节点")); 40 } 41 pCurrent.setNext(pNext); 42 } 43 44 //打印链表 45 public static void PrintList(ListNode head){ 46 System.out.println("PrinyList start"); 47 ListNode pNode = head ; 48 System.out.print("{ "); 49 while(pNode != null){ 50 System.out.print(pNode.getData() +" "); 51 pNode= pNode.getNext(); 52 } 53 System.out.println("}"); 54 } 55 56 }
方法思路:
假设整个链表有n个节点,那么倒数第K 个节点就是从头结点开始的第 n-K+1 个节点。
方法一:第一次遍历链表,得出节点数 n , 第二次遍历链表就能找到倒数第 K 个节点, 一共需要两次遍历。
方法二:只需要遍历链表一次即可。
在方法一的基础上改进如下:
为了实现只遍历链表一次就能找到倒数第 K 个节点,可以定义两个指针。
第一个指针从链表的头指针开始遍历向前走 k - 1 步,第二个指针保持不动。
从第 k 步 开始,第二个指针也开始从链表的头指针开始遍历。
当第一个指针指向链表的最后一个节点时,第二个指针刚好指向链表的倒数第 K 个节点。
写代码时,要考虑鲁棒性,最好采用防御性变成,即考虑在哪些地方会出错,
然后提前加上错误判断,这样可以避免错误的输入而导致程序崩溃。
方法二中,需要可考虑的情况有:
1. 链表为空 ,返回null。
2. 输入的 K值为0 , 由于计数是从 1 开始的,数以输入0没意义,返回null。
3. 输入的 K值 大于链表的长度 ,在 for 循环中遍历链表可能会出现指向 null 的指针,
因此需要在 for 循环中加入一个 if 判断。
1 package com.hb.jzoffer; 2 3 import offer.utilities.ListNode; 4 5 6 //面试题15:链表中倒数第k个结点 7 //要求只遍历链表一次,所以需要使用两个指针 8 //问题:考虑各种出现的特殊情况,如:头指针为空,K的值大于链表长度,输入的K为0 9 10 public class KthNodeFromEnd { 11 12 ListNode FindKthFromTail(ListNode head , int k) throws Exception{ 13 14 if(head == null || k == 0){ 15 throw new Exception("输入有误"); 16 } 17 ListNode phead = head ; 18 ListNode Pbehind =null; 19 20 for(int i = 0 ; i < k -1 ; ++ i){ 21 if(phead.getNext() != null){ //判断条件,防止K的值大于节点个数 22 phead = phead.getNext(); 23 }else{ 24 return null; 25 } 26 } 27 28 Pbehind = head ; 29 while(phead.getNext() != null){ 30 phead = phead.getNext() ; 31 Pbehind = Pbehind.getNext() ; 32 } 33 34 return Pbehind; 35 } 36 37 38 public static void main(String[] args) throws Exception { 39 ListNode node1 = new ListNode("1" , null); 40 ListNode node2 = new ListNode("2" , null); 41 ListNode node3 = new ListNode("3" , null); 42 ListNode node4 = new ListNode("4" , null); 43 ListNode node5 = new ListNode("5" , null); 44 ListNode node6 = new ListNode("6" , null); 45 46 ListNode.connectListNodes(node1, node2); 47 ListNode.connectListNodes(node2, node3); 48 ListNode.connectListNodes(node3, node4); 49 ListNode.connectListNodes(node4, node5); 50 ListNode.connectListNodes(node5, node6); 51 52 System.out.println("初始化链表:"); 53 ListNode.PrintList(node1); 54 55 KthNodeFromEnd test = new KthNodeFromEnd(); 56 ListNode kthNode = test.FindKthFromTail(null, 3); 57 System.out.println(kthNode.getData()); 58 } 59 60 }