0
点赞
收藏
分享

微信扫一扫

【数据结构的魅力】005.链表问题专项

链表问题专项

1.快慢指针

package class03;

public class LinkedListMid {

    public static class Node{
        public int value;
        public Node next;

        public Node(int v){
            value = v;
        }
    }

    public static Node midOrUpMidNode(Node head){
        if(head==null||head.next==null||head.next.next==null){
            return head;
        }
        Node slow = head.next;
        Node fast = head.next.next;
        while (fast.next!=null&&fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public static Node midOrDownMidNode(Node head){
        if(head==null||head.next==null){
            return head;
        }
        Node slow = head.next;
        Node fast = head.next;
        while (fast.next!=null&&fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public static Node minOrUpMidPreNode(Node head){
        if(head==null||head.next==null||head.next.next==null){
            return null;
        }
        Node slow = head;
        Node fast = head.next.next;
        while (fast.next!=null&&fast.next.next!=null){
            slow=slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public static Node midOrDownMidPreNode(Node head){
        if(head==null||head.next==null){
            return null;
        }
        if(head.next.next==null){
            return head;
        }
        Node slow = head;
        Node fast = head.next;
        while (fast.next!=null&&fast.next.next!=null){
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}

2.回文结构

1)容器实现

实现思路

  1. 将链表中的元素全部入栈
  2. 再将每一个元素依次出栈与链表每一节点的元素依次进行比较
  3. 如果有一个不一样就直接返回false,全部一样则返回true

代码实现

//回文结构判断
    public static boolean idPalindrome1(Node head) {
        Stack<Node> stack = new Stack<>();
        Node cur = head;
        while (cur != null) {
            stack.push(cur);
            cur = cur.next;
        }
        while (head != null) {
            if (head.value != stack.pop().value) {
                return false;
            }
            head = head.next;
        }
        return true;
    }

2)仅使用一半的容器

实现思路

  1. 使用快慢指针,当快指针指向链表结尾最后一个元素的时候,慢指针指向链表中间
  2. 将链表中间节点的下一个元素直到结尾都存入栈中
  3. 依次出栈与链表节点一一比对
  4. 如果有一个不一样就直接返回false ; 如果全部一样直到慢指针指向的节点,则返回true

3)完全不使用容器

实现思路

  1. 使用快慢指针,当快指针指向链表结尾最后一个元素的时候,慢指针指向链表中间
  2. 将中间节点之后的元素逆序,如下:
  3. 从链表头部和尾部开始同时比对
  4. 如果有一个不一样就直接返回false ; 如果全部一样直到某个指针走到null,则返回true

代码实现

//不使用容器
    public static boolean idPalindrome2(Node head) {
        if(head==null||head.next==null){
            return true;
        }

        Node n1 = head;
        Node n2 = head;
        while (n2.next!=null&&n2.next.next!=null){
            n1 = n1.next;
            n2 = n2.next.next;
        }
        n2 = n1.next;
        n1.next = null;
        Node n3 = null;
        while(n2!=null){
            n3 = n2.next;
            n2.next = n1;
            n1 = n2;
            n2 = n3;
        }
        n3 = n1;
        n2=head;
        boolean res = true;
        while (n1!=null&&n2!=null){
            if(n1.value!=n2.value){
                res = false;
                break;
            }
            n1 = n1.next;
            n2 = n2.next;
        }
        n1 = n3.next;
        n3.next=null;
        while (n1!=null){
            n2 = n1.next;
            n1.next = n3;
            n3=n1;
            n1=n2;
        }
        return res;
    }

3.链表划分

1)做partition

实现思路

  • 荷兰国旗问题

代码实现

2)分区

实现思路

  1. 根据输入的pivot值划分区域,每个区域分别都有头结点和尾节点
  2. 小于区域的尾巴连等于区域的头,等于区域的尾巴连大于区域的头(注意判断小于区域是否存在 , 等于区域是否存在)

代码实现

public static Node listPartition(Node head, int pivot) {
        Node sH = null; //small head
        Node sT = null; //small tail
        Node eH = null; //equal head
        Node eT = null; //equal tail
        Node mH = null; //big head
        Node mT = null; //big tail

        Node next = null;

        while (head != null) {
            next = head.next;
            head.next = null;
            if (head.value < pivot) {
                if (sH == null) {
                    sH = head;
                    sT = head;
                } else {
                    sT.next = head;
                    sT = head;
                }
            } else if (head.value == pivot) {
                if (eH == null) {
                    eH = head;
                    eT = head;
                } else {
                    eT.next = head;
                    eT = head;
                }
            } else {
                if (mH == null) {
                    mH = head;
                    mT = head;
                } else {
                    mT.next = head;
                    mT = head;
                }
            }
            head = next;
        }
        //小于区域的尾巴连等于区域的头,等于区域的尾巴连大于区域的头
        //另外考虑是否有小于区域的问题
        if (sT != null) {
            sT.next = eH;
            eT = eT == null ? sT : eT;
        }
        if (eT != null) {
            eT.next = mH;
        }
        return sH != null ? sH : (eH != null ? eH : mH);
    }

4.特殊链表

public static class Node {
        public int value;
        public Node next;
        public Node rand;

        public Node(int data) {
            this.value = data;
        }
    }

    public static Node copyListWithRand(Node head) {
        HashMap<Node, Node> map = new HashMap<>();
        Node cur = head;
        while (cur != null) {
            map.put(cur, new Node(cur.value));
            cur = cur.next;
        }
        cur = head;
        while (cur != null) {
            //cur:老
            //map.get(cur):新
            map.get(cur).next = map.get(cur.next);
            map.get(cur).rand = map.get(cur.rand);
            cur = cur.next;
        }
        return map.get(head);
    }

5.可能有环的链表相交问题

注意 : 单链表只有一个next指针

实现思路

获取入环节点

  1. 使用快慢指针,fast一次走2步,slow一次走一步,如果连表有环,那么fast和slow一定会在链表某一处相遇
  2. 若有环,此时将fast指针重置到链表开头,变为一次走一步,同时slow也一次走一步,当fast与slow相遇的是时候,即为入环节点
public static Node getLoopNode(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        Node n1 = head.next;    //n1 -> slow
        Node n2 = head.next.next;   //n2 -> fast
        while (n1 != n2) {
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        n2 = head;  //n2重新回到头结点
        while (n1 != n2) {
            n1 = n1.next;
            n1 = n2.next;
        }
        return n1;
    }

如果两个连表都无环,返回第一个相交节点,如果不想交则返回null

//如果两个连表都无环,返回第一个相交节点,如果不想交则返回null
    public static Node noLoop(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node cur1 = head1;
        Node cur2 = head2;
        int n = 0;
        while (cur1.next != null) {
            n++;
            cur1 = cur1.next;
        }
        while (cur2.next != null) {
            n--;
            cur2 = cur2.next;
        }
        if (cur1 != cur2) {
            return null;
        }

        //重新定向
        //谁长,谁把头给cur1,谁短,谁把头给cue2
        cur1 = n > 0 ? head1 : head2;
        cur2 = cur1 == head1 ? head2 : head1;

        //n取绝对值,长链表先走n个节点,然后两个链表同时走
        n = Math.abs(n);
        while (n != 0) {
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) {
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

如果两个链表都有环,寻找相交节点

public static Node bothLoop(Node head1, Node loop1, Node head2, Node loop2) {
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {
            cur1 = head1;
            cur2 = head2;
            int n = 0;
            while (cur1 != loop1) {
                n++;
                cur2 = cur2.next;
            }
            cur1 = n > 0 ? head1 : head2;
            cur2 = cur1 == head1 ? head2 : head1;
            n = Math.abs(n);
            while (n != 0) {
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
            cur1 = loop1.next;
        }
        while (cur1 != loop1) {
            if (cur1 == loop2) {
                return loop1;
            }
            cur1 = cur1.next;
        }
        return null;
    }

主方法

    //main
    public static Node getIntersectNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }
        Node loop1 = getLoopNode(head1);
        Node loop2 = getLoopNode(head2);
        if (loop1 == null || loop2 == null) {
            return noLoop(head1, head2);
        }
        if (loop1 != null && loop2 != null) {
            return bothLoop(head1, loop1, head2, loop2);
        }
        return null;
    }

6.无头结点删除

表面删除

思路

  • 将要删除节点的下一个节点的数据覆盖到要删除的结点上,然后删除那个节点

带来的问题:无法删除最后一个节点

其实是无法真正的删掉的,无论什么方法,一定会存在很多问题.因此在面试的过程中,若果问到了这个问题,告诉面试官一定要给出头结点,并给出及格看似可能实则有问题的做法并作出解释.

举报

相关推荐

0 条评论