高频链表问题总结—面试题

0. 链表题目注意

清楚链表在内存中的存储方式
注意每个节点中指针的指向
写链表相关题目时首先在纸上画出相关操作,然后再写代码
注意对非法情况进行判断

  • 一些高频的链表问题
    • 翻转链表
    • K个节点为一组进行翻转
    • 判断链表是否有环
    • 返回链表中间(1/2)节点(扩展返回链表1/k 节点)
    • 求链表的交
    • 旋转链表

1. 翻转链表

不断通过头插法进行翻转链表的构建,需要注意其中的指针指向防止断链。

/**
     * 链表翻转非递归实现
     * @param head:头指针
     * @return
     */
    public static Node reverseList(Node head){
        if(head == null || head.next == null){
            return head;
        }
        Node cur = head.next;
        Node pre = head;
        pre.next = null;
        Node tmp ;
        while(cur != null){
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }

2. K个节点为一组进行翻转

经常有两个节点一组翻转,三个节点一组翻转,考虑到扩展性,可以直接实现K个节点一组进行翻转,如果在面试时候可以这样实现,肯定会得到面试官的青睐。
思路:采用递归实现,先找到前K个节点,然后对前K个节点进行翻转。

/**
     * K个节点一组进行翻转
     * @param head:头指针
     * @param k:需要进行翻转的组数
     * @return
     */
    public static Node reverseKList(Node head, int k){
        if(head == null || head .next == null){
            return head;
        }
        //不直接对head操作,这样可以保留一个头指针
        Node cur = head;
        // 记录节点个数
        int count = 0;
        // 找到需要进行翻转的K个节点
        while(cur != null && count != k){
            cur = cur.next;
            count ++;
        }
        // 进行特殊情况处理
        if(count == k){
            cur = reverseKList(cur, k);
            while(count -- > 0){
                Node tmp = head.next;
                head.next = cur; // 与后面翻转好的连接
                cur = head;
                head = tmp ;
            }
        }
        return head;
    }

3. 判断链表是否有环

思路:1使用快慢指针的方式,一个一次走一步,一个一次走两步,如果相遇则说明有环;2使用set或者map进行记录,如果重复出现则说明有环;

/**
     * 使用快慢指针判断链表是否有环
     * @param head:头指针
     * @return
     */
    public static boolean isCircleByFastSlow(Node head){
        if(head == null || head.next == null){
            return false;
        }
        Node fast = head;
        Node slow = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }

 /**
     * 使用set判断是否含有环
     * @param head:头指针
     * @return
     */
    public static boolean isCircleBySet(Node head){
        if(head == null || head.next == null){
            return false;
        }
        Set<Node> set = new HashSet<>();
        Node cur = head;
        while(cur != null){
            // 如果set中不包含当前节点则加入
            // 否则直接返回true
            if(!set.contains(cur)){
                set.add(cur);
                cur = cur.next;
            }else{
                return true;
            }
        }
        return false;
    }

4. 返回链表的中间(1/2)节点(扩展返回链表1/k 节点)

这个题目一个可以扩展的题目,例如返回中间节点,返回1/3节点等,可以实现返回1/k节点的方法。
思路:两个指针,一个一次走一步,一个一次走K步;

/**
     * 当前节点走K步之后是否为空
     * @param head:头结点
     * @param k:K处
     * @return
     */
    public static boolean isNull(Node head, int k){
        if(head == null){
            return true;
        }
        Node cur = head;
        while(k -- > 0){
            if(cur.next != null){
                cur = cur.next;
            }else{
                return true;
            }
        }
        return false;
    }
    /**
     * 返回1/k处的节点,例如中间节点为1/2处节点
     * @param head:头结点
     * @param k:K处
     * @return
     */
    public static Node getNodeOfK(Node head, int k){
        if(head == null){
            return null;
        }
        Node fast = head;
        Node slow = head;
        while(!isNull(fast, k)){
            int count = k;
            while(count -- > 0){
                fast = fast.next;
            }
            slow = slow.next;
        }
        return slow;
    }

5.求链表的交

相交链表特点:从后往前总是相同的;
注意:在进行链表相交问题判断的时候要先判断两个链表是否有环,有环时处理方式稍有差别,下面的方法不考虑环。
思路:
(1)两边扫描,因为两个链表的长度可能不同,扫描两边可以保证两个指针扫描的长度相同,扫描过程中如果两个指针相遇则相交;这个方法需要提前判断两个链表有交,判断链表有交有多种方法:例如:判断最后一个节点是否相同;将一个链表的首位相连从另外一个链表的头开始扫描;
(2)使用栈进行存储,然后再不断的出栈;
(3)先对一个链表使用set进行存储,然后再遍历另外一个链表;

/**
     * 两边扫描求链表的交,此处保证链表有交
     * 可以提前判断链表是否有交,判断链表有交的方法很多
     * @param head1:头指针
     * @param head2:头指针
     * @return
     */
    public static Node mixedListByFastSlow(Node head1, Node head2){
        if(head1 == null || head2 == null){
            return null;
        }
        Node p1 = head1;
        Node p2 = head2;
        while(p1 != p2){
            p1 = p1 == null ? head2 : p1.next;
            p2 = p2 == null ? head1 : p2.next;
        }
        return p1;
    }

    /**
     * 使用栈求交
     * @param head1
     * @param head2
     * @return
     */
    public static Node mixedListByStack(Node head1, Node head2){
        if(head1 == null || head2 == null){
            return null;
        }
        Stack<Node> stack1 = new Stack<>();
        Stack<Node> stack2 = new Stack<>();
        Node result = new Node(0);
        while(head1 != null){
            stack1.push(head1);
            head1 = head1.next;
        }
        while(head2 != null){
            stack2.push(head2);
            head2 = head2.next;
        }
        if(stack1.peek() != stack2.peek()){
            return null;
        }else{
            while(stack1.peek() == stack2.peek()){
                result = stack1.pop();
                stack2.pop();
            }
        }
        return result;
    }

    /**
     * 使用set集合判断链表的交
     * @param head1
     * @param head2
     * @return
     */
    public static Node mixedListBySet(Node head1, Node head2){
        if(head1 == null || head2 == null){
            return null;
        }
        Set<Node> set = new HashSet<>();
        while(head1 != null){
            set.add(head1);
            head1 = head1.next;
        }
        while(head2 != null){
            if(set.contains(head2)){
                return head2;
            }else{
                head2 = head2.next;
            }
        }
        return null;
    }

    /**
     * 将链表向右移动K个位置
     * @param head:头结点
     * @param k:位置
     * @return
     */
    public static Node rotateList(Node head, int k){
        if(head == null){
            return null;
        }
        Node cur = head;
        int count = 0;
        while(cur != null){
            count ++;
            cur = cur.next;
        }
        k %= count;
        // 指向后面部分
        Node fast = head;
        while(k -- > 0){
            fast = fast.next;
        }
        // 指向前面部分
        Node slow = head;
        // 这个地方要想明白,两个指针同时向前走
        // 最终可以走到分界点
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        Node newHead = slow.next;
        fast.next = head;
        slow.next = null;
        return newHead;
    }

6.旋转链表

问题:将链表向右移动K个位置,例如 1 2 3 4 5 在K = 2时旋转为 4 5 1 2 3。
思路:使用两个指针

 /**
     * 将链表向右移动K个位置
     * @param head:头结点
     * @param k:位置
     * @return
     */
    public static Node rotateList(Node head, int k){
        if(head == null){
            return null;
        }
        Node cur = head;
        int count = 0;
        while(cur != null){
            count ++;
            cur = cur.next;
        }
        k %= count;
        // 指向后面部分
        Node fast = head;
        while(k -- > 0){
            fast = fast.next;
        }
        // 指向前面部分
        Node slow = head;
        // 这个地方要想明白,两个指针同时向前走
        // 最终可以走到分界点
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        Node newHead = slow.next;
        fast.next = head;
        slow.next = null;
        return newHead;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值