LeetCode 排序链表

这篇博客介绍了两种对链表进行排序的方法:快速排序和归并排序。首先,第一种方法是将链表的元素存入数组,使用快速排序算法对数组进行排序,然后更新链表的节点值。第二种方法则是直接在链表上进行归并排序,通过快慢指针找到中点,递归地对左右子链表进行排序,最后合并。博主详细阐述了两种算法的实现细节,并给出了相应的代码示例。
摘要由CSDN通过智能技术生成

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
在这里插入图片描述
在这里插入图片描述

1、暴力方法,先将链表的值存放到一个数组中,然后对这个数组进行快速排序,然后只需要遍历这个数组,将对应的数组的值赋值给链表的节点即可。但是这样做的话需要开辟空间给数组。
对应的代码:

class Solution {
    public ListNode sortList(ListNode head) {
       ListNode cur = head;
       List<Integer> list = new ArrayList<Integer>(); //临时存放链表的值
       while(cur != null){
           list.add(cur.val);
           cur = cur.next;
       }
       int[] arr = new int[list.size()];
       int i;
       for(i = 0; i < arr.length; i++){
           arr[i] = list.get(i);
       }
       sort(arr,0,arr.length - 1); //对数组进行快速排序
       /*
       遍历链表之后,重新创建链表的操作虽然也可以,但是这样的话就还需要
       开辟空间,所以为了减少空间的浪费,直接在原来的链表的基础上修改它
       节点的值即可。
       */
       cur = head;
       //遍历已经排好序的数组,然后构建链表
       for(i = 0; i < arr.length; i++){
           cur.val = arr[i];
           cur = cur.next;
       }
       return head;
    }
    public void sort(int[] arr,int low,int high){
        if(low < high){
            int pivot = getPivot(arr,low,high); //利用三数中值法,从而获取中间枢纽
            System.out.println(pivot);
            int i = low,j = high - 1;
            while(i < j){
            /*
            必须要从左边开始遍历,找到第一个大于枢纽的数,然后从右边遍历
            找到第一个小于枢纽的数,然后将两者的值进行交换。如果两个步骤
            交换顺序,那么就会导致快速排序的错误
            */
            
                while(i < j && arr[++i] <= pivot); 
                while(i < j && arr[--j] >= pivot); 
                if(i >= j)
                   break;
                swap(arr,i,j);               
            }
            arr[high - 1] = arr[i];
            arr[i] = pivot;
            sort(arr,low,i - 1);
            sort(arr,i + 1,high);
        }
    }
    public void swap(int[] arr,int i,int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    public int getPivot(int[] arr,int low,int high){
        int mid = (low + high) / 2;
        if(arr[low] > arr[mid])
          swap(arr,low,mid);
        if(arr[low] > arr[high])
          swap(arr,low,high);
        if(arr[mid] > arr[high])
          swap(arr,mid,high);
        swap(arr,mid,high - 1);
        return arr[high - 1];
    }
}

运行结果:
在这里插入图片描述

2、利用归并排序
但是对于链表来说,利用归并排序,首先我们需要获取中间的节点。怎么实现这一步呢?利用快慢指针就好了,快指针走的是慢指针的2被,此时当快指针走完整个链表的时候,那么慢指针就是终点的位置了。然后进入递归,当链表为空或者只有一个节点的时候,那么就结束递归,进行合并的操作了。

对应的代码:

class Solution {
    /*
    利用归并排序对链表进行排序
    */
    public ListNode sortList(ListNode head) {
       return mergeSort(head);
    }
    public ListNode mergeSort(ListNode head){
        if(head != null && head.next != null){
        //如果链表不为空,并且不止一个节点的时候,那么就对这个链表进行分割
            ListNode cur,pre,tmp;
            pre = head;
            cur = head;
              //通过双指针,从而得到链表的中点
            while(cur != null && cur.next != null && cur.next.next != null){
            //循环条件必须是这样,因为需要考虑链表的节点的个数是奇数和偶数的情况
                pre = pre.next;
                cur = cur.next.next;
            }
            tmp = pre.next;
            pre.next = null; //基于上面while循环的判断条件,这一步是必要的,从而将链表彻底变成两条子链表
            ListNode left = mergeSort(head);
            ListNode right = mergeSort(tmp);
            ListNode sorted = merge(left,right);//对两条子链表进行排序,将排好序的
            return sorted; //返回排好序的链表的头结点
            
        }else{
        //如果链表为空,或者链表只有一个节点的时候,直接将这个节点返回
            return head;
        }
        
    }
    /*
    对两条有序链表进行合并的时候,合并时是在其中一条链表上操作的,而不是重
    新创建一条链表
    */
    public ListNode merge(ListNode head1,ListNode head2){
        ListNode cur1,cur2,pre,tmp;
        ListNode root = new ListNode(0);
        root.next = head1;
        pre = root;
        cur1 = head1;
        cur2 = head2;
        while(cur1 != null && cur2 != null){
            if(cur1.val < cur2.val){
                cur1 = cur1.next;
            }else{
                tmp = cur2.next;
                cur2.next = pre.next;
                pre.next = cur2;
                cur2 = tmp;
            } 
           /*
           这一步是关键,不可以将这一步写在if判断里面,否则就会导致错误
           比如当前的值是cur2的值小,如果将pre = pre.next这一步写在if判断
           里面,那么最后导致合并的链表缺少某些值
           */
            pre = pre.next; 
        }
        if(cur2 != null)
          pre.next = cur2;
        return root.next;
    }
}

运行结果:
在这里插入图片描述

但是考虑到ListNode是一个引用类型,那么在方法中对形参修改,会影响到实际的参数,所以原来的代码是这样的,但是结果是错误的,如果有大佬明白错误的原因的话,请指正。

 public ListNode sortList(ListNode head) {
       mergeSort(head);
       return head;
    }
    public void mergeSort(ListNode head){
        if(head != null && head.next != null){
            //通过双指针,从而得到链表的中点
            ListNode cur,pre,tmp;
            pre = head;
            cur = head;
            while(cur != null && cur.next != null && cur.next.next != null){
                pre = pre.next;
                cur = cur.next.next;
            }
            tmp = pre.next;
            pre.next = null;
            mergeSort(head);
            mergeSort(tmp);
            ListNode sorted = merge(head,tmp);
        }
        
    }
    public void merge(ListNode head1,ListNode head2){
        ListNode cur1,cur2,pre,tmp;
        ListNode root = new ListNode(0);
        root.next = head1;
        pre = root; 
        cur1 = head1;
        cur2 = head2;
        while(cur1 != null && cur2 != null){
            if(cur1.val < cur2.val){
                cur1 = cur1.next;
            }else{
                tmp = cur2.next;
                cur2.next = pre.next;
                pre.next = cur2;
                cur2 = tmp;
            } 
            pre = pre.next;
        }
        if(cur2 != null)
          pre.next = cur2;
        head1 =  root.next;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值