算法基础入门——单链表、双链表、判断回文、链表的荷兰国旗、随机指针节点链表的复制、比较器、单链表相交、获取环节点

package com.zuoshen.jichurumen.class04;

import java.util.*;

/**
 * @author ShiZhe
 * @create 2022-02-25 21:35
 */
public class code01 {

    /**
     * Node结构体
     * 单链表的节点结构
     */
    public static class Node {
        public int value;
        public Node next;
        public Node rand;
        // 构造函数
        public Node(int data) {
            this.value = data;
        }
    }

    /**
     * 双链表的节点结构
     */
    public static class DoubleNode {
        public int value;
        public DoubleNode last;
        public DoubleNode next;
        // 构造函数
        public DoubleNode(int data) {
            this.value = data;
        }
    }

    /**
     * 单链表反转
     * @param head
     * @return
     */
    public static Node reverseList(Node head) {
        Node pre = null;
        Node next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            pre =head;
            head = next;
        }
        return pre;
    }

    /**
     * 打印单链表
     * @param head
     */
    public static void printLinkedList(Node head) {
        System.out.println("Linked List: ");
        while (head != null) {
            System.out.print(head.value + " ");
            head = head.next;
        }
        System.out.println();
    }

    /**
     * 双链表反转
     * @param head
     * @return
     */
    public static DoubleNode reverseList(DoubleNode head) {
        DoubleNode pre = null;
        DoubleNode next =null;
        while (head != null) {
            next = head.next;
            head.last = next;
            head.next = pre;
            pre = head;
            head = next;
        }
        return pre;
    }

    /**
     * 打印双链表
     * @param head
     */
    public static void printDoubleLinkedList(DoubleNode head) {
        System.out.print("Double Linked List: ");
        DoubleNode end = null;
        while (head != null) {
            System.out.print(head.value + " ");
            end = head;
            head = head.next;
        }
        System.out.print("| ");
        while (end != null) {
            System.out.print(end.value + " ");
            end = end.last;
        }
        System.out.println();
    }

    /**
     * 打印两个有序链表的公共部分
     * @param head1
     * @param head2
     */
    public static void printCommonPart(Node head1, Node head2) {
        System.out.print("Common Part: ");
        while (head1 != null && head2 != null) {
            if (head1.value < head2.value) {
                head1 = head1.next;
            } else if (head1.value > head2.value) {
                head2 = head2.next;
            } else {
                System.out.print(head1.value + " ");
                head1 = head1.next;
                head2 = head2.next;
            }
        }
        System.out.println();
    }

    /**
     * 判断一个链表是否为回文结构
     * need n extra space:新建一个栈,链表全部放入栈中,然后一一弹出与head相比,head++。
     * need n/2 extra space:新建一个栈,利用2个指针,一个next,一个next.next,
     * 使某一指针指向空时,一指针指向回文的右边,将右边放入栈中,一一弹出与head相比较,head++。
     * need O(1) extra space:反转右边的单链表。利用2个指针一一对比。
     * @param head
     * @return
     */
    public static boolean isPalindrome(Node head) {
        if (head == null || head.next == null) {
            return false;
        }
        // 定义指向右半部分的辅助节点
        Node right = head;
        // 定义2倍速跳转的辅助节点
        Node next = head;
        // next指针更快,只用判断next指针不越界
        while (next.next != null && next.next.next != null) {
            // 一倍速,right到中间
            right = right.next;
            // 二倍速,next到末尾
            next = next.next.next;
        }
        // 右半部分的开头
        next = right.next;
        // 反转右半部分链表的辅助节点
        Node pre = null;
        // 从中间断开
        right.next = null;
        // 反转右边单链表
        while (next != null) {
            pre = next;
            next = next.next;
            pre.next = right;
            right = pre;
        }
        // 将左边的头赋值给next
        next = head;
        // 返回结果
        boolean res = true;
        // 检查回文
        while (right != next) {
            if (next.value != right.value) {
                res = false;
                break;
            }
            next = next.next;
            right = right.next;
        }
        // 恢复反转的右部分单链表
        right = pre;
        Node tmp = null;
        while (right != next) {
            right = right.next;
            pre.next = tmp;
            tmp = pre;
            pre = right;
        }
        pre.next = tmp;
        return res;
    }

    /**
     * 将单向链表按某值划分成左边小、中间相等、右边大的形式
     * 时间复杂度请达到O(N),额外空间复杂度请达到O(N)。
     * 方法一:类似于荷兰国旗问题,新建一个Node类型的数组,进行排序,然后再建立指针。
     * 时间复杂度请达到O(N),额外空间复杂度请达到O(1)。
     * 方法二:申请6个辅助节点,标识各个3个区间(小于、等于、大于)的头和尾。
     * @param head
     * @param pivot
     * @return
     */
    public static Node listPartition(Node head, int pivot) {
        // 小于头
        Node sH = null;
        // 小于尾
        Node sT = null;
        // 等于头
        Node eH = null;
        // 等于尾
        Node eT = null;
        // 大于头
        Node bH = null;
        // 大于尾
        Node bT = null;
        // 辅助节点
        Node next = null;
        // 将所有节点划分给3条链
        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 (bH == null) {
                    bH = head;
                    bT = head;
                } else {
                    bT.next = head;
                    bT = head;
                }
            }
            head = next;
        }
        // 整合
        if (sT != null) {
            sT.next = eH;
            // eT为空,将st赋值给eT
            eT = eT == null ? sT : eT;
        }
        if (eT != null) {
            eT.next = bH;
        }
        return sH != null ? sH : eH != null ? eH : bH;
    }

    /**
     * 复制含有随机指针节点的链表
     * 时间复杂度O(N),额外空间复杂度O(1)
     * 方法一:hashMap,遍历新建节点,再遍历新建连接。额外空间是O(N)
     * 方法二:将新节点放在老节点的next中,节省了hashMap,使额外空间为O(1),遍历放入新节点,再遍历构建新节点的rand,再遍历分离
     * @param head
     * @return
     */
    public static Node copyListWithRand(Node head) {
        if (head == null) {
            return null;
        }
        Node cur = head;
        Node next =null;
        // 将新建节点插入在老节点的next中
        while (cur != null) {
            next = cur.next;
            cur.next = new Node(cur.value);
            cur.next.next = next;
            cur =next;
        }
        // 构建新节点的rand
        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;
        }
        // 分离
        cur = head;
        Node res = head.next;
        while (cur != null) {
            next = cur.next.next;
            curCopy = cur.next;
            cur.next = next;
            curCopy.next = next != null ? next.next : null;
            cur = next;
        }
        return res;
    }

    /**
     * 比较器
     */
    public static class NodeComparator implements Comparator<Node> {

        // 根据Node的value大小来排序,官方默认下面的返回为升序。
        // < return -1
        // = return 0
        // > return 1
        // 需要为降序则return的值与官方相反
        @Override
        public int compare(Node o1, Node o2) {
            return o1.value - o2.value;
        }
    }

    /**
     * 带有rand节点的链表打印
     * @param head
     */
    public static void printRandLinkedList(Node head) {
        Node cur = head;
        System.out.print("order: ");
        while (cur != null) {
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
        cur = head;
        System.out.print("rand:  ");
        while (cur != null) {
            System.out.print(cur.rand == null ? "- " : cur.rand.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    /**
     * 两个单链表相交的一系列问题
     * @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 head
     * @return
     */
    public static Node getLoopNode(Node head) {
        if (head == null || head.next == null || head.next.next == null) {
            return null;
        }
        // n1 -> slow
        Node n1 = head.next;
        // n2 -> fast
        Node n2 = head.next.next;
        while (n1 != n2) {
            if (n2.next == null || n2.next.next == null) {
                return null;
            }
            n2 = n2.next.next;
            n1 = n1.next;
        }
        // n2 -> walk again from head,这步很重要。
        n2 = head;
        while (n1 != n2) {
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }

    /**
     * 第一种情况:均无环
     *
     * @param head1
     * @param head2
     * @return
     */
    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
        cur1 = n > 0 ? head1 : head2;
        // 将节点数少的赋值给cur1
        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
     * @param loop1
     * @param head2
     * @param loop2
     * @return
     */
    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++;
                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;
        }
    }

    public static void main(String[] args) {

        Node nodeA = null;
        Node nodeB = null;
        Node nodeC = null;

        // hashSet1的key是基础类型->int类型
        HashSet<Integer> hashSet1 = new HashSet<>();
        hashSet1.add(3);
        System.out.println(hashSet1.contains(3));
        hashSet1.remove(3);
        System.out.println(hashSet1.contains(3));
        System.out.println("========1=========");

        // hashSet2的key是非基础类型->Node类型
        nodeA = new Node(1);
        nodeB = new Node(1);
        HashSet<Node> hashSet2 = new HashSet<>();
        hashSet2.add(nodeA);
        System.out.println(hashSet2.contains(nodeA));
        System.out.println(hashSet2.contains(nodeB));
        hashSet2.remove(nodeA);
        System.out.println(hashSet2.contains(nodeA));
        System.out.println("========2=========");

        // hashMap1的key是基础类型->String类型
        HashMap<String, Integer> hashMap1 = new HashMap<>();
        String str1 = "key";
        String str2 = "key";
        hashMap1.put(str1, 1);
        System.out.println(hashMap1.containsKey(str1));
        System.out.println(hashMap1.containsKey(str2));
        System.out.println(hashMap1.get(str1));
        System.out.println(hashMap1.get(str2));

        hashMap1.put(str2, 2);
        System.out.println(hashMap1.containsKey(str1));
        System.out.println(hashMap1.containsKey(str2));
        System.out.println(hashMap1.get(str1));
        System.out.println(hashMap1.get(str2));

        hashMap1.remove(str1);
        System.out.println(hashMap1.containsKey(str1));
        System.out.println(hashMap1.containsKey(str2));
        System.out.println("========3=========");

        // hashMap2的key是非基础类型->Node类型
        // 如果不是基础类型,内部按引用传递
        nodeA = new Node(1);
        nodeB = new Node(1);
        HashMap<Node, String> hashMap2 = new HashMap<>();
        hashMap2.put(nodeA, "A节点");
        System.out.println(hashMap2.containsKey(nodeA));
        System.out.println(hashMap2.containsKey(nodeB));
        System.out.println(hashMap2.get(nodeA));
        System.out.println(hashMap2.get(nodeB));
        hashMap2.put(nodeB, "B节点");
        System.out.println(hashMap2.containsKey(nodeA));
        System.out.println(hashMap2.containsKey(nodeB));
        System.out.println(hashMap2.get(nodeA));
        System.out.println(hashMap2.get(nodeB));
        System.out.println("========4=========");

        // treeSet的key是非基础类型->Node类型
        // 如果不是基础类型,必须提供比较器
        nodeA = new Node(5);
        nodeB = new Node(3);
        nodeC = new Node(7);

        TreeSet<Node> treeSet = new TreeSet<>();
        // 以下的代码会报错,因为没有提供Node类型的比较器
        try {
            treeSet.add(nodeA);
            treeSet.add(nodeB);
            treeSet.add(nodeC);
        } catch (Exception e) {
            System.out.println("错误信息:" + e.getMessage());
        }

        treeSet = new TreeSet<>(new NodeComparator());
        // 以下的代码没问题,因为提供了Node类型的比较器
        try {
            treeSet.add(nodeA);
            treeSet.add(nodeB);
            treeSet.add(nodeC);
            System.out.println("这次节点都加入了");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        System.out.println("========5=========");

        // 展示有序表常用操作
        TreeMap<Integer, String> treeMap1 = new TreeMap<>();
        treeMap1.put(7, "我是7");
        treeMap1.put(5, "我是5");
        treeMap1.put(4, "我是4");
        treeMap1.put(3, "我是3");
        treeMap1.put(9, "我是9");
        treeMap1.put(2, "我是2");
        System.out.println(treeMap1.containsKey(5));
        System.out.println(treeMap1.get(5));
        System.out.println(treeMap1.firstKey() + ", 我最小");
        System.out.println(treeMap1.lastKey() + ", 我最大");
        System.out.println(treeMap1.floorKey(8) + ", 在表中所有<=8的数中,我离8最近");
        System.out.println(treeMap1.ceilingKey(8) + ", 在表中所有>=8的数中,我离8最近");
        System.out.println(treeMap1.floorKey(7) + ", 在表中所有<=7的数中,我离7最近");
        System.out.println(treeMap1.ceilingKey(7) + ", 在表中所有>=7的数中,我离7最近");
        treeMap1.remove(5);
        System.out.println(treeMap1.get(5) + ", 删了就没有了哦");
        System.out.println("========6=========");

        // 单链表反转与输出
        Node head1 = new Node(1);
        head1.next = new Node(2);
        head1.next.next = new Node(3);
        printLinkedList(head1);
        head1 = reverseList(head1);
        printLinkedList(head1);

        // 双链表反转与输出
        DoubleNode head2 = new DoubleNode(1);
        head2.next = new DoubleNode(2);
        head2.next.last = head2;
        head2.next.next = new DoubleNode(3);
        head2.next.next.last = head2.next;
        head2.next.next.next = new DoubleNode(4);
        head2.next.next.next.last = head2.next.next;
        printDoubleLinkedList(head2);
        printDoubleLinkedList(reverseList(head2));

        // 打印两个有序链表的公共部分
        Node node1 = new Node(2);
        node1.next = new Node(3);
        node1.next.next = new Node(5);
        node1.next.next.next = new Node(6);

        Node node2 = new Node(1);
        node2.next = new Node(2);
        node2.next.next = new Node(5);
        node2.next.next.next = new Node(7);
        node2.next.next.next.next = new Node(8);

        printLinkedList(node1);
        printLinkedList(node2);
        printCommonPart(node1, node2);

        // 判断回文
        Node head3 = null;
        head3 = new Node(1);
        head3.next = new Node(2);
        head3.next.next = new Node(3);
        head3.next.next.next = new Node(2);
        head3.next.next.next.next = new Node(1);
        printLinkedList(head3);
        System.out.println(isPalindrome(head3));
        printLinkedList(head3);
        System.out.println("=========================");

        // 将单向链表按某值划分成左边小、中间相等、右边大的形式
        Node head4 = new Node(7);
        head4.next = new Node(9);
        head4.next.next = new Node(1);
        head4.next.next.next = new Node(8);
        head4.next.next.next.next = new Node(5);
        head4.next.next.next.next.next = new Node(2);
        head4.next.next.next.next.next.next = new Node(5);
        printLinkedList(head4);
        head4 = listPartition(head4, 5);
        printLinkedList(head4);

        // 复制含有随机指针节点的链表
        Node head5 = new Node(1);
        head5.next = new Node(2);
        head5.next.next = new Node(3);
        head5.next.next.next = new Node(4);
        head5.next.next.next.next = new Node(5);
        head5.next.next.next.next.next = new Node(6);

        head5.rand = head5.next.next.next.next.next; // 1 -> 6
        head5.next.rand = head5.next.next.next.next.next; // 2 -> 6
        head5.next.next.rand = head5.next.next.next.next; // 3 -> 5
        head5.next.next.next.rand = head5.next.next; // 4 -> 3
        head5.next.next.next.next.rand = null; // 5 -> null
        head5.next.next.next.next.next.rand = head5.next.next.next; // 6 -> 4

        printRandLinkedList(head5);
        Node res = copyListWithRand(head5);
        printRandLinkedList(res);

        // 两个单链表相交的一系列问题
        // 1->2->3->4->5->6->7->null
        Node head6 = new Node(1);
        head6.next = new Node(2);
        head6.next.next = new Node(3);
        head6.next.next.next = new Node(4);
        head6.next.next.next.next = new Node(5);
        head6.next.next.next.next.next = new Node(6);
        head6.next.next.next.next.next.next = new Node(7);

        // 0->9->8->6->7->null
        Node head7 = new Node(0);
        head7.next = new Node(9);
        head7.next.next = new Node(8);
        head7.next.next.next = head6.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head6, head7).value);

        // 1->2->3->4->5->6->7->4...
        head6 = new Node(1);
        head6.next = new Node(2);
        head6.next.next = new Node(3);
        head6.next.next.next = new Node(4);
        head6.next.next.next.next = new Node(5);
        head6.next.next.next.next.next = new Node(6);
        head6.next.next.next.next.next.next = new Node(7);
        head6.next.next.next.next.next.next = head6.next.next.next; // 7->4

        // 0->9->8->2...
        head7 = new Node(0);
        head7.next = new Node(9);
        head7.next.next = new Node(8);
        head7.next.next.next = head6.next; // 8->2
        System.out.println(getIntersectNode(head6, head7).value);

        // 0->9->8->6->4->5->6..
        head7 = new Node(0);
        head7.next = new Node(9);
        head7.next.next = new Node(8);
        head7.next.next.next = head6.next.next.next.next.next; // 8->6
        System.out.println(getIntersectNode(head6, head7).value);
    }
}

 

posted @ 2022-03-12 13:32  北漂的尘埃  阅读(48)  评论(0编辑  收藏  举报