[******] 链表问题:将单向链表按某值划分成左边小、中间相等、右边大的形式...

问题描述

普通问题:
给定一个单向链表的头节点head,节点的值类型是整数,再给定一个整数 pivot,实现一个调整链表的函数: 使得左半部分的值都是小于pivot的节点,中间部分都是等于pivot的节点,右边部分都是大于pivot的节点,对于左半部分、中间、右半部分内部的顺序没有要求。
进阶问题:
【要求】 调整后所有小于pivot的节点之间的相对顺序和调整前一样
【要求】 调整后所有等于pivot的节点之间的相对顺序和调整前一样
【要求】 调整后所有大于pivot的节点之间的相对顺序和调整前一样
【要求】 时间复杂度请达到O(N), 额外空间复杂度请达到O(1)。

思路描述

普通问题:变成数组,数组上partition(快排),然后再返回链表。 时间O(N),空间O(N)
进阶问题:想办法弄三个链表(小于,等于,大于),然后合并链表。时间O(N),空间O(1)

代码

普通问题

public class Main{
    static class Node {
        int value;
        Node next;
        public Node(int value){
            this.value = value;
        }
    }
    /**************************普通问题******************/
     /**
     * 普通问题
     * 时间复杂度是 O(N),空间复杂度是 O(N)
     * 首先遍历一遍链表,得到链表的长度,将链表元素依次放到数组中,然后利用类似于快速排序中的partition 思想进行分割
     */
    public static Node listPartition(Node head, int pivot) {
        if (head == null) {
            return head;
        }
        //计算链表的长度
        Node cur = head;
        int len = 0;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        //将链表变成数组:将链表中的每个元素依次放入到数组中
        Node[] nodeArray = new Node[len];
        int i = 0;
        cur = head;
        for (i = 0; i != nodeArray.length; i++) {
            nodeArray[i] = cur;
            cur = cur.next;
        }
        //将数组进行分区: 进行类似快速排序的partition
        arrPartition(nodeArray, pivot);
        //将数组变成链表:重新连接各个链表节点
        for (i = 1; i != len; i++) {
            nodeArray[i - 1].next = nodeArray[i];
        }
        nodeArray[i - 1].next = null;
        //返回链表的head
        return nodeArray[0];
    }
    /**进行类似快速排序的partition(补充,如果要稳定性,可以考虑插入排序,冒泡排序或者归并排序)*/
    public static void arrPartition(Node[] nodeArray, int pivot) {
        int small = -1;
        int big = nodeArray.length;
        int index = 0;
        while (index != big) {
            if (nodeArray[index].value < pivot) {//如果小于p,
                swap(nodeArray, ++small, index++);//首先交换small和index的位置,然后各自+1,表示
            } else if (nodeArray[index].value == pivot) {
                index++;
            } else {
                swap(nodeArray, --big, index);
            }
        }
    }
    /**交换两个数*/
    public static void swap(Node[] nodeArray, int a, int b) {
        Node tmp = nodeArray[a];
        nodeArray[a] = nodeArray[b];
        nodeArray[b] = tmp;
    }
    
    
    /***测试**/
    public static void main(String[] args) {
        int[] array = {2,3,4,6,7,1,2,4,8,9};
        int pivot = 4;
        Node head = new Node(array[0]);
        Node cur = head;
        for (int i = 1; i < array.length; i++) {
            cur.next = new Node(array[i]);
            cur=cur.next;
        }
        head = listPartition(head, pivot);
        cur = head;
        while(cur!=null){
            System.out.print(cur.value+" ");
            cur = cur.next;
        }
    }
}

 

进阶问题

public class Main{
    static class Node {
        int value;
        Node next;
        public Node(int value){
            this.value = value;
        }
    }
    
    /**************************进阶问题******************/
    /**
     *  进阶问题: 保证各部分内部的顺序与原来链表中各个节点的顺序相同(稳定性)
     *  时间复杂度是 O(N),空间复杂度是 O(1)
     *  考察 利用有限的几个变量来调整链表的代码实现能力。
     *  具体思路如下:
     *  1.将链表分为三部分:small,equal,big
     *                 small:1->2->null
     *                 equal:5->5->null
     *                 big:9->8->null
     *  2.将small、equal、big三个链表重新串起来
     *  3.整个过程需要特别注意对null节点的判断和处理
     */
    public static Node listPartition2(Node head, int pivot){

        // 将链表分为small、equal、big
        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){
            // 将head节点独立出来
            next = head.next;
            head.next = null;

            if (head.value < pivot){
                if (sH == null){
                    sH = head;
                }else {
                    sT.next = head;
                }
                sT = head;
            }else if (head.value == pivot){
                if (eH == null){
                    eH = head;
                }else {
                    eT.next = head;
                }
                eT = head;
            }else {
                if (bH == null){
                    bH = head;
                }else {
                    bT.next = head;
                }
                bT = head;
            }
            head = next;
        }

        // 小的和相等的重新连接
        if (sT != null){
            sT.next = eH;
            if(eT==null){
                eT=sT;
            }
        }
        // 和大的重新连接
        if (eT != null){
            eT.next = bH;
        }

        // 判断头节点是否为空
        if(sH!=null){
            return sH;
        }else{
            if(eH!=null){
                return eH;
            }else{
                return bH;
            }
        }
    }
    
    /***测试**/
    public static void main(String[] args) {
        int[] array = {2,3,4,6,7,1,2,4,8,9};
        int pivot = 4;
        Node head = new Node(array[0]);
        Node cur = head;
        for (int i = 1; i < array.length; i++) {
            cur.next = new Node(array[i]);
            cur=cur.next;
        }
        head = listPartition2(head, pivot);
        cur = head;
        while(cur!=null){
            System.out.print(cur.value+" ");
            cur = cur.next;
        }
    }
}

 

整合

public class Main{
    static class Node {
        int value;
        Node next;
        public Node(int value){
            this.value = value;
        }
    }
    /**************************普通问题******************/
     /**
     * 普通问题
     * 时间复杂度是 O(N),空间复杂度是 O(N)
     * 首先遍历一遍链表,得到链表的长度,将链表元素依次放到数组中,然后利用类似于快速排序中的partition 思想进行分割
     */
    public static Node listPartition(Node head, int pivot) {
        if (head == null) {
            return head;
        }
        //计算链表的长度
        Node cur = head;
        int len = 0;
        while (cur != null) {
            len++;
            cur = cur.next;
        }
        //将链表变成数组:将链表中的每个元素依次放入到数组中
        Node[] nodeArray = new Node[len];
        int i = 0;
        cur = head;
        for (i = 0; i != nodeArray.length; i++) {
            nodeArray[i] = cur;
            cur = cur.next;
        }
        //将数组进行分区: 进行类似快速排序的partition
        arrPartition(nodeArray, pivot);
        //将数组变成链表:重新连接各个链表节点
        for (i = 1; i != len; i++) {
            nodeArray[i - 1].next = nodeArray[i];
        }
        nodeArray[i - 1].next = null;
        //返回链表的head
        return nodeArray[0];
    }
    /**进行类似快速排序的partition(补充,如果要稳定性,可以考虑插入排序,冒泡排序或者归并排序)*/
    public static void arrPartition(Node[] nodeArray, int pivot) {
        int small = -1;
        int big = nodeArray.length;
        int index = 0;
        while (index != big) {
            if (nodeArray[index].value < pivot) {//如果小于p,
                swap(nodeArray, ++small, index++);//首先交换small和index的位置,然后各自+1,表示
            } else if (nodeArray[index].value == pivot) {
                index++;
            } else {
                swap(nodeArray, --big, index);
            }
        }
    }
    /**交换两个数*/
    public static void swap(Node[] nodeArray, int a, int b) {
        Node tmp = nodeArray[a];
        nodeArray[a] = nodeArray[b];
        nodeArray[b] = tmp;
    }
    /**************************进阶问题******************/
    /**
     *  进阶问题: 保证各部分内部的顺序与原来链表中各个节点的顺序相同(稳定性)
     *  时间复杂度是 O(N),空间复杂度是 O(1)
     *  考察 利用有限的几个变量来调整链表的代码实现能力。
     *  具体思路如下:
     *  1.将链表分为三部分:small,equal,big
     *                 small:1->2->null
     *                 equal:5->5->null
     *                 big:9->8->null
     *  2.将small、equal、big三个链表重新串起来
     *  3.整个过程需要特别注意对null节点的判断和处理
     */
    public static Node listPartition2(Node head, int pivot){

        // 将链表分为small、equal、big
        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){
            // 将head节点独立出来
            next = head.next;
            head.next = null;

            if (head.value < pivot){
                if (sH == null){
                    sH = head;
                }else {
                    sT.next = head;
                }
                sT = head;
            }else if (head.value == pivot){
                if (eH == null){
                    eH = head;
                }else {
                    eT.next = head;
                }
                eT = head;
            }else {
                if (bH == null){
                    bH = head;
                }else {
                    bT.next = head;
                }
                bT = head;
            }
            head = next;
        }

        // 小的和相等的重新连接
        if (sT != null){
            sT.next = eH;
            if(eT==null){
                eT=sT;
            }
        }
        // 和大的重新连接
        if (eT != null){
            eT.next = bH;
        }

        // 判断头节点是否为空
        if(sH!=null){
            return sH;
        }else{
            if(eH!=null){
                return eH;
            }else{
                return bH;
            }
        }
    }
    
    /***测试**/
    public static void main(String[] args) {
        int[] array = {2,3,4,6,7,1,2,4,8,9};
        int pivot = 4;
        Node head = new Node(array[0]);
        Node cur = head;
        for (int i = 1; i < array.length; i++) {
            cur.next = new Node(array[i]);
            cur=cur.next;
        }
        head = listPartition2(head, pivot);
        cur = head;
        while(cur!=null){
            System.out.print(cur.value+" ");
            cur = cur.next;
        }
    }
}

 

转载于:https://www.cnblogs.com/haimishasha/p/11446954.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值