算法链表相关题目(二)

题目零

判断一个链表是否为回文结构

给定一个链表的头节点head,请判断该链表是否为回文结构。例如:1 -> 2 -> 1,返回true。1 -> 2 -> 2 -> 1,返回true。15 -> 6 ->15,返回true。1 ->2->3,返回false。

进阶:如果链表长度为N,时间复杂度达到O(N),额外空间复杂度达到O(1)。

解法1:使用栈 解法2:使用快慢指针

image.png

如果是偶数个节点,走到中间两个的前一个,在进行两边比较,走到空指针停。

image.png

解法一代码

public static boolean isPalindromel(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;
    }
    //  n/2的辅助空间
    public static boolean isPalindromel2(Node head){
        if (head == null || head.next == null){
            return true;
        }
        Node right = head.next;
        Node cur = head;
        while (cur.next != null && cur.next.next != null){
            //慢指针走一步,快指针走两步
            right = right.next;
            cur = cur.next.next;
        }
        Stack<Node> stack = new Stack<>();
        while (right != null){
            stack.push(right);
            right = right.next;
        }
        while (!stack.isEmpty()){
            if (head.value != stack.pop().value){
                return false;
            }
            head = head.next;
        }
        return true;
    }

解法2代码

   //完全不使用额外空间
    public static boolean isPailndrome3(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;//让n1指向空
        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;

    }

题目十二 类似于荷兰国旗问题

image.png

image.png

/**
 * @author :LY
 * @date :Created in 2021/3/19 10:52
 * @modified By:
 */
public class 单向链表划分 {

    public static void main(String[] args) {
        ListNode listNode6 = new ListNode(1,null);
        ListNode listNode5 = new ListNode(2, listNode6);
        ListNode listNode4 = new ListNode(3, listNode5);
        ListNode listNode3 = new ListNode(5, listNode4);
        ListNode listNode2 = new ListNode(3, listNode3);
        ListNode listNode1 = new ListNode(2, listNode2);
        ListNode listNode = new ListNode(1, listNode1);
        listPartition1(listNode,3);
    }
    static class ListNode {
        private int val;
        private ListNode next;
        public ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
        @Override
        public String toString() {
            return "Node{" +
                    "value=" + val +
                    ", next=" + next +
                    '}';
        }
    }
    public static ListNode listPartition1(ListNode head, int pivot){

        if (head == null)
            return head;
        ListNode cur = head;
        int i = 0;
        //计算链表大小
        while (cur != null){
            i++;
            cur = cur.next;
        }
        //创建一个与链表大小相当的数组
        ListNode[] listNodeArr = new ListNode[i];
        i = 0;
        cur = head;
        for (i = 0; i != listNodeArr.length; i++) {
            listNodeArr[i]  = cur;
            cur = cur.next;
        }
        arrPartition(listNodeArr,pivot);
        for (i = 1; i != listNodeArr.length ; i++) {
            listNodeArr[i-1].next = listNodeArr[i];
        }
        listNodeArr[i-1].next = null;
        return listNodeArr[0];
    }

    private static void arrPartition(ListNode[] listNodeArr, int pivot) {
        int small = -1;
        int big = listNodeArr.length;
        int index = 0;
        while (index != big){
            if (listNodeArr[index].val < pivot){
                swap(listNodeArr,++small,index++);
            }else if (listNodeArr[index].val == pivot){
                index++;
            }else {
                swap(listNodeArr,--big,index);
            }
        }
    }
    public static void swap(ListNode[] arr, int i, int j){
        ListNode tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

题目十三

image.png

使用hash表 思路:使用hash表, 遍历链表,复制每个节点,原节点作为key,拷贝的节点作为value存储

存储完每个值以后,再遍历链表第二遍,通过key找到value,然后再通过key的next与rand指针找到对应Key,此时,将value的next与rand进行赋值 ,循环赋值完毕后返回第一个value

image.png

用hash表的代码

 public static Node copyListWithRand1(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){
            map.get(cur).next = map.get(cur.next);
            map.get(cur).rand = map.get(cur.rand);
            cur = cur.next;
        }
        return map.get(head);
    }

不使用hash表

思路:遍历链表,第一次将拷贝节点放在节点的next上,新节点next放原来的next,第二边遍历找rand,新节点的rand指向rand的next

image.png

    public static Node copyListWithRand2(Node head){
       if (head == null){
           return null;
       }
       Node cur = head;
       Node next = null;
       while (cur != null){
           next = cur.next;
           cur.next = new Node(cur.value);
           cur.next.next = next;
           cur = next;
       }
       cur = head;
       Node curCopy = null;
       while (cur != null){
           next = cur.next.next;
           curCopy = cur.next;
           curCopy.rand = cur.rand != null ? cur.rand.next :null;
           cur = next;
       }
       Node res = head.next;
       cur = head;
       //split
       while (cur != null){
            next = cur.next.next;
            curCopy = cur.next;
            cur.next = next;
            curCopy.next = next != null ? next.next : null;
            cur = next;
       }
        return res;
    }

题目十四

两个单链表相交的一系列问题

在本题中,单链表可能有环,也可能无环。给定两个单链表的头节点head1和head2,,这两个链表可能相交,也可能不相交。请实现一个函数,如果两个链表相交,请返回相交的第一个节点;如果不相交,返回null即可。要求:如果链表1的长度为N,链表2的长度为M,时间复杂度请达到O(N+M),额外空间复杂度请达到O(1).

三个问题的综合题

1.判断链表是有环还是无环。

思路:利用hashset存储,遍历链表,每一个都放到hash表中,当循环到hash中已存在的节点时,就证明此链表是有环的0。

不用hash表怎么做:准备两个指针,快慢指针。快指针一次走两步,慢指针一次走一步。

如果有环那么快慢指针一定会在某节点上相遇,相遇之后快指针回到头节点一次走一步,快慢指针将会在入环节点相遇,

2.如何找到两个无环链表的相交节点

思路:用map,与之前同理,略过

不用map:先遍历链表1,统计链表1的长度,以及链表1的最后一个节点。然后链表2也做同样操作,先判断两个最后一个节点的内存地址是否相等,不相等则不可能相交。

那么如何找到第一个相交的节点处呢,首先计算两个长度的差值,让更长的节点先走完差值,然后两个链表一起走,必定会走到第一个相交的节点

三种结构

image.png

public class 题目十四链表相交问题 {

     public static class Node{
        private int value;
        private Node next;
        public Node(int value, Node next) {
            this.value = value;
            this.next = next;
        }
        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    ", next=" + next +
                    '}';
        }
    }

    /**
     * 总处理方法
     * @param head1
     * @param head2
     * @return
     */
    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;
    }

    /**
     * 两个 有环链表处理
     * @param head1
     * @param loop1
     * @param head2
     * @param loop2
     * @return
     */
    private 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++;
                cur1 = cur1.next;
            }
            while (cur2 != loop2){
                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;
        }
     }

    /**
     * 两个无环链表相交处理
     * @param head1
     * @param head2
     * @return
     */
    private 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
         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;
    }

    /**
     * 获取链表是否有环,如果有环就返回第一个入环节点
     * @param head1
     * @return
     */
    private static Node getLoopNode(Node head1) {
         if (head1 == null || head1.next == null || head1.next.next == null){
             return null;
         }
         Node n1 = head1.next;//慢指针,一次走一步
         Node n2 = head1.next.next;//快指针 一次走两步
        //进行遍历,当快慢指针相遇时停止
         while (n1 != n2){
             if (n2.next == null || n2.next.next == null){
                 return null;
             }
             n2 = n2.next.next;
             n1 = n1.next;
         }
         n2 = head1;//快指针回到开头
        //快指针此时一次走一步,两者相遇时停止
         while (n1 != n2){
             n1 = n1.next;
             n2 = n2.next;
         }
         return n1;
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值