链表题汇总

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. 链表环的入口结点

https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/linked-list-cycle-ii-kuai-man-zhi-zhen-shuang-zhi-/
思路

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)
}
posted @ 2020-10-20 14:45  童年の波鞋  阅读(99)  评论(0编辑  收藏  举报