个人笔记-分治法中快速排序与归并排序

1,分治法:把整体分为若干个单元块,然后去解决问题。最后每个单元块解决后,合并出最后结果。

     最常见的就是递归调用。将一个基本处理单元,反复调用。最终解决问题。

 

 

   关于排序中,有两种算法很好的提现这种思想。分别为:快速排序法和归并排序法。

 

一,快速排序

      主要原理: 在一组数据头或者尾部,拿出一个元素,然后分别在头部和尾部滑动指针,和拿出的数比大小。最终头尾指针重合,将拿出的元素放到重合的位置。核心是,一轮完毕后,以最初拿出的数据为界限,一边全部比它小,一边全部比他大。

    拿到分界位置后,将原有集合分为两部分,在按照以上思路排一轮。重复此步骤,最终生成结果。

    此部分类似于二叉树的前序遍历,先做处理,然后做左右两部分操作继续。

 

//快速排序    //比较两个数时,相等也取出来,重复数据,性能下降

/**
 *
 * 特点:一轮排完后,以中间指针位置,左侧全部小于,右侧全部大于。比较两个数时,相等也取出来,重复数据,性能下降
 * 快速排序思路先取一个,然后头尾指针分别找。最后重合后。结束本轮。   然后下一轮从重合指针的位置,分成两组,再分别头尾找。
 * @param datas
 * @param start
 * @param end
 *
 *
 * 优点,空间小,效率高
 * 不足:链表不适用,大量重复数据效率很低(原因是规则要求相等数据也取出换位置)
 */

public void quickSort(int[] datas,int start,int end){

    if(start >= end) return;

    //取出第一个
    int x = datas[start];
    //定义两个指针初始化位置
    int low = start;
    int height = end;


    //在定义一个挪动方向
    boolean heigh2low = true;//默认高向低

    //开始判断,当没有重合时,继续挪动
    LI:while (low < height){

        if(heigh2low) {
            //首先从右侧最高位开始
            for (int i = height; i > low; i--) {//从高到低找
                if (datas[i] <= x) {//小于选中的,就取出来放到低指针位置,并挪动
                     datas[low] = datas[i];
                     low ++;//底指针挪动
                     height = i;//固定高指针

                    heigh2low = !heigh2low;
                    continue LI;//切换方向,继续
                }
            }

            //没有进去,直接低位给高位,重合
            height = low;
        }else{

            //开始从低位向高位找
            for(int i = low;i<height;i++){
                if(datas[i] >= x){//大于的话,换位置
                    datas[height] = datas[i];
                    height --;
                    low = i;

                    heigh2low = !heigh2low;
                    continue LI;
                }
            }


            low = height;
        }

    }


    //一轮完毕,把重合指针的值放回
    datas[low] = x;


    //分为的两个部分各自再执行一次

    quickSort(datas,start,low-1);//低位部分
    quickSort(datas,low+1,end);//高位部分排序
}

 

 

 

二,归并排序

     主要原理:将一组数据从中间分开,但要求分开的两组内部已经排序完毕,然后将这两组数据,从头比对,依次找出较小的放到原数据集合,直至一组比对完毕,然后剩下一组,全部放入原数据集合,从而生成一组有序的集合。

    核心步骤为合并,1,首先计算出原数据O的中间位置,然后创建两个数据组。将数据分别填充到AB组。2,从A组第一个元素开始,和B组第一个比较,如果比B组去除的小,就放到原先数据的起始位置。然后继续取A组第二个数据,比较,如果大,则把B组数据取出放到O原数据组,接下来取B组第二个,再比较。

     重复以上比较,然后比完一组为止,剩下的一组留下的数据,返回原数据组O

    这种归并,最终将数据切割成一个元素为一组,这样两辆比较,不断生成比较好顺序的组,进行再次合并。

    这种操作,类似于二叉树后序遍历。先处理好前两个部分,最后合并处理。

代码如下:

/**
 * 归并排序,
 * 思路: 将两个各自有顺序的部分合并,从而生成一个新的有序的部分,从下到上依次归并(二叉树的后序遍历)
 *
 * 有点:重复数据效率高,适合链表
 * 缺点:空间占用大
 *
 */

public void mergeSort(int[] datas,int start,int end){
    if(start == end){
        return;
    }


    int mid = (end + start)/2;
    //分成两半,继续归并
    mergeSort(datas,start,mid);
    mergeSort(datas,mid+1,end);

    merge(datas,start,mid+1,end);
}


/**
 * 合并两个有序  中间切开的,左右两部分是排好的
 * @param arrays
 * @param left
 * @param mid
 * @param right
 */
public void merge(int[] arrays,int left,int mid,int right){

    //创建两个集合,承装左右部分
    int leftSize = mid - left;
    int rightSize = right - mid + 1;

    int[] leftArray = new int[leftSize];
    int[] rightArray = new int[rightSize];


    //给两个集合填充数据
    for(int i = left;i<mid;i++){
        leftArray[i - left] = arrays[i];//左侧数据填充
    }

    for(int i = mid;i<right+1;i++){
        rightArray[i - mid] = arrays[i];
    }


    //开始比较,然后合并  左右从零开始比较,谁小,谁放到新数组,然后移动指针
    int i = 0;//左侧
    int j = 0;//右侧
    int k = left;//原数组定位的指针, 从当前位置开始填充


    while (i < leftSize && j < rightSize){//两个指针都在范围内,说明可以继续比

        if(leftArray[i] < rightArray[j]){
            arrays[k] = leftArray[i];//拿到数组
            k++;//挪动合并数组指针
            i++;//挪动左侧数据指针
        }else{
            arrays[k] = rightArray[j];
            k++;
            j++;
        }
    }


    //当有一侧被比较完毕后,把剩下的一侧数组放到原先数组
    while(i < leftSize){
          arrays[k] = leftArray[i];
          i++;
          k++;
    }

    while(j < rightSize){
          arrays[k] = rightArray[j];
          j++;
          k++;
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值