例1:找出单向链表中的一个节点,该节点到尾指针的距离为K。链表的倒数第0个结点为链表的尾指针。要求时间复杂度为O(n)。
链表节点的值初始化为1,2,3,4,5,6,7
function LinkFun(){//自定义链表的方法
let Node = function(val){
this.val = val;
this.next = null
}
let head = null;
let length = 0;
this.append = function(val){//向链表添加节点以及数据的方法
let node = new Node(val);
let cur ;
if(head == null){//第一次进入这个方法,head为空,
head = node
}else{//第二次以后调用这个方法,进当前的判断
cur = head;
while(cur.next){
cur = cur.next
}
cur.next =node
}
length ++
}
this.getHead = function(){
return head
}
this.getLength = function(){
return length
}
}
let arr = [];
for(let i = 1;i<8;i++){
arr.push(i)
}
let link = new LinkFun();
for(let i = 0;i<7;i++){
link.append(arr[i])
}
let head = link.getHead()
function getNode(head){
let arr = [],pNode = head;
while(pNode){
arr.unshift( pNode.val )
pNode = pNode.next
}
return arr
}
let res = getNode(head)
let n = readline()
print(res[n-1])
例2:已知输入一个链表,求该链表中的倒数第k个节点
(1)方法一:可以遍历链表然后将它存入数组,然后查询就可以了,实现过程就如同上一题的getNode()方法。
(2)方法二:可以用栈。遍历链表,然后将节点一次压入栈中,最后根据k,依次弹出,弹出的次数等于k,那么当前这个节点就是倒数第k个。
private ListNode sol1(ListNode head, int k) {
Stack<ListNode> stack = new Stack<>();
while (head != null) {
stack.push(head);
head = head.next;
}
if (stack.size() < k) {
return null;
}
while (k > 1) {
stack.pop();
k--;
}
return stack.pop();
}
(3)方法三:定义两个快慢指针。
以快指针走到null停下为例:
快指针停在null,慢指针刚好停在倒数第K个结点。
此时就它两的距离是K,所以我们设置慢指针开始走的条件应该是快指针走了K步之后。第K+1步的时候,快慢指针一起走。
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
//初始化两个指针
ListNode fast,slow;
fast= head;
slow= head;
//用于统计快指针走的步数
int i = 0;
//当快指针指向null时,停止循环
while(fast != null){
//当i大等k时,目标指针开始走
/*这个判断写在最前面,逻辑会清晰点:
不会漏掉对i=0时fast 、slow都指向head时,slow能不能走的判断*/
if(i >= k){
slow= slow.next;
}
//快指针指向下一个结点
fast = fast .next;
//i加一
i++;
}
//如果i小于k,说明输入了k大于总结点数,返回null
if(i < k){return null;}
//否则返回目标结点
return slow;
}
}