其他分享
首页 > 其他分享> > 面试常见链表题总结(如果失眠了就拿出来看看)

面试常见链表题总结(如果失眠了就拿出来看看)

作者:互联网

无头单链表  现场手撕代码+分析,供自己闲时手机复习使用!

/*
        力扣和牛客的部分链表练习题
 */


class ListNode {
    public int data = 0;
    public ListNode next = null;

    public ListNode(int data) {
        this.data = data;
    }
}






public class List001 {

    /*
       1.删除链表中等于给定值为val的所有结点
    */
    public ListNode removeElements(ListNode head, int val) {

        //空链表
        if (head == null) {
            return null;
        }

        //一般情况非头结点
        ListNode prev = head;
        ListNode node = head.next;
        while (node != null) {
            if (node.data == val) {
                prev.next = node.next;
                node = prev.next;
            } else {

                //找完链表都没找到val
                prev = node;
                node = node.next;
            }
        }

        //删除的正好是头结点
        if (head.data == val) {
            head = head.next;
        }
        return head;
    }







    /*
          2.反转一个单链表
     */
    public ListNode reverse(ListNode head) {

        //空链表
        if (head == null) {
            return null;
        }

        //对于只有一个元素情况
        if (head.next == null) {
            return head;
        }

        //如果有多个结点
        ListNode newHead = null;
        ListNode cur = head;
        ListNode prev = null;


        while (cur != null) {
            ListNode next = cur.next;
            if (next == null) {

                //此时cur已经指向最后一个结点了
                newHead = cur;
            }

            //翻转cur引用的指向
            cur.next = prev;

            //维护prev和cur
            prev = cur;
            cur = next;
        }
        return newHead;
    }









    /*
         3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点
           如果有两个中间结点,那么返回第二个中间结点
     */
    public ListNode middleNode(ListNode head) {
        ListNode cur = head;
        int steps = size(head) / 2;
        for (int i = 0; i < steps; i++) {
            cur = cur.next;
        }
        return cur;
    }









    /*
           4.输入一个链表,输出该链表中倒数第k个结点
     */
    public ListNode FindKthToTail(ListNode head, int k) {
        int len = size(head);

        //非法情况
        if (head == null || k <= 0 || k > len) {
            return null;
        }

        //倒数第K个,也就是正数第len-k个
        int offset = len - k;
        ListNode cur = head;
        for (int i = 0; i < offset; i++) {
            cur = cur.next;
        }
        return cur;
    }








    /*
         5.将两个有序链表合并为一个新的有序链表并返回
           新链表是通过拼接给定的两个链表的所有结点组成的
     */
    public ListNode mergeTwoLists(ListNode L1,ListNode L2){

        //合并链表的话,如果一个为空,则只剩下另一个链表
        if(L1 == null){
            return L2;
        }

        if(L2 == null){
            return L1;
        }

        //一般情况
        ListNode cur1 = L1;
        ListNode cur2 = L2;
        ListNode newHead = null;
        ListNode newTail = null;
        while (cur1 != null && cur2 != null){
            if(cur1.data < cur2.data){

                //先把cur1指向的结点插在新链表的尾端
                //如果新链表为空,直接插
                if(newHead == null){
                    newHead = cur1;
                    newTail = cur1;
                    cur1 = cur1.next;
                }else{

                    //不是空链表就续在后面
                    newTail.next = cur1;

                    //更新尾部指向
                    newTail = newTail.next;
                    cur1 = cur1.next;
                }
            }else{

                //如果cur2比cur1小,则把cur2插在尾端
                if(newHead == null){
                    newHead = cur2;
                    newTail = cur2;
                    cur2 = cur2.next;
                }else{
                    newTail.next = cur2;
                    newTail = newTail.next;
                    cur2 = cur2.next;
                }
            }
        }

        if(cur1 == null){
            //L1元素已经完了,L2有剩余,直接放进去就好了
            newTail.next = cur2;
        }else{
            //L2元素已经完了,L1有剩余,直接放进去就好了
            newTail.next = cur1;
        }
        return newHead;
    }










    /*
         6.编写代码,以给定值x为基准将链表分割成两部分,
           所有小于x的结点排在大于或等于x的结点之前
     */
    public ListNode partition(ListNode pHead,int x){

        //空链表
        if(pHead == null){
            return null;
        }

        //只有一个元素
        if(pHead.next == null){
            return pHead;
        }

        //定义一个只含有傀儡节点的链表放小于x的结点
        ListNode smallHead = new ListNode(-1);
        ListNode smallTail = smallHead;

        //定义一个只含有傀儡节点的链表放大于x的结点
        ListNode bigHead = new ListNode(-1);
        ListNode bigTail = bigHead;

        //遍历原来的链表
        for(ListNode cur = pHead; cur != null; cur = cur.next){

            //比较cur和x的大小
            if(cur.data < x){

                //小于x的话,插在smallTail之后
                smallTail.next = new ListNode(cur.data);

                //维护一下smallTail
                smallTail = smallTail.next;
            }else{

                //大于x的话,插在bigTail之后
                bigTail.next = new ListNode(cur.data);

                //维护一下bigTail
                bigTail = bigTail.next;
            }
        }

        //小的结点在前一个链表,大的结点在后一个链表
        smallTail.next = bigHead.next;  //小链表尾部接大链表头部
        return smallHead.next;
    }










    /*
         7.在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,
           重复的结点不保留,返回链表头指针
     */
    public ListNode deleteDuplication(ListNode pHead){

        //创建一个带有傀儡结点的新链表,用来放不重复的结点
        ListNode newHead = new ListNode(-1);
        ListNode newTail = newHead;

        ListNode cur = pHead;
        while(cur != null){

            //cur和cur.next都不为null的时候,如果cur的值等于cur.next的值
            //那么此时cur就是一个重复结点
            if(cur.next !=null && cur.data == cur.next.data){

                //此时让cur继续往后走就是为了跳过相同的结点
                while (cur.next !=null && cur.data == cur.next.data){
                    cur = cur.next;
                }

                //循环结束之后,cur指向的是重复元素的最后一个
                //再多走一次,就跳过了所有重复的元素
                cur = cur.next;
            }else{

                //这个结点不是重复的,那么就插在新链表就可以了
                newTail.next = new ListNode(cur.data);
                newTail = newTail.next;
                cur = cur.next;
            }
        }
        return newHead.next;  // 跳过那个傀儡节点,返回链表
    }










    /*
          8.链表的回文结构
     */
    public boolean chkPalindrome(ListNode A){

        //假设空链表也是回文结构
        if(A == null){
            return true;
        }

        //一个结点一定是回文
        if(A.next == null){
            return true;
        }

        //找中间结点
        int len = size(A)/2;
        ListNode B = A;
        for (int i = 0; i < len; i++) {
            B = B.next;
        }

        //反转原来链表中间结点之后的部分
        ListNode prev = null;
        ListNode cur = B;
        while (cur != null){
            ListNode next = cur.next;
            if(next == null){

                //把B指向新的链表头部
                B = cur;
            }
            cur.next = prev;

            //维护prev和cur
            prev = cur;
            cur = next;
        }

        //比较两部分是否相同,如果相同那么就是回文
        while (B != null){
            if(A.data != B.data){
                return false;
            }

            //不断往后比较直到结束
            A = A.next;
            B = B.next;
        }
        return true;
    }












    /*
          9.输入两个链表,找出它们的第一个公共结点
     */
    public ListNode getIntersectionNode(ListNode headA,ListNode headB){

        //分别求两个链表的长度
        int lenA = size(headA);
        int lenB = size(headB);

        //让相对比较长的链表先走差值步数,然后再一起走
        if(lenA > lenB){
            int offset = lenA - lenB;
            for (int i = 0; i < offset; i++) {
                headA = headA.next;
            }
        }else{
            int offset = lenB - lenA;
            for (int i = 0; i < offset; i++) {
                headB = headB.next;
            }
        }

        //分别让两个链表的结点同时移动,比较是否是相同结点
        while (headA != null && headB != null){

            //比较地址即可
            if(headA.equals(headB)){
                return  headA;
            }
            headA = headA.next;
            headB = headB.next;
        }
        return null;
    }













    /*
            10.给定一个链表,判断链表中是否有环。
     */
    public boolean hasCycle(ListNode head) {
        if (head == null) {
            return false;
        }
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }













    /*
          11.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
     */
    public ListNode detectCycle(ListNode head) {
        // 从链表头部出发, 到入口点的距离,
        // 和从快慢指针的交汇处出发, 到入口点的距离相等
        if (head == null) {
            return null;
        }
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }
        if (fast == null || fast.next == null) {
            // 不带环
            return null;
        }
        // 循环结束之后, fast 和 slow 就已经重合了
        ListNode cur1 = head;
        ListNode cur2 = fast;
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }











    /*
           12.获取链表的长度
     */
    public int size(ListNode head) {
        int size = 0;
        for (ListNode cur = head; cur != null; cur = cur.next) {
            size++;
        }
        return size;
    }
}

 

标签:head,ListNode,cur,失眠,next,链表,面试,null
来源: https://blog.csdn.net/WalterBrain/article/details/102750210