关于算法:将单向链表划分成左边小中间等右边大的形式

37次阅读

共计 3384 个字符,预计需要花费 9 分钟才能阅读完成。

咋一看,仿佛和快排的 partition 过程,也就是荷兰国旗问题很类似,只是此处换成了单链表构造,实际上也的确能够采纳荷兰国旗问题来求解,只是此时的额定空间复杂度为 O(N)。

1、采纳荷兰国旗问题求解:

(1)将链表放到数组中

(2)应用荷兰国旗办法将数组分为小于区、等于区、大于区

(3)将数组从新连成链表,返回头节点

/**
 * @author Java 和算法学习:周一
 */
public static Node comparator(Node head, int pivot) {if (head == null) {return null;}

    // 获取链表节点数
    Node current = head;
    int i = 0;
    while (current != null) {
        i++;
        current = current.next;
    }

    // 将链表放到数组中
    Node[] arr = new Node[i];
    current = head;
    for (i = 0; i < arr.length; i++) {arr[i] = current;
        current = current.next;
    }

    // 将数组分为小于区、等于区、大于区(外围就是荷兰国旗问题)partition(arr, pivot);

    // 将数组连成链表
    for (i = 1; i < arr.length; i++) {arr[i - 1].next = arr[i];
    }
    arr[i - 1].next = null;

    // 返回头节点
    return arr[0];
}

private static void partition(Node[] arr, int pivot) {
    int small = -1;
    int big = arr.length;
    int index = 0;
    while (index < big) {if (arr[index].value < pivot) {swap(arr, ++small, index++);
        } else if (arr[index].value == pivot) {index++;} else {swap(arr, --big, index);
        }
    }
}

2、额定空间复杂度为 O(1) 的解法:

(1)应用 6 个变量,顺次示意小于区头节点、尾结点,等于区头节点、尾结点,大于区头节点、尾结点

(2)遍历链表,把节点放到各个局部、同时调整尾结点

(3)将小于区 等于区 大于区 三块头尾连接起来

/**
 * @author Java 和算法学习:周一
 */
public static Node smallerEqualBigger(Node head, int pivot) {
    // 小于区头节点
    Node sH = null;
    // 小于区尾节点
    Node sT = null;
    // 等于区头节点
    Node eH = null;
    // 等于区尾节点
    Node eT = null;
    // 大于区头节点
    Node bH = null;
    // 大于区尾节点
    Node bT = null;
    // 标记以后遍历的 next 节点
    Node next = null;

    // 将链表分成 小于区 等于区 大于区 三块
    while (head != null) {
        // 记录 next 节点
        next = head.next;
        // 以后节点 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;
            }
        }

        // 以后节点挪动到 next 节点
        head = next;
    }

    // 将小于区 等于区 大于区 三块头尾连接起来
    // 小于区存在
    if (sT != null) {
        // 则小于区尾节点连贯等于区头节点
        sT.next = eH;
        // 如果此时等于区尾结点为空(即整个等于区为空),那么等于区的尾结点则为小于区尾结点
        // 如果此时等于区尾结点不为空,那么等于区的尾结点就是之前的尾结点
        eT = eT == null ? sT : eT;
    }
    // 有等于区,eT 就是等于区的尾结点
    // 无等于区,有小于区,eT 就是小于区的尾结点
    // 无等于区,无小于区,eT 就是空
    if (eT != null) {
        // 则等于区的尾结点连贯大于区头节点
        eT.next = bH;
    }

    // 最初返回以后的头节点
    // 有小于区,返回小于区头节点
    // 无小于区,有等于区,返回等于区头节点
    // 无小于区,无等于区,有大于区,返回大于区头节点
    return sH != null ? sH : (eH != null ? eH : bH);
}

本文所有代码:https://github.com/monday-pro/algorithm-study/tree/master/src/basic/node

正文完
 0