【回文链表】反转后半段,依次比较
解题思路
- 快慢指针二分链表
- 通过fast是否为空,判断链表节点数量是奇数还是偶数,并找到中心点
- 让slow指向后半段的初始节点,反转后半段链表,fast指向头结点
- 依次循环比较
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode fast, slow;
slow = fast = head;
//通过快慢指针二分链表,并且找到中点
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//注意:slow和fast都是从head开始走的,slow一次走一步,fast一次走两步
//所以如果链表节点数量是奇数,应该是fast正好是最后一个,slow正好指向中间轴
//如果链表节点数量是偶数,跳出循环时,fast为空,slow指向后半段第一个
if (fast != null) {
slow = slow.next;
}
//反转后半部分链表,之后slow是反转后的链表的头结点,fast指向前半段头结点
slow = reverse(slow);
fast = head;
//比较值是否相等
while(slow != null){
if(slow.val != fast.val) return false;
slow = slow.next;
fast = fast.next;
}
return true;
}
//反转链表
public ListNode reverse(ListNode head) {
if(head == null) return head;
ListNode pre,post;
pre = head;
post = null;
while(pre != null){
ListNode tmp = pre.next;
pre.next = post;
post = pre;
pre = tmp;
}
return post;
}
}
从尾到头打印链表
三种方法:逆序、递归、栈
- 原链表逆序——得到链表长度——初始化数组——遍历逆序后的链表,按位置插入结果数组
class Solution {
public int[] reversePrint(ListNode head) {
//逆序链表
ListNode revListHead = reverse(head);
//计算链表长度
int size = 0;
ListNode count = revListHead;
while(count != null){
count = count.next;
size++;
}
//通过size初始化结果数组
int[] res = new int[size];
int index = 0;
//遍历链表,往数组里插入数值
while(revListHead != null){
res[index] = revListHead.val;
revListHead = revListHead.next;
index++;
}
return res;
}
//逆序链表
public ListNode reverse(ListNode head){
ListNode pre,post;
pre = head;
post = null;
while(pre != null){
ListNode tmp = pre.next;
pre.next = post;
post = pre;
pre = tmp;
}
return post;
}
}
- 递归法
class Solution {
//定义全局变量,i用来初始化数组容量
//递归后半段,从后往前,j是元素插入数组的索引位置
//res数组定义要放在全局,不能放在方法中
int i = 0;
int j = 0;
int[] res;
public int[] reversePrint(ListNode head) {
recur(head);
return res;
}
public void recur(ListNode head){
if(head == null){
res = new int[i];
return;
}
i++;
recur(head.next);
res[j] = head.val;
j++;
}
}
3.通过栈完成
class Solution {
public int[] reversePrint(ListNode head) {
LinkedList<ListNode> stack = new LinkedList<>();
//把链表节点从头到尾放入栈中
while(head != null){
stack.addFirst(head);
head = head.next;
}
//通过栈大小得到结果数组大小
int[] res = new int[stack.size()];
int index = 0;
//出栈,把值存入结果数组
while(!stack.isEmpty()){
res[index++] = stack.poll().val;
}
return res;
}
}
删除链表中的特定节点
- 考虑特殊情况:
head == null
和head.val == val
- 其他情况:设置当前节点
cur
检测,前置节点pre
辅助删除
class Solution {
public ListNode deleteNode(ListNode head, int val) {
if(head == null) return head; //head为空
if(head.val == val) return head.next; //第一个元素就是val,特殊处理
//除了第一个元素,其他任何元素的删除操作,必须存在一个前置节点
ListNode pre,cur;
pre = null;
cur = head;
while(cur != null){
if(cur.val == val) break;
pre = cur;
cur = cur.next;
}
pre.next = pre.next.next;
return head;
}
}
- 递归
思想:
递归前半段是找到那个值为val的节点,后半段就是把节点接上
递归出口有两个:
(1)是head == null直接返回
(2)还有一个出口是head.val == val,当前的head即为要删除的节点
所以当head.val == val时,返回head.next即可
【返回链表倒数第k个节点】
解题思路
固定好双指针之间的距离
双指针向后滑动,滑到fast为空,返回slow即可
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
if(head == null) return head;
ListNode slow,fast;
slow = fast = head;
//固定好slow和fast的距离
while(k > 0){
fast = fast.next;
k--;
}
//一起向后滑动
while(fast != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
【删除链表倒数第k个节点】请与上一题作比较
思想:想删除倒数第k个,有一个指针在倒数第k+1的位置上即可;
- 滑动的游标卡尺的长度为k+1
- 为了统一头结点的操作,我们新建一个头结点nHead
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head == null || head.next == null) return null;
ListNode nHead = new ListNode(0);
nHead.next = head;
ListNode slow,fast;
slow = fast = nHead;
while(n > 0){
fast = fast.next;
n--;
}
while(fast.next != null){
slow = slow.next;
fast = fast.next;
}
slow.next = slow.next.next;
return nHead.next;
}
}
链表逆序
固定模板,熟记
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null) return head;
ListNode pre,post;
pre = head;
post = null;
while(pre != null){
ListNode tmp = pre.next;
pre.next = post;
post = pre;
pre = tmp;
}
return post;
}
}
两个链表的第一个公共节点
思想:浪漫相遇,走过你来时的路,我就会和你相遇
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null) return null;
ListNode a,b;
a = headA;
b = headB;
while(a != b){
a = a==null?headB:a.next;
b = b==null?headA:b.next;
}
return a;
}
}
合并两个升序链表
- 递归
- 非递归
递归
在这里插入代码片
非递归
新建链表头结点,尾插法建立链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
/*
非递归,新建链表头结点,尾插法建立链表
*/
if(l1 == null && l2 == null) return null;
if(l1 == null) return l2;
if(l2 == null) return l1;
ListNode newHead = new ListNode(0);
ListNode tail = newHead;
while(l1 != null && l2 != null){
if(l1.val < l2.val){
tail.next = l1;
l1 = l1.next;
tail = tail.next;
continue;
}else if(l1.val > l2.val){
tail.next = l2;
l2 = l2.next;
tail = tail.next;
continue;
}else{
tail.next = l1;
l1 = l1.next;
tail = tail.next;
tail.next = l2;
l2 = l2.next;
tail = tail.next;
continue;
}
}
if(l1 != null) tail.next = l1;
if(l2 != null) tail.next = l2;
return newHead.next;
}
}