这次分享两个相对简单,但是又是必备的链表题。先给出链表结构的定义。
public class ListNode{
int val;
ListNode next;
ListNode(){}
ListNode(int val){
this.val = val;
}
ListNode(int val, ListNode next){
this.val = val;
this.next = next;
}
}
反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
如链表1->2->3->4->null,反转后的结果为4->3->2->1->null。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head;
ListNode pre = null;
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
}
以1->2->3->4->null为例子,说明反转链表的整个过程。cur指向1,pre指向null,进入while循环,tmp指向2->3->4->null,cur.next指向pre,也就是1->null,之后更新pre和cur。pre指向了1,cur指向了2。一轮循环结束。
第二轮循环,tmp指向3->4->null,cur.next指向pre,也就是2->1->null。之后更新pre和cur,pre指向2,cur指向3。第二轮循环结束。
第三轮循环,tmp指向了4->null,cur.next指向pre,也就是3->2->1->null。之后更新pre和cur,pre指向3,cur指向4。第三轮循环结束。
第四轮循环,tmp指向null,cur.next指向pre,4->3->2->1->null。之后更新pre和cur,pre指向4,cur指向null。cur指向null,在第五轮循环时不满足while条件,跳出循环。此时,pre指向了反转后链表的头结点。注意上述while循环体内,每个语句的顺序都是固定的,不能调换。
回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
如链表1->2->2->1->null就是一个回文链表。1->2->3->null不是一个回文链表。
回文链表有两种做法,第一种做法先遍历一遍链表,将链表中的数据保存为字符串,之后按照字符串验证回文串的方法进行验证。
还有一种做法的思路就是先找到链表的中间结点,将中间结点的下一个结点到尾结点反转,之后同时遍历对比头结点到中间结点和反转后的从中间结点下一个结点到末尾结点这两端链表的值是否相同。这里有两个难点,一个就是如何找到链表的中间结点,第二个难点就是如何实现链表的反转。链表的反转已经在上一个题给出。
class Solution {
public boolean isPalindrome(ListNode head) {
// 找到链表的中间结点
ListNode fast = head;
ListNode slow = head;
while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
slow = slow.next;
}
// 反转中间结点的下一个结点到末尾结点这段链表
ListNode cur = slow.next;
ListNode pre = null;
while(cur != null){
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
// 对比这起始结点到中间结点,反转后中间结点的下一个结点到末尾结点,这两段链表的值是否相等
ListNode node = head;
ListNode node2 = pre;
boolean isValid = true;
while(node2 != null && node != null){
if(node2.val != node.val){
isValid = false;
}
node = node.next;
node2 = node2.next;
}
// 将链表恢复原样
// cur = pre;
// pre = null;
// while(cur != null){
// ListNode tmp = cur.next;
// cur.next = pre;
// pre = cur;
// cur = tmp;
// }
// slow.next = pre;
// 遍历验证链表是否还原
// node = head;
// while(node != null){
// System.out.println(node.val);
// node = node.next;
// }
return isValid;
}
}
这里就来说明一下如何通过快慢指针得到链表的中间结点。思路也比较简单,就是快慢指针同时从头结点开始,快指针每次走两步,慢指针每次走一步,当快指针不满足fast.next != null或者fast.next.next != null条件跳出循环时,slow指针也就是慢指针就指向了链表的中间结点(如果结点个数为奇数个,slow就指向中间结点,如链表为1->2->3->4->5->null,slow最后就指向3,如果链表为偶数,如链表1->2->3->4->null,slow最后指向为2)。
以1->2->3->2->1->null为例,第一步,得到slow指向中间结点3,将slow指向的下一个结点至尾结点进行反转,也就是将2->1->null反转为1->2->null这段。反转之后pre指针就指向了反转后的头结点。之后同时遍历对比1->2->3->null和1->2->null链表的值是否相等。注意while循环的条件,必须两个链表结点都不为空,因为原链表个数为奇数时,头结点到中间结点要比中间结点的下一个结点到尾结点多一个结点。
为了体现完整性,在对比完两段结点得出结果之前,最好将链表还原成初始状态。