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++;
}
}