leetcode 148题 排序链表JAVA实现

leetcode刷了9题了,一直没有在博客上面记录一下,发现做完一题忘了一题所以今天开始做一道记一道。
链表排序

使用递归的归并排序

首先这个方法肯定不满足题目要求的常数级空间复杂度,但是还是从这里入手,因为算法太渣了。。。。

链表的排序要比数组排序难,主要是链表的断链还有重新建立连接。递归需要的时间复杂度为递归的深度为logn
递归排序的思想很简单,先把递归的把左面和右面排好序然后在merge
归并排序
代码分为两个部分

  1. cut部分
    先用fast和slow双指针法找到链表的中点(奇数链表为中点,偶数链表为中点的左面一个)
    灵魂画图。。。。。。。。双指针法找中点
    然后进行断链操作,把两个链表进行递归操作
    在这里插入图片描述
    递归结束条件 : 一个节点
    在这里插入图片描述
  2. merge部分
    双指针法插入链表
    新建一个临时的头结点用于构造新链表然后定义一个H指针指向新链表的最后一个结点,接下来比较P1和P2哪个小就把哪个接上就好了,最后把剩余加到尾巴处就可以了
//链表排序 要求nlogn
public class leetcode148 {
    public static class ListNode{
        public int val;
        public ListNode next;
        public ListNode( int val){
            this.val = val;
        }
    }
    //排序cut部分
    public static ListNode mergeSort(ListNode head){
        if(head==null||head.next==null){
            return head;
        }
        ListNode slow = head;
        ListNode quick = head.next;
        while(quick!=null&&quick.next!=null){
            slow = slow.next;
            quick = quick.next.next;
        }
        ListNode tmp = slow.next;
        slow.next = null;
//        printList(head);
//        printList(tmp);
        head = mergeSort(head);
        tmp = mergeSort(tmp);

        return merge(head,tmp);
    }
//合并部分
    private static ListNode merge(ListNode head, ListNode tmp) {
        ListNode p1 = head;
        ListNode p2 = tmp;
        ListNode tempH = new ListNode(0);
        ListNode h = tempH;

        while(p1!=null&&p2!=null){
            if(p1.val<p2.val){
                h.next = p1;
                h = p1;
                p1=p1.next;
            }else{
                h.next = p2;
                h = p2;
                p2=p2.next;
            }
        }
        if(p1!=null){
            h.next = p1;
        }
        if(p2!=null){
            h.next = p2;
        }
//        printList(tempH.next);
        return tempH.next;
    }
//打印链表
    public static void printList(ListNode head){
        ListNode p = head;
        while(p!=null){
            System.out.print(p.val+"->");
            p=p.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        ListNode a = new ListNode(9);
        a.next= new ListNode(1);
        a.next.next  = new ListNode(7);
        a.next.next.next = new ListNode(3);
        a.next.next.next.next  = new ListNode(5);
        a.next.next.next.next.next = new ListNode(2);
        printList(a);
        ListNode head = mergeSort(a);
        printList(head);
    }
}

链表排序

从底到顶直接排序(非归并排序)

如果要求空间复杂度为O(1)的话就不能用递归来排序了,只能用迭代的方式
先1个1个合并然后2个2个合并,结束条件为合并的个数大于等于链表长度
bottomtoup排序
代码主要是控制两个循环

size循环
  1. 控制合并的个数size增长,每轮合并size长度的链表。
  2. size=1 每次循环乘以2 当size<=length时一直继续循环
合并
  1. tail指针开始指向dummyhead,cur指向第一个节点
  2. left指针指向左面的链表,right指向右面的链表
  3. 通过cut操作可以实现断链并返回链表后面的部分,cur指向后面的部分在这里插入图片描述
  4. 然后merge操作是双路归并,返回的链表可以用tail指向
  5. 最后移动tail到最后一个结点在这里插入图片描述
  6. cur==null时代表此轮归并完毕
cut操作

cut操作可以将链表截断前n长度并返回后面的部分

容易出现的错

cut操作可能size大小比链表长度要大,所以要在循环里判断一下
在这里插入图片描述
dummyhead的用处:
每轮cur都从新指向头结点,所以定义一个虚拟的头结点,这样很方便就能找到新的结点(开始代码写错了,写的是cur=head,但是head已经被排在后面了)
因为头结点会变化,定义一个虚拟的头结点代替头结点的指针。
在这里插入图片描述

/*merge 和 数据结构 在上面有*/
//不用递归的归并排序
    public static ListNode mergeSort2(ListNode head){
        if(head==null||head.next==null){
            return head;
        }
        //先求链表的长度
        int length = 0;
        ListNode p = head;
        while(p!=null){
            p=p.next;
            length++;
        }

        ListNode dummyHead = new ListNode(Integer.MIN_VALUE);
        dummyHead.next = head;

        //迭代控制每次合并的size大小
        for(int size=1;size<=length;size <<=1){
            //不能用head头结点 head头结点已经变化到末尾了
            ListNode cur = dummyHead.next;
            ListNode tail = dummyHead;
            //按size大小合并一轮
            while(cur!=null){
                ListNode left = cur;
                ListNode right = cut(left,size);
                cur = cut(right,size);

                tail.next = merge(left,right);
                //tail指针指向最后一个结点可以连接merge完的链表

                while(tail.next!=null){
                    tail=tail.next;
                }
            }
            //printList(dummyHead.next);
        }
        return dummyHead.next;
    }
    //切断链表的n个结点 然后返回剩余部分的head
    public static ListNode cut(ListNode head,int size){
        if(head==null){
            return null;
        }
        ListNode cur = head;
        while(--size!=0&&cur.next!=null){
            cur = cur.next;
        }
        ListNode p = cur.next;
        cur.next = null;
        return p;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值