234 回文链表 Palindrome Linked List
问题
请判断一个链表是否为回文链表:正看和倒着看一样
例子

思路
[]是
[1]是
[1,2,1]是
[1,2,2,1]是
- 先判断[]和[1],都返回true
- 方法1 O(N) 空间复杂度O(n)
先遍历链表,保存值到数组
然后按判断字符串回文方式:前后两个指针,向中间走
确保i<j:奇数个,跳出循环时:i==j,偶数个跳出循环时:i>j
- 方法2 O(N) 空间复杂度O(1)

先找到中间结点,然后反转中间结点后面到结尾的所有结点。slow=head,fast=head。
//最终fast为null:1+2m=n+1==n=2m slow=1+m为4个的第3个
//最终fast是最后一个节点:1+2m=n slow=1+m为5个的第3个
反转后面的n/2个节点,此时后面反转的节点为<=前面链表的节点数量
比较两个链表
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None or head.next is None: return True
arr=[]
while head :
arr.append(head.val)
head=head.next
i=0
j=len(arr)-1
while i<j:
if arr[i]!=arr[j]:
return False
i+=1
j-=1
return True
#方法2
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None or head.next is None: return True
old=head
slow=head
fast=head.next
hhead=ListNode(-1)
while fast and fast.next:
fast=fast.next.next
slow=slow.next
#此时:为奇数个时slow为中间结点,为偶数个时,为前半段的最后一个,.next取后半段
slow=slow.next
#将后半段进行反转
while slow:
t=slow.next
slow.next=hhead.next
hhead.next=slow
slow=t
new = hhead.next
#比较前半段和后半段(为奇数个时,前半段长度-后半段长度=1)
while new:#因为new是后半段,当为偶数个时,和前半段一样长,当为奇数个时,比前半段少一个中间结点
if new.val!=old.val:
return False
new = new.next
old=old.next
return True
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if (head==null || head.next==null) return true;
List<Integer> list = new ArrayList<>();
while (head!=null) {
list.add(head.val);
head=head.next;
}
int i=0,j=list.size()-1;
while (i<j) {
if(!list.get(i).equals(list.get(j))) return false;
i++;
j--;
}
return true;
}
}
//方法2
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode slow=head,fast=head,h1=head;
//找到中间节点。由于slow和fast第一次都走了一步。
//最终fast为null:1+2m=n+1==n=2m slow=1+m为4个的第3个
//最终fast是最后一个节点:1+2m=n slow=1+m为5个的第3个
while(fast!=null && fast.next!=null)
{
fast = fast.next.next;
slow = slow.next;
}
//将后面len/2部分反转
ListNode h2 = new ListNode(0);
while(slow!=null){
ListNode temp = slow.next;
slow.next = h2.next;
h2.next = slow;
slow = temp;
}
//h2此时为去掉头结点的反转链表
h2 = h2.next;
while(h2!=null){
if(h1.val!=h2.val) return false;
h1 = h1.next;
h2 = h2.next;
}
return true;
}
}
143. 重排链表Reorder List
问题

例子

思路
先找到中间结点(偏后),然后反转中间结点后面到结尾的所有结点。slow=head,fast=head。
//最终fast为null:1+2m=n+1==n=2m slow=1+m为4个的第3个
//最终fast是最后一个节点:1+2m=n slow=1+m为5个的第3个
反转后面的n/2个节点,此时后面反转的节点为<=前面链表的节点数量

得到两个链表:h1 数目为n/2上取整,h2 数目n/2下取整,然后依次拼接,知道h2链表到达之前找到的中间节点(偏厚)slow【或者说h2到最后一个即.next==null】,拼接完成


代码
class Solution {
public void reorderList(ListNode head) {
if(head==null || head.next==null) return ;
ListNode fast=head,slow=head,h1=head;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
}
ListNode mid = slow;
ListNode h2 = new ListNode(0);
while(slow!=null){
ListNode t = slow.next;
slow.next = h2.next;
h2.next = slow;
slow = t;
}
h2 = h2.next;
//然后依次拼接,知道h2链表到达之前找到的中间节点(偏厚)slow【或者说h2到最后一个即.next==null】,拼接完成
while(h2!=mid){//h2.next!=null
ListNode t1 = h1.next,t2=h2.next;
h2.next = h1.next;
h1.next = h2;
h1=t1;
h2=t2;
}
return ;
}
}
92. Reverse Linked List II【 反转链表 2】
描述
Reverse a linked list from position m to n. Do it in one-pass
1 ≤ m ≤ n ≤ length of list
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
1 ≤ m ≤ n ≤ 链表长度
例子

思路

l-1不一定存在,所以先知道一个头结点h.next = head


关键点:
-
m-1_node是否存在影响很大,因为m-1_node.next=反转链表,而n+1_node是否存在无影响
-
2:遍历链表时,无论怎么样,cur都要变为cur.next,结合遇到反转的结点要保存下一个结点,所以直接:xia=cur.next cur=xia

答案 -
java
//先造一个头结点,遍历时反转(第二种头插法)
class Solution {
public ListNode reverseBetween(ListNode head, int m, int n) {
//需要第l-1,l,r,r+1四个节点信息,其中后三个一定存在(r+1节点可能为null),为保证l-1一定存在加上头结点,此时第x个,变成下标为x
ListNode h = new ListNode(0);
h.next = head;
ListNode pre = h;
//pre为下标l-1,到它一共有l个节点,除去第一个,还有l-1个,即需要走l-1步
for(int i=0; i<left-1; i++)
pre = pre.next;
ListNode lnode = pre.next;
//挨个删除left后面的节点,并插到pre后面。共删除:right-left+1 - 1个。
//反转部分共有:right-left+1个,由于left节点天然已经跟pre插入
for(int i=0; i<right-left; i++)
{
ListNode remove = lnode.next;
lnode.next = remove.next;
remove.next = pre.next;
pre.next = remove;
}
return h.next;
}
}
//插个头结点,然后找到l-1,l,r,r+1,将l~r部分反转,然后接上。
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode h = new ListNode(0);
h.next = head;
ListNode hh = h;
int i=0;
ListNode pre = null,lnode=null,rnode=null,back=null;
while(h!=null){
if(i==left-1) pre=h;
if(i==left) lnode = h;
if(i==right) rnode = h;
if(i==right+1) {
back = h;
break;
}
i++;
h = h.next;
}
rnode.next = null;
pre.next = reverse(lnode);
lnode.next = back;
return hh.next;
}
public ListNode reverse(ListNode head){
if(head==null || head.next==null) return head;
ListNode next = head.next;
ListNode h = reverse(next);
head.next = null;
next.next = head;
return h;
}
}
- python
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
cur = head
h = ListNode(0)
m_node, m_less_node = None, None
now = 1
while now <= n:
xia = cur.next
if now >= m - 1:
if now == m - 1:
m_less_node = cur
else: # >=m
if now == m:
m_node = cur
# >=m时
cur.next = h.next
h.next = cur
cur = xia
now += 1
m_node.next = cur # 为第n+1个
if m_less_node:
m_less_node.next = h.next
return head
else:
return h.next
- c++
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode* cur=head;//遍历链表
ListNode* h=new ListNode(0);//反转链表块的头结点
ListNode* m_less_node = NULL,*m_node=NULL,*xia=NULL;
int now = 1;
while(now <=n)
{
xia = cur->next;
if (now>=m-1)
{
if (now==m-1)
m_less_node = cur;
else
{//包含<=n
if (now==m)
m_node = cur;
cur->next=h->next;
h->next=cur;
}
}
cur=xia;
now ++;
}
m_node->next = cur;//cur 为n+1结点
if (m_less_node)
{
m_less_node->next = h->next;//n结点
return head;
}
else
return h->next;
}

本文深入探讨了链表的各种操作技巧,包括判断回文链表、重排链表及链表局部反转等核心算法。提供了多种实现方法,如使用额外数组、双指针技巧和原地反转等,同时对比了不同方法的空间复杂度。
5万+

被折叠的 条评论
为什么被折叠?



