* 面试题22:链表中倒数第k个节点
* 题目:输入一个单向链表,输出该链表中倒数第k个节点。
* 为了符合大多数人的习惯,本体从1开始计数,即链表的尾节点是倒数第一个节点
* 例如:一个链表有6个节点,从头节点开始,他们的值依次是1、2、3、4、5、6.
* 这个链表的倒数第3个节点是值为4的节点。
* 链表定义:
* struct ListNode{
* int m_nValue;
* LinkNode m_pnext;
* }
*
* 思路:
* 方法一:遍历两边链表 :第一遍统计链表的个数n,第二遍根据n值和k值,得到要从头节点向后走到第n-k+个节点。但是此方法要遍历两边链表。
* 方法二:遍历一遍链表:设置两个指针,都设置在头节点,第一个指针先走k-1步,第二个指针不动;
* 从第k步开始,第二个指针向后移动,第一个指针也向后移动,直到第一个指针走到链表的尾部。
* 因为 开始第一个指针走了k-1步 到达 第k个节点 然后还剩余n-k个节点,然后两个指针一起走,直到第一个指针到达尾部,那就走n-k+1步,
* 完成这长度为n【k-1步+n-k+1步 == n步】的链表的依次遍历
package Test;
import java.util.Scanner;
public class No22findKthToTail {
/*
* 面试题22:链表中倒数第k个节点
* 题目:输入一个单向链表,输出该链表中倒数第k个节点。
* 为了符合大多数人的习惯,本体从1开始计数,即链表的尾节点是倒数第一个节点
* 例如:一个链表有6个节点,从头节点开始,他们的值依次是1、2、3、4、5、6.
* 这个链表的倒数第3个节点是值为4的节点。
* 链表定义:
* struct ListNode{
* int m_nValue;
* LinkNode m_pnext;
* }
*
* 思路:
* 方法一:遍历两边链表 :第一遍统计链表的个数n,第二遍根据n值和k值,得到要从头节点向后走到第n-k+个节点。但是此方法要遍历两边链表。
* 方法二:遍历一遍链表:设置两个指针,都设置在头节点,第一个指针先走k-1步,第二个指针不动;
* 从第k步开始,第二个指针向后移动,第一个指针也向后移动,直到第一个指针走到链表的尾部。
* 因为 开始第一个指针走了k-1步 到达 第k个节点 然后还剩余n-k个节点,然后两个指针一起走,直到第一个指针到达尾部,那就走n-k+1步,
* 完成这长度为n【k-1步+n-k+1步 == n步】的链表的依次遍历
*
* */
static class ListNode{ //单向链表
int data;
ListNode next;
ListNode(int data) { //构造函数
this.data = data;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No22findKthToTail f = new No22findKthToTail();
ListNode head = new ListNode(1);
ListNode second = new ListNode(2);
ListNode three = new ListNode(3);
ListNode four = new ListNode(4);
ListNode five = new ListNode(5);
ListNode six = new ListNode(6);
ListNode seven = new ListNode(7);
head.next = second;
second.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = null;
seven.next = null;
ListNode node = head;
System.out.println("输出单向链表数据:");
for(int i=0;i<6;i++) {
System.out.println(node.data);
node = node.next;
}
System.out.println("输入要查找的倒数第k个节点的k值:");
Scanner in = new Scanner(System.in);
int k = in.nextInt();
ListNode find = f.findKthToTail(head,k);
System.out.println("输出单向链表倒数第"+k+"个字符:"+find.data);
}
//方法二:两个指针,遍历一遍链表,查找倒数第k个节点的值
public ListNode findKthToTail(ListNode head, int k) {
// TODO Auto-generated method stub
ListNode first = head;
ListNode second = head;
//******************************************
//增加鲁棒性 1>输入的head为空指针,k为0,都会导致程序崩溃,
//所以要预防性编程,考虑输入是否符合规范,不符合的处理
if(head == null || k == 0) {
return null;
}
for(int i = 0;i < k-1;i ++) {
//********************************************************
//增加鲁棒性 2>输入为head的头节点的连边界点总数少于k ,会导致空指针,造成程序崩溃
if(first.next != null) {
first = first.next;
}
else
return null;
}
//刚好first走到了链表尾部 second走到了倒数第k个字符
while(first.next != null) {
first = first.next;
second = second.next;
}
return second;
}
}
* 面试题23:链表中环的入口节点
* 题目:如果一个链表中包括环,如何找到环的入口节点?
* 例如:在一个1->2->3->4->5->6 链表中,环的入口节点是3
* ^_____________|
*
* 思路:1>确定链表中是否存在环。使用两个指针,一个快指针,一个慢指针。
* 若是快指针追上了满指针证明有环的存在;若是没有追到快指针走到了末尾,则表示没有环。
* 2>确定环的入口。定义两个指针,假设环有n个节点构成,则先将一个节点向前移动n个节点;
* 然后再将两个一起移动,当第二个指针只想入口节点时,刚好第一个指针已经绕着环走了一圈了,又回到了入口节点。
* 3>确定环中包含几个节点。1>中提到的快慢节点相遇是在环的内部,所以可以从相遇的这个节点出发向前,
* 并开始计数,当再次回到这个节点时,就能够得到环中节点数了。
package Test;
import java.util.Scanner;
import Test.No22findKthToTail.ListNode;
public class No23entryNodeOfLoop {
/*
* 面试题23:链表中环的入口节点
* 题目:如果一个链表中包括环,如何找到环的入口节点?
* 例如:在一个1->2->3->4->5->6 链表中,环的入口节点是3
* ^_____________|
*
* 思路:1>确定链表中是否存在环。使用两个指针,一个快指针,一个慢指针。
* 若是快指针追上了满指针证明有环的存在;若是没有追到快指针走到了末尾,则表示没有环。
* 2>确定环的入口。定义两个指针,假设环有n个节点构成,则先将一个节点向前移动n个节点;
* 然后再将两个一起移动,当第二个指针只想入口节点时,刚好第一个指针已经绕着环走了一圈了,又回到了入口节点。
* 3>确定环中包含几个节点。1>中提到的快慢节点相遇是在环的内部,所以可以从相遇的这个节点出发向前,
* 并开始计数,当再次回到这个节点时,就能够得到环中节点数了。
* */
static class ListNode{ //单向链表
int data;
ListNode next;
ListNode(int data) { //构造函数
this.data = data;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
No23entryNodeOfLoop e = new No23entryNodeOfLoop();
ListNode head = new ListNode(1);
ListNode second = new ListNode(2);
ListNode three = new ListNode(3);
ListNode four = new ListNode(4);
ListNode five = new ListNode(5);
ListNode six = new ListNode(6);
ListNode seven = new ListNode(7);
head.next = second;
second.next = three;
three.next = four;
four.next = five;
five.next = six;
six.next = four;
seven.next = null;
ListNode node = head;
System.out.println("输出单向链表数据:");
for(int i=0;i<6;i++) {
System.out.println(node.data);
node = node.next;
}
ListNode entryNode = e.entryNodeOfLoop(head);
System.out.println("单向链表中环的入口节点为:"+entryNode.data);
}
//环的入口节点 两个节点从头走,其一先走n步
public ListNode entryNodeOfLoop(ListNode head) {
// TODO Auto-generated method stub
//找到相遇的节点
ListNode meetingNode = meetingNode(head);
if(meetingNode == null) {
return null;
}
//环中的节点数,使用快慢指针 就使用上一步求出来的相遇的点(若有环,相遇的话,两个指针一定在环内)即可
int loopCount = 1;
ListNode node1 = meetingNode;
while(node1.next != meetingNode) { //再次回到相遇的节点,就计数完毕
loopCount++;
node1 = node1.next;
}
//根据环的节点数loopCount计算出环的入口点
node1 = head;
for(int i = 0;i < loopCount;i++) {
node1 = node1.next;
}
ListNode node2 = head;
while(node1 != node2) {
node1 = node1.next;
node2 = node2.next;
}
return node1;
}
//两个指针相遇的节点
public ListNode meetingNode(ListNode head) {
if(head == null) {
return null;
}
ListNode slow = head.next;
if(slow == null) {
return null;
}
ListNode fast = slow.next;
while(slow != null || fast != null) {
if(slow == fast) {
return slow;
}
slow = slow.next;
fast = fast.next;
if(fast != null) {
fast = fast.next;
}
}
return null;
}
}