引言:以下内容均只是个人看法,并无严格经过核对,如有异议,欢迎交流,发现错误,欢迎您的及时指正,感谢!本文所示代码均为java代码
本文中使用的结点定义语句如下
public class listNode {
public int data;
public listNode next;
public listNode(int data) {
this.data = data;
this.next=null;
}
}
单链表的遍历一般从第一个结点依次遍历整个链表,所以第一个结点十分重要,在本文中头节点指的是第一个存放数据的结点,与其他相关文章中关于它的定义可能不同,无需深究,理解相关操作的逻辑原理即可,本质上无较大差别。
1.双指针寻找中间节点
leetcode876,给出一个带有头节点为head的非空链表,返回位于中间位置的节点,如有两个中间节点即返回靠后的一个
示例
输入 1,2,3,4
输出 3
输入 1,2,3,4,5
输出 3
思路设置fast和low指针,fast步长为2,low步长为1,当fast到达终点时,low刚好处于中间位置,设置low和fast初始均等于head,当链表长度为奇数时,fast.next==null为截止条件,当链表长度为偶数时,fast==null为截止条件
注意:当fast和low的起始地址不为head,例如是指向head的一个虚拟节点,那么fast指针停止后,low指针的位置可能和下面的情况不一样。
/**
*
* @param head 需要查找中间节点的链表的头节点
* @return
*/
public listNode middleNode(listNode head){
listNode fast=head,low=head;
//当链表长度为奇数或者偶数时,例如(1,2,3,4)或(1,2,3)从head开始,fast最后在第一个链表的4停止,在第二个链表的3.next停止
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next.next;
}
return low;
}
2.寻找倒数第k个元素
输入一个链表,输出该链表中倒数第k个节点。
本题从1开始计数,即链表的尾节点是倒数第1个节点。
示例 给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4
思路:使用两个指针low和fast,先让fast指针向后走k步,注意可能k可能大于链表长度,所以在fast向后移动时需要判断fast是否为空,移动结束后同时移动low和fast,当fast遍历完整个链表后,low恰好在倒数第k个节点的位置上
代码如下
/**
*
* @param head 实现操作的链表的头节点
* @param k 倒数第k个节点
* @return
*/
//寻找倒数第k个元素
public listNode getKeythFromEnd(listNode head,int k){
listNode fast=head;
listNode low=head;
//让fast比low先走k步,这里判断fast是否为空,是为了防止k比链表长度大的情况
while(k>0&&fast!=null){
fast=fast.next;
k--;
}
//因为low比fast慢k步,所以当fast到达终点时,low即为倒数第k个节点
while(fast!=null){
fast=fast.next;
low=low.next;
}
return low;
}
3.旋转链表
leetcode61,给出一个链表的头节点,让链表的每个节点都向右移动k个位置
示例:
给出链表是1->2->3->4->5 k=2
思路一:我们观察输入和输出,相当于将链表成环,不断改变起始结点的位置,我们根据题目容易知道当k大于n时,相当于移动的位置为k%n,当k%n==0时,不需要修改头节点的指向,在head的基础上向后遍历n-k%n个结点(包括head处的结点)后断开环。
思路二:观察结果,相当于把链表分为123和45两条链表,把123放到45后面,这个思路的关键是找到断开的地方(这里对于断开位置的查找可以用双指针找到,或者结合k与断开位置的关系),比如样例中是断开3与4之间,将4作为新的头结点,然后把123排到5后面
思路三:将整个链表反转,变成54321,然后将前k个节点和剩下的n-k个节点分别反转变成45123
(关于反转的思想在下一大关会进行汇总)
下面的代码仅用了双指针来实现
/**
* 利用双指针完成链表的旋转
* @param head
* @param k
* @return
*/
public static listNode rotateRight(listNode head, int k) {
if(head==null||k==0){
return head;
}
listNode node=head;
listNode fast=head;
listNode slow=head;
int len=0;
while(node!=null){
len++;
node=node.next;
}
int move=k%len;
for(int i=0;i<move;i++){
fast=fast.next;
}
//让fast遍历到倒数第一个节点,slow到达倒数第k+1个节点,这样slow.next就是倒数第k个节点
while(fast.next!=null){
slow=slow.next;
fast=fast.next;
}
listNode res=slow.next;
slow.next=null;
fast.next=head;
return res;
}