算法:归并排序

归并:将两个有序数组合并成一个有序数组

 public void merge(int left,int right,int mid,int[] num)
     //标记左半区第一个未排序的元素                                  
     int l_pos=left;                                   
     //标记右半区第一个未排序的元素                                  
     int r_pos=mid+1;                                  
     int pos=left;                                     
      while(l_pos<=mid&&r_pos<=right){                 
              if(nums[l_pos]<nums[r_pos])              
                  temp[pos++]=nums[l_pos++];           
                                     
              else                                     
                                     
                  temp[pos++]=nums[r_pos++];           
                 }                                     
      //合并左半区剩余的元素,剩余元素已经有序                            
     while(l_pos<=mid)                                 
         temp[pos++]=nums[l_pos++];                    
     //合并右半区剩余的元素,剩余元素已经有序                                      
     while (r_pos<=right)                              
         temp[pos++]=nums[r_pos++];                    
     //把临时数组中合并后的元素复制回原来的数组                            
      int j=0;                                         
      for(int i=left;i<=right;i++)                     
          nums[i]=temp[i];                                                   
                                                                                      
 }                                                     

对于一个完全无序的数组,使用归并排序,要用到分治和递归的思想

分治

在这里插入图片描述

归并排序

 public static void main(String[] args) {                              
     int[] nums={6,3,8,0,1,3,2,3,3,3,8,4,10,12,3};                     
     //归并排序需要一个额外的空间                                                   
     int[] temp=new int[nums.length];                                  
     new Merge().mergeSort(0,nums.length-1,nums,temp);                 
     for(int n:nums)                                                   
         System.out.print(n);                                          
 }                                                                     

 public void mergeSort(int left,int right,int[] nums,int[] temp){   
     //如果只有一个元素,那么不需要继续划分,只有一个元素的区域                                 
     //本身就是有序的,只需要被归并即可                                             
 if(left==right)                                                    
        return;                                                     
                                                                    
  else {                                                            
         int mid = left + (right - left) / 2;                       
         //[left,mid] [mid+1,right]                                 
         //递归划分左半区,进行排序                                                  
         mergeSort(left, mid, nums,temp);                           
         //递归划分右半区,进行排序                                              
         mergeSort(mid+1, right, nums,temp);                        
         //合并已经排序的部分                                                
         merge(left, right, mid,nums,temp);                         
     }                                                              
 }                                                                  

在这里插入图片描述

链表的归并排序

在这里插入图片描述
递归的空间复杂度由递归的深度决定,归并排序递归深度为logn(二分法决定),空间复杂度为O(logn)
在这里插入图片描述

递归

在这里插入图片描述
在这里插入图片描述

/* 知识点1:归并排序的整体思想
 * 知识点2:找到一个链表的中间节点的方法
 * 知识点3:合并两个已排好序的链表为一个新的有序链表
 */

class Solution {
    public ListNode sortList(ListNode head) {
        if(head==null || head.next==null) return head;
        ListNode slow = head; //慢指针
        ListNode fast = head.next; //快指针
        
        while(fast!=null && fast.next!=null){ //快慢指针找到链表中点
            slow = slow.next; //慢指针走一步
            fast = fast.next.next; //快指针走两步
        }
        ListNode rightHead = slow.next; //链表第二部分的头节点
        slow.next = null; //cut 链表
        
        ListNode left = sortList(head); //递归排序前一段链表
        ListNode right = sortList(rightHead); //递归排序后一段链表
        return merge(left,right);
    }  
    public ListNode merge(ListNode h1,ListNode h2){ //合并两个有序链表
        ListNode dummy = new ListNode(-1);
        ListNode p = dummy;
        while(h1!=null && h2!=null){
            if(h1.val < h2.val){
                p.next = h1;
                h1 = h1.next;
            }else{
                p.next = h2;
                h2 = h2.next;
            }
            p = p.next;
        }
        if(h1!=null)    p.next = h1;
        else if(h2!=null) p.next = h2;
        return dummy.next;

    }
}

迭代:使用迭代替代分割环节,分割环节本质上是通过二分法得到链表最小节点单元,再通过多轮合并得到排序结果(从底至顶直接合并)

递归主要实现分割,那么需要使用迭代替代递归直接实现分割环节,从前到后
在这里插入图片描述

class Solution {
    public ListNode sortList(ListNode head) {
        int length = getLength(head);
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
       
        for(int step = 1; step < length; step*=2){ //依次将链表分成1块,2块,4块...
            //每次变换步长,pre指针和cur指针都初始化在链表头
            ListNode pre = dummy; 
            ListNode cur = dummy.next;
            while(cur!=null){
                ListNode h1 = cur; //第一部分头 (第二次循环之后,cur为剩余部分头,不断往后把链表按照步长step分成一块一块...)
                ListNode h2 = split(h1,step);  //第二部分头
                cur = split(h2,step); //剩余部分的头
                ListNode temp = merge(h1,h2); //将一二部分排序合并
                pre.next = temp; //将前面的部分与排序好的部分连接
                while(pre.next!=null){
                    pre = pre.next; //把pre指针移动到排序好的部分的末尾
                }
            }
        }
        return dummy.next;
    }
    public int getLength(ListNode head){
    //获取链表长度
        int count = 0;
        while(head!=null){
            count++;
            head=head.next;
        }
        return count;
    }
    public ListNode split(ListNode head,int step){
        //断链操作 返回第二部分链表头
        if(head==null)  return null;
        ListNode cur = head;
        for(int i=1; i<step && cur.next!=null; i++){
            cur = cur.next;
        }
        ListNode right = cur.next;
        cur.next = null; //切断连接
        return right;
    }
    public ListNode merge(ListNode h1, ListNode h2){
    //合并两个有序链表
        ListNode head = new ListNode(-1);
        ListNode p = head;
        while(h1!=null && h2!=null){
            if(h1.val < h2.val){
                p.next = h1;
                h1 = h1.next;
            }
            else{
                p.next = h2;
                h2 = h2.next;
            }
            p = p.next;           
        }
        if(h1!=null)    p.next = h1;
        if(h2!=null)    p.next = h2;

        return head.next;     
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值