算法面试题-划分单链表

题目:

将单向链表按某值划分为左边小、中间相等、右边大的形式,并且调整后三个部分中节点的相对位置和调整前是一样的。要求时间复杂度为O(N),额外空间复杂度为O(1)

分析:

方法1: 采用数组储存链表中的节点,然后采取快排中的Partition操作将数组进行排序为三个部分。但是该方法不稳定,无法满足相对位置不变,且额外空间复杂度为O(N)
方法2:采取有限变量,形成三个链表
1.对三个部分分别设置头指针和尾指针
2.根据节点的大小对三个部分链表进行扩充
3.将三个链表的头和尾进行相连

代码实现:

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

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

    // 方法1:采用数组储存链表中的节点,在数组中进行Partition操作
    public static Node listPartition1(Node head, int pivot) {
        if (head == null) {
            return head;
        }
        Node cur = head;
        int length_list = 0;  // 记录链表长度
        while (cur != null) {
            length_list++;
            cur = cur.next;
        }

        Node[] nodeArr = new Node[length_list];
        cur = head;
        for (int i = 0; i < length_list; i++) {
            nodeArr[i] = cur;
            cur = cur.next;
        }
        arrPartition(nodeArr, pivot);

        // 将数组重新转换为链表
        for (int i = 1; i < nodeArr.length; i++) {
            nodeArr[i - 1].next = nodeArr[i];
        }
        nodeArr[nodeArr.length - 1].next = null;
        return nodeArr[0];

    }

    // 将数组根据划分值分为三个部分,参考快排部分有详细讲解
    public static void arrPartition(Node[] nodeArr, int pivot) {
        int small = -1;  // 小于区的右边界
        int big = nodeArr.length;  // 大于区的左边界
        int index = 0;  // 表示当前数的位置
        while (index != big) {
            if (nodeArr[index].value < pivot) {  //当前数 < 划分值
                swap(nodeArr, ++small, index++);
            } else if (nodeArr[index].value == pivot) {
                index++;
            } else {
                swap(nodeArr, --big, index);  // 当前数 > 划分值
            }
        }

    }

    public static void swap(Node[] nodeArr, int a, int b) {
        Node tmp = nodeArr[a];
        nodeArr[a] = nodeArr[b];
        nodeArr[b] = tmp;
    }

    // 方法2:采取有限变量
    public static Node listPartition2(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 bH = null;  // big head
        Node bT = 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 (bH == null) {
                    bH = head;
                    bT = head;
                } else {
                    bT.next = head;
                    bT = head;
                }
            }
            head = next; //继续往下遍历
        }

        // 将小于部分的链表和等于部分的链表进行相连
        if (sT != null) {  //当sT==null的时候说明小于部分的链表为空
            sT.next = eH;
            eT = eT == null ? sT : eT;  // 谁去连接大于区域的头节点,谁就变为eT
        }

        // 将三个部分链表全部相连
        if (eT != null) {  //当eT==null的时候说明等于部分的链表为空
            eT.next = bH;
        }

        // 最后返回头节点需要判断返回三个部分中第一个头节点不为空的节点
        return sH != null ? sH : eH != null ? eH : bH;
    }
}

tips:

在方法2中的第三步对三个部分进行相连时,需要注意不同部分的链表是否为空。不能简单直接将上个部分的尾节点直接连到下个部分的头节点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值