数据结构List实例(三):寻找倒数第k个结点值

来源:互联网 发布:网络政治参与图片 编辑:程序博客网 时间:2024/06/12 00:02

    这个也是非常常见的问题。

    分析:为了得到倒数第k个结点,很自然的想法是先走到链表的尾端,再从尾端回溯k步。可是输入的是单向链表,只有从前往后的指针而没有从后往前的指针。因此我们需要打开我们的思路。既然不能从尾结点开始遍历这个链表,我们还是把思路回到头结点上来。假设整个链表有n个结点,那么倒数第k个结点是从头结点开始的第n-k-1个结点(从0开始计数)。如果我们能够得到

    链表中结点的个数n,那我们只要从头结点开始往后走n-k-1步就可以了。如何得到结点数n?这个不难,只需要从头开始遍历链表,每经过一个结点,计数器加一就行了。这种思路的时间复杂度是O(n),但需要遍历链表两次。第一次得到链表中结点个数n,第二次得到从头结点开始的第n?-k-1个结点即倒数第k个结点。
    如果链表的结点数不多,这是一种很好的方法。但如果输入的链表的结点个数很多,有可能不能一次性把整个链表都从硬盘读入物理内存,那么遍历两遍意味着一个结点需要两次从硬盘读入到物理内存。我们知道把数据从硬盘读入到内存是非常耗时间的操作。我们能不能把链表遍历的次数减少到1?如果可以,将能有效地提高代码执行的时间效率。
    如果我们在遍历时维持两个指针,第一个指针从链表的头指针开始遍历,在第k-1步之前,第二个指针保持不动;在第k-1步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在k-1,当第一个(走在前面的)指针到达链表的尾结点时,第二个指针(走在后面的)指针正好是倒数第k个结点。
    这种思路只需要遍历链表一次。对于很长的链表,只需要把每个结点从硬盘导入到内存一次。因此这一方法的时间效率前面的方法要高。

    这是双指针思维在链表中的第二次应用。实现代码如下所示:

/**   
* @Title: KthBackwards.java 
* @Package List 
* @Description: TODO 
* @author peidong  
* @date 2017-4-24 上午8:23:02 
* @version V1.0   
*/
package List;


import List.MergeSortList.ListNode;


/** 
 * @ClassName: KthBackwards 
 * @Description: 寻找链表的倒数第k个结点
 * @date 2017-4-24 上午8:23:02 
 *  
 */


public class KthBackwards {

/**

* @ClassName: ListNode 
* @Description: 构建链表结点
* @date 2017-4-24 上午8:30:22 
*
*/
public static class ListNode{
public int data;
public ListNode next;


public ListNode(int data) {
this.data = data;
this.next = null;
}
}


/**

* @Title: findKthBackwardsSolution1 
* @Description:实现第一种思路
* @param @param head
* @param @param k
* @param @return    
* @return ListNode    
* @throws
*/
public static ListNode findKthBackwardsSolution1(ListNode head, int k){
   //如果链表为空
if(head == null){
return null;
}

//计算当前链表的结点数
ListNode pCur = head;
int num = 0;
while(pCur.next != null){
pCur = pCur.next;
num ++;
}

//如果k的值比num大
if(num < k){
return null;
}

//重新开始查找
pCur = head;
for(int i = 0; i< num-k; i++){
pCur = pCur.next;
}
return pCur;
}

/**

* @Title: findKthBackwardsSolution2 
* @Description: 实现第2种思路
* @param @param head
* @param @param k
* @param @return    
* @return ListNode    
* @throws
*/
public static ListNode findKthBackwardsSolution2(ListNode head, int k){
//如果链表为空
if(head == null){
return null;
}

//构建亮哥指针
ListNode p1 = head;
ListNode p2 = null;

//先让第一个指针走k-1步
for(int i = 0; i< k; i++){
if(p1.next != null){
p1 = p1.next;
}else{
return null;
}
}

//开始走第二个指针
p2 = head;

while(p1.next != null){
p1 = p1.next;
p2 = p2.next;
}

return p2;
}
/** 
* @Title: main 
* @Description: 
* @param @param args    
* @return void    
* @throws 
*/
public static void main(String[] args) {
// TODO Auto-generated method stub

// 添加数据
ListNode t1 = new ListNode(13);
ListNode t2 = new ListNode(11);
ListNode t3 = new ListNode(15);
ListNode t4 = new ListNode(17);
ListNode t5 = new ListNode(16);
ListNode t6 = new ListNode(20);
ListNode t7 = new ListNode(9);
ListNode t8 = new ListNode(6);


// 构建链表
t1.next = t2;
t2.next = t3;
t3.next = t4;
t4.next = t5;
t5.next = t6;
t6.next = t7;
t7.next = t8;

        t1 = KthBackwards.findKthBackwardsSolution1(t1, 3);
        ListNode t =KthBackwards.findKthBackwardsSolution2(t1, 3);
System.out.println(t1.data);
System.out.println(t.data);
}


}

0 0
原创粉丝点击