常见面试算法题之链表专题

145 阅读2分钟

反转链表

1. 反转单链表

leetcode-cn.com/problems/re…

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null) return head;
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode help = cur.next;
            // pre cur help
            cur.next = pre;
            pre = cur;
            cur = help;
        }
        return pre;
    }
}

2. k个一组反转单链表

leetcode-cn.com/problems/re…

class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null) return head;
        ListNode cur = head;
        for(int i = 0 ; i < k ; i++){
            if(cur == null) return head;
            cur = cur.next;
        }

        ListNode newHead = reverse(head , cur);
        head.next = reverseKGroup(cur , k);
        return newHead;
    }

    ListNode reverse(ListNode head , ListNode end){
        ListNode cur = head;
        ListNode pre = null;
        while(cur != end){
            ListNode help = cur.next;
            //pre cur help
            cur.next = pre;
            pre = cur;
            cur = help;
        }
        return pre;
    }
}

3. 反转left到right的单链表

leetcode-cn.com/problems/re…


class Solution {
    public ListNode reverseBetween(ListNode head, int m, int n) {
        //1. 建立虚拟头结点
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        //2. 找到第m-1个结点,也就是开始翻转的结点,用cur保存
        ListNode cur = dummy;
        for(int i = 1; i < m ; i++) cur = cur.next; //(注意这里是从dummy结点开始的)

        //3. a 就是第m个结点
        ListNode a = cur.next;
        ListNode tail = cur.next; //用tail来保存这个结点,翻转之后是尾结点,注意和之前的链表连接起来

        //4. 翻转单链表的操作
        ListNode pre = null;
        ListNode help = null;
        for(int i = m ; i <= n ;i++){
            help = a.next;
            a.next = pre;
            pre = a;
            a = help;
        }
        
        //5. 翻转完之后pre 是头结点,tail 是尾结点,help是尾结点的下一个结点,
        cur.next = pre;
        tail.next = help;
        return dummy.next;
    }
}

4. 两两交换链表的节点

leetcode-cn.com/problems/sw…

class Solution {
    public ListNode swapPairs(ListNode head) {
        //1. 数据校验
        if(head == null || head.next == null) return head;
        //2. 定义一个虚拟头结点,使其next指针指向head
        ListNode dummy = new ListNode(0);
        dummy.next = head;

        //3. 开始翻转
        ListNode cur = dummy;
        //因为要保证后面还有2个结点可以交换,所以后面两个结点不为空
        while(cur.next != null && cur.next.next != null){
            //定义两个指针保存位置
            ListNode a = cur.next;
            ListNode b = cur.next.next;
            //开始交换
            // cur a b
            ListNode c = b.next;
           
            cur.next = b;
            b.next = a;
            a.next = c;
            cur = a;
        }
        return dummy.next;
       

    }
}

5. 判断是不是回文链表

leetcode-cn.com/problems/pa…

class Solution {
    public boolean isPalindrome(ListNode head) {
        if(head == null || head.next == null) return true;
        //快慢指针找中点
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }

        //slow 为中点
        ListNode a = slow.next;
        slow.next = null;

        ListNode newHead = reverse(a);
        ListNode cur = head;
        while(cur != null && newHead != null){
            if(cur.val != newHead.val) return false;
            cur =cur.next;
            newHead = newHead.next;
        }
        return true;

    }


    ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode help = cur.next;
            cur.next = pre;
            pre = cur;
            cur = help;
        }
        return pre;
    }
}

6. 重排链表

leetcode-cn.com/problems/re…

class Solution {
    public void reorderList(ListNode head) {
        if(head == null) return;
        ListNode fast = head , slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        ListNode a = reverse(slow.next);
        slow.next = null;
        ListNode b = head;
        while(a != null && b != null){
            ListNode c = a.next;
            ListNode d = b.next;
            //a c
            //b d
            b.next = a;
            a.next = d;
    
            a = c;
            b = d;
        }
    }

    ListNode reverse(ListNode head){
        ListNode pre = null;
        ListNode cur = head;
        while(cur != null){
            ListNode help = cur.next;
            cur.next = pre;
            pre = cur;
            cur = help;
        }
        return pre;
    }
}

7. 奇偶链表

leetcode-cn.com/problems/od…

class Solution {
    public ListNode oddEvenList(ListNode head) {

        if(head == null || head.next == null) return head;

        ListNode oddHead = head;
        ListNode evenHead = head.next;

        ListNode a = head;
        ListNode b = head.next;

        while(b != null && b.next != null){
            ListNode c = b.next;
            ListNode d = c.next;
            //a b c d
            a.next = c;
            b.next = d;
            a = a.next;
            b = b.next;
        }

        a.next = evenHead;
        return oddHead;



    }
}

8. 两个链表的第一个公共节点

leetcode-cn.com/problems/li…

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null; 
        ListNode l1 = headA , l2 = headB;
        while(l1 != l2){
            l1 = l1 == null ? headB : l1.next;
            l2 = l2 == null ? headA : l2.next;
        }
        return l1;
    }
}

9. 旋转链表

leetcode-cn.com/problems/ro…

class Solution {
    public ListNode rotateRight(ListNode head, int k) {
        if(head == null || k == 0) return head;
        //1. 求出链表长度
        int len = 1;
        ListNode cur = head;
        while(cur.next != null){
            cur = cur.next;
            len++;
        }

        //2. 首尾相连,并找出x
        cur.next = head;
        k = k % len;
        int x = len - k;
        //3. 找到第x个节点,后面那个就是要返回的头结点
        while(x > 0){
            cur = cur.next;
            x--;
        }
        ListNode res = cur.next;
        cur.next = null;
        return res;
    }
}

合并排序链表

5. 合并两个排序单链表

leetcode-cn.com/problems/me…

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //1. 数据校验
        if(l1 == null || l2 == null) return  l1 == null ? l2 : l1;
        //2. 建立虚拟头结点
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        //3. 两个指针分别向后移动,那个结点小就接到cur的后面
        while(l1 != null && l2 != null){
            if(l1.val < l2.val){
                cur.next = new ListNode(l1.val);
                cur = cur.next; //cur 也要向后移动一个
                l1 = l1.next;
            }else{
                cur.next = new ListNode(l2.val);
                cur = cur.next;
                l2 = l2.next;
            }
        }
        //4.剩下的链表直接接上去即可,因为是已经排好序的链表了
        cur.next = l1 == null ? l2 : l1;
        return dummy.next;

    }
}

6. 合并k个排序单链表

leetcode-cn.com/problems/me…

class Solution {
    public ListNode mergeKLists(ListNode[] lists) {
        //1. 建立小根堆
        Queue<ListNode> heap = new PriorityQueue<>((a , b) -> (a.val - b.val));
        //2. 将所有头结点放入堆中,这样堆顶就是最小的结点
        for(ListNode node : lists){
            if(node != null) heap.add(node);
        }
        //3. 建立虚拟结点
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        //4. 一直弹出堆顶的结点,放入cur的后面,并将弹出的结点的下一个结点再压入堆中
        while(!heap.isEmpty()){
            ListNode minNode = heap.poll();
            cur.next = minNode;
            cur = cur.next;
            if(minNode.next != null) heap.add(minNode.next);
        }
        return dummy.next;
    }
}

6. 分割链表

leetcode-cn.com/problems/pa…

class Solution {
    public ListNode partition(ListNode head, int x) {
        if(head == null) return head;
        ListNode dummy1 = new ListNode(-1);
        ListNode a = dummy1;
        ListNode dummy2 = new ListNode(-1);
        ListNode b = dummy2;
        ListNode cur = head;
        while(cur != null){
            if(cur.val < x){
                a.next = cur;
                cur = cur.next;
                a = a.next;
            }else{
                b.next = cur;
                cur = cur.next;
                b = b.next;
            }
        }

        a.next = dummy2.next;
        b.next = null;
        return dummy1.next;
    }
}

快慢指针找倒数第n个节点

1.删除链表的倒数第 n 个结点

leetcode-cn.com/problems/re…

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode fast = dummy;
        ListNode slow = dummy;
        while(n > 0){
            fast = fast.next;
            n--;
        }

        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }

        slow.next = slow.next.next;
        return dummy.next;
    }
}

2.找链表的中间节点-如果有两个中间结点,则返回第二个中间结点

leetcode-cn.com/problems/mi…


class Solution {
    //如果有两个中间结点,则返回第二个中间结点。
    public ListNode middleNode(ListNode head) {
        if(head == null) return head;
        ListNode fast = head;
        ListNode slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

链表排序

1. 链表归并排序

leetcode-cn.com/problems/so…

class Solution {
    public ListNode sortList(ListNode head) {
        if(head == null) return head;
        return mergeSort(head);
    }

    ListNode mergeSort(ListNode head){
        //1. 递归终止条件
        if(head == null || head.next == null) return head;
        //2. 快慢指针找中点
        ListNode fast = head;
        ListNode slow = head;
        while(fast.next != null && fast.next.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //3. 递归排序右边
        ListNode r = mergeSort(slow.next);
        slow.next = null;
        //4. 递归排序左边
        ListNode l = mergeSort(head);
        //5. 合并两个链表
        return merge(l,r); 
    }

    ListNode merge(ListNode l , ListNode r){
        ListNode dummy = new ListNode(-1);
        ListNode cur = dummy;
        while(l != null && r != null){
            if(l.val <= r.val){
                cur.next = l;
                l = l.next;
                cur = cur.next;
            }else{
                cur.next = r;
                r = r.next;
                cur = cur.next;
            }
        }

        cur.next = l == null ? r : l;
        return dummy.next;
    }

}

删除链表中的重复元素

1. 只删除一个重复元素

leetcode-cn.com/problems/re…

class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null) return head;
    
        ListNode cur = head;
        while(cur.next != null){
            if(cur.val == cur.next.val) cur.next = cur.next.next;
            else cur = cur.next;
        }
        return head;
    }
}

2. 删除全部的重复元素

leetcode-cn.com/problems/re…


class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode fast = head;
        ListNode slow = dummy;
        while(fast != null){
            if(fast.next != null && fast.val == fast.next.val){
                while(fast.next != null && fast.val == fast.next.val ) fast = fast.next;
                slow.next = fast.next;
                fast = fast.next;
            }else{
                fast = fast.next;
                slow = slow.next;
            }
        }
        return dummy.next;
    }
}