链表题汇总
c. 反转链表
输入一个链表,反转链表后,输出新链表的表头。
思路
还是递归好想
class Solution {
public ListNode ReverseList(ListNode head) {
if (head==null || head.next==null) return head;
ListNode newHead=ReverseList(head.next);
head.next.next=head;
head.next=null;
return newHead;
}
}
迭代,你指针赋值之前一定要保存下一个指针在哪,不然会找不到
class Solution {
public ListNode ReverseList(ListNode head) {
if (head==null || head.next==null) return head;
ListNode pre=null, post=head;
while (head!=null) {
post=head.next;
head.next=pre;
pre=head;
head=post;
}
return pre;
}
}
b. 链表内指定区间反转
思路
先找到反转区间的起点,然后双指针翻转即可
注:第一次因为指针指向顺序的错误导致丢失了结点
func reverseBetween(head *ListNode, m, n int) *ListNode {
dead := new(ListNode)
dead.Next = head
pre, cur := dead, dead.Next //推荐这里写pre=dead代替pre=head,因为后面会因为pre为null报错
for i := 1; i < m; i++ {
pre = cur
cur = cur.Next
}
for i := 0; i < n-m; i++ {
nxt := cur.Next
cur.Next = cur.Next.Next
nxt.Next = pre.Next
pre.Next = nxt
}
return dead.Next
}
a. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
int n=0;
ListNode dead=new ListNode(0), pre=dead, cur=head, p=head, tmp;
dead.next=head;
while (p!=null) { n++; p=p.next; }
for (int i=0, group=n/k; i<group; i++) {
for (int j=1; j<k; j++) {
tmp=cur.next;
cur.next=tmp.next;
tmp.next=pre.next; //注:不是cur,因为cur会交换到后面,此时如果pre.next始终指向后一个结点
pre.next=tmp;
}
pre=cur;
cur=cur.next;
}
return dead.next;
}
}
c. 合并有序链表
将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。
class Solution {
public ListNode mergeTwoLists (ListNode l1, ListNode l2) {
if (l1==null) return l2;
if (l2==null) return l1;
ListNode dead=new ListNode(-1), p=dead;
dead.next=p;
while (l1!=null && l2!=null) {
if (l1.val<l2.val) {
p.next=l1;
l1=l1.next;
} else {
p.next=l2;
l2=l2.next;
}
p=p.next;
}
p.next=l1==null ? l2 : l1;
return dead.next;
}
}
c. 两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
简单解法
class Solution {
public ListNode FindFirstCommonNode(ListNode p1, ListNode p2) {
boolean vis[]=new boolean[100005];
while (p1!=null) {
vis[p1.val]=true;
p1=p1.next;
}
while (p2!=null) {
if (vis[p2.val]) return p2;
p2=p2.next;
}
return null;
}
}
双指针:p走了a+b,q走了b+a,则会在c点相遇
class Solution {
public ListNode FindFirstCommonNode(ListNode head1, ListNode head2) {
ListNode p1=head1, p2=head2;
while (p1!=p2) {
p1 = p1==null ? head2 : p1.next;
p2 = p2==null ? head1 : p2.next;
}
return p1;
}
}
c. 判断一个链表是否为回文结构
请判断一个链表是否为回文链表
思路:栈,需要想一下链表的结点个数的奇偶性:快慢指针找到链表的后半段,并存入栈中(链表长度为奇数的话就是中间结点的下一个),然后比较栈中元素和前半段元素
class Solution {
public boolean isPalindrome(ListNode head) {
if (head==null || head.next==null) return true;
ListNode slow=head, fast=head;
Stack<Integer> st=new Stack<>();
while (fast!=null && fast.next!=null) {
slow=slow.next;
fast=fast.next.next;
}
while (slow!=null) {
st.push(slow.val);
slow=slow.next;
}
while (!st.isEmpty()) {
if (st.pop()!=head.val) return false;
head=head.next;
}
return true;
}
}
O(1)空间解法:将链表的后半段翻转然后和前半段比较
class Solution {
ListNode reverse(ListNode head) {
if (head==null || head.next==null) return head;
ListNode pre=null, post=head;
while (head!=null) {
post=head.next;
head.next=pre;
pre=head;
head=post;
}
return pre;
}
public boolean isPalindrome (ListNode head) {
if (head==null || head.next==null) return true;
ListNode slow=head, fast=head;
while (fast!=null && fast.next!=null) {
slow=slow.next;
fast=fast.next.next;
}
slow=reverse(slow);
while (slow!=null) {
if (slow.val!=head.val) return false;
slow=slow.next;
head=head.next;
}
return true;
}
}
c. 判断链表是否有环
判断给定的链表中是否有环;扩展:你能给出空间复杂度的解法么?
public class Solution {
public boolean hasCycle(ListNode head) {
if (head==null || head.next==null) return false;
ListNode slow=head, fast=head.next;
while (fast!=null && fast.next!=null) {
if (fast.val==slow.val) return true;
slow=slow.next;
fast=fast.next.next;
}
return false;
}
}
b. 链表环的入口结点
func detectCycle(head *ListNode) *ListNode {
slow, fast := head, head
for (fast != nil && fast.Next != nil) {
slow, fast = slow.Next, fast.Next.Next
if (slow == fast) {
slow = head
for (slow != fast) {
slow, fast = slow.Next, fast.Next
}
return slow
}
}
return nil
}
b. 重排链表
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.
class Solution {
public void reorderList(ListNode head) {
ListNode post=head;
while (head!=null && head.next!=null) {
while (post.next.next!=null) {
post=post.next;
}
ListNode t=post.next; //重排区域的尾节点
post.next=null;
t.next=head.next; //为什么要把post.next指定在这里置空,移到下一行都不行
head.next=t;
head=post=t.next;
}
}
}
b. 生成相加链表
给定两个这种链表,请生成代表两个整数相加值的结果链表。
例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。
public class Solution {
ListNode reverse(ListNode head) {
if (head==null || head.next==null) return head;
ListNode pre=null, post=head;
while (head!=null) {
post=head.next;
head.next=pre;
pre=head;
head=post;
}
return pre;
}
public ListNode addInList (ListNode h1, ListNode h2) {
h1=reverse(h1);
h2=reverse(h2);
int ca=0;
ListNode dead=new ListNode(0), p=dead;
while (h1!=null || h2!=null || ca!=0) {
int a=h1==null ? 0 : h1.val;
int b=h2==null ? 0 : h2.val;
ca+=a+b;
ListNode t=new ListNode(ca%10);
t.next=p.next; //头插法
p.next=t;
ca/=10;
if (h1!=null) h1=h1.next;
if (h2!=null) h2=h2.next;
}
return dead.next;
}
}
b. 删除链表中的重复元素
给出一个升序排序的链表,删除链表中的所有重复出现的元素,只保留原链表中只出现一次的元素。
给出的链表为1→1→1→2→3, 返回2→3
思路:双指针
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head==null || head.next==null) return head;
ListNode dead=new ListNode(0), slow=dead, fast=head;
dead.next=head;
while (fast!=null && fast.next!=null) {
if (fast.val!=fast.next.val) { //证明fast.val是需要保留的
slow=fast;
} else {
while (fast.next!=null && fast.next.val==fast.val) //一直遍历到重复元素子链表的最后一个元素
fast=fast.next;
slow.next=fast.next; //抹掉重复的全部元素
}
fast=fast.next;
}
return dead.next;
}
}
a. 排序链表
按 升序 排列并返回 排序后的链表
思路:归并排序
class Solution:
def sortList(self, head: ListNode) -> ListNode:
def merge(l,r):
dead=ListNode(-1)
p=dead
while l and r:
if l.val<r.val: p.next, l = l, l.next
else: p.next, r = r, r.next
p=p.next
if l: p.next=l
else: p.next=r
return dead.next
def merge_sort(head):
if not head or not head.next:
return head
# print(head.val)
#将链表分成两半
slow,fast=head,head.next.next
while fast and fast.next:
slow=slow.next
fast=fast.next.next
r=merge_sort(slow.next)
slow.next=None
l=merge_sort(head)
#双指针合并
return merge(l,r)
return merge_sort(head)
快排:代办
b. 分隔链表
给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。
你应当保留两个分区中每个节点的初始相对位置。
思路:双指针,注意链表的循环引用问题,b.next可能不为空,b.next可能就是a的结尾,从而导致这种情况:a->b->a
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
dead1,dead2=ListNode(0),ListNode(0)
a,b=dead1,dead2
while head:
if head.val<x:
a.next=head
a=a.next
else:
b.next=head
b=b.next
head=head.next
a.next=dead2.next
b.next=None
return dead1.next
b. 排序链表
归并:将链表不断切分成两部分(快慢指针实现,注意还要断开),直到剩下两个结点,然后 merge,返回上层
func merge(l, r *ListNode) *ListNode {
dead := new(ListNode)
cur := dead
for (l != nil && r != nil) {
if (l.Val < r.Val) {
cur.Next, l = l, l.Next
} else {
cur.Next, r = r, r.Next
}
cur = cur.Next
}
if (l != nil) { cur.Next = l }
if (r != nil) { cur.Next = r }
return dead.Next
}
func merge_sort(head *ListNode) *ListNode {
if (head == nil || head.Next == nil) {
return head
}
slow, fast := head, head.Next.Next
for (fast != nil && fast.Next != nil) {
slow, fast = slow.Next, fast.Next.Next
}
r := merge_sort(slow.Next)
slow.Next = nil
l := merge_sort(head)
return merge(l, r)
}
func sortList(head *ListNode) *ListNode {
return merge_sort(head)
}