今天又是元气满满的一天,加油
涉及leecode题目
目录
1、递归反转整个链表
了解链表的基础知识后再来看这个反转链表,真的很有意思,嗯,数据结构感兴趣有动力就变得容易理解了哦
根据作者的思路,这种递归的题目不能用传统方法,在脑子里debug,一层一层肯定晕掉了,需要有整体的思维。
ListNode reverse(ListNode head){
if(head.next == null){
return head;
}
ListNode last = reverse(head.next);
//很神奇将链表两个节点指向关系翻转
head.next.next = head;
head.next = null;
return last;
}
2、实现翻转前N个节点
ListNode successor = null;
ListNode reverseN(LiseNode head, int n){
if(n==1){
successor = head.next;
return head;
}
ListNode last = reverseN(head.next,n-1);
head.next.next = head;
//翻转递归最后一步,将第n个节点后面的部分连接到第一个节点以后
head.next = successor;
return last;
}
3、翻转链表的一部分
这个题真的太难理解了,上一个还好,这个题是基于上一个的,我是这样理解:
ListNode reverseBetwween(ListNode head, int m,int n){
//这里巧妙的把区间的概念(m,n)转化为上一题前n个翻转的问题,
//head在这里已经不再是表头
if(m==1){
return reverseN(head,n);
}
//这里分为两部分理解,等号前面其实相当于不发生反转的部分向后索引
//等号后面便是递归,找到需要翻转的链表部分的“head”,这里是由n这个数来控制
head.next = reverseBetween(head.next,m-1,n-1);
return head;
}
实战练习
92. 反转链表 II
class Solution {
ListNode tempNode = null;
public ListNode reverseN(ListNode head, int n){
if(n==1){
tempNode = head.next;
return head;
}
ListNode last = reverseN(head.next, n-1);
head.next.next = head;
head.next = tempNode;
return last;
}
public ListNode reverseBetween(ListNode head, int left, int right) {
if(left==1){
return reverseN(head,right);
}
head.next = reverseBetween(head.next,left-1,right-1);
return head;
}
}
25.k个一组翻转链表
这道题没做过翻转链表相关题目的话实在不好想
看了题解之后,分解开来理解如下:
首先k个一组翻转,每组内部翻转需要一个方法,参数为链表中两个节点[a,b),返回的是每组b之前的节点;
然后需要构造a和b,通过for循环去做
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if(head == null){
return null;
}
ListNode a, b;
a=b = head;
for(int i=0;i<k;i++){
if(b==null){
return head;
}
b=b.next;
}
ListNode newHead = reverse(a,b);
a.next=reverseKGroup(b,k);
return newHead;
}
public ListNode reverse(ListNode a,ListNode b){
ListNode pre, cur,nxt;
pre = null;
cur=a;
nxt = a;
while(cur!=b){
nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}
234.回文链表
通过双指针的思路可以如以下方法:
它是用递归,然后有别于以前我做递归一层一层处理的思路,用的是每层递归做类似缓存的操作,每层操作拿到对应的递归栈中的数据做对比,真的震到我了,想不到还能这样做题:
class Solution{
ListNode left;
public boolean isPalindrome(ListNode head){
left = head;
return traverse(head);
}
boolean traverse(ListNode right){
if(right ==null){
return true;
}
boolean res = traverse(right.next);
res = res&&(right.val==left.val);
left = left.next;
return res;
}
}
这个题是做出来了,但是空间复杂度有点高,回文判断嘛,不需要每个节点都过一遍。
优化空间复杂度
在这里又学到一个新技巧:快慢指针!
ListNode slow,fast;
slow = fast = head;
while(fast!=null&&fast.next!=null){
slow =slow.next;
fast = fast.next.next;
}
这两个节点就像赛跑一样,通过循环中的条件可以看出结果无非两种,fast指向null与否。
这样子写的作用是判断链表长度是奇数还是偶数。
若是fast没有指向null,说明链表是奇数,这样slow还要再前进一步
最后就可以比较回文串了
题解:
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode slow,fast;
slow= fast=head;
while(fast!=null&&fast.next!=null){
slow = slow.next;
fast= fast.next.next;
}
if(fast!=null){
slow=slow.next;
}
ListNode left = head;
ListNode right = reverse(slow);
while(right !=null){
if(left.val != right.val){
return false;
}
left = left.next;
right = right.next;
}
return true;
}
ListNode reverse(ListNode a){
ListNode pre = null,cur = a;
while(cur!=null){
ListNode nxt = cur.next;
cur.next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
}