归并排序——史上最详细图解教程!!!
2017-11-29 23:57
519 查看
题目大意:把n个数,分成若干份,然后每一份暴力排序一下,然后递归地合起来。
为什么要这样做?这样有个球用?
核心问题就在于,每两份之间你是怎么合起来的。
我们举个例子。
一个比较呆萌的思路就是,2二分插入,形成新的序列,再继续用4插入。。。这样的话,确实没什么球用。
正确思路:2在1,3中插入以后,记下插入的这个位置的index,然后下一次,就是从3开始找了,然后4插入,接下来就是从5开始找了。。。请注意哦,这个情况已经是最复杂的情况了哦!另外两种极端情况,找遍前面的数组都没找到,直接在最后一股脑加进去。或者说后面的数都没前面的某个数大,一股脑加进去。这两种极端情况的时间复杂度是O(N)。中间会略高一点点,最差情况O(n^2),注意,是所有数都插进去的时间复杂度,可以说是很快很快很快了。
弄明白上面归并排序的核心思想,就可以开始动手了!
接下来我们需要分析总体的思路。
1.拆分n个数为根号n组(后面会分析具体怎么分成这个奇怪的份数的)
2.给每个组进行排序
3.把当前所有的组,两两合并,如果发现合并以后,剩下的组数并没有达到1,那就继续合并,直到合并到1组为止。
4.合并的过程又要细化,要单独写一个两两合并的方法。
5.两两合并的过程又要细化,要单独写一个方法:单个值并到数组中,返回一个标记,下次从这个标记开始找。
所以我们用伪代码串联起这个程序
// TODO: 2017/11/29 把一个数组分成根号n份 // TODO: 2017/11/29 把每一份都排好序,存入List<List<Integer>>中(二维数组也可以) // TODO: 2017/11/29 写一个递归方法,功能是把List<List<Integer>>中的List合并成一个 // TODO: 2017/11/29 写一个两个List<Integer>之间合并的方法,后一个List<Integer>赋给前一个 // TODO: 2017/11/29 写一个一个值在List<Integer>插入的方法,返回index记录下次开始的位置
这样才是正确的做法,上来就想啃掉这整个流程肯定不现实,把他拆分成一个个模块,不仅容易成功,出现bug的时候还便于debug。
拆分这个数组成根号n段
其思维是这样的,假设是5,根号5向下取整就是2,那就要分成两份,一份2,一份3。
再举一个11的例子,根号11向下取整就是3,那就要分成3份,3,3,5。
int len = a.length;//数组长度 int k = (int) Math.sqrt(len);//根号后向下取整的值,即份数 int num = a.length / k;//把最后一份区别对待,前面每份的个数
把每一份都排好序赋值(挺复杂的,需要好好理解下的 )
for (int row = 0; row < k - 1; row ++) {//0 1 List<Integer> list = new ArrayList<>(); for (int col = 0; col < num; col ++) { //index 是你开始点,其实就是col的变形 int index = row * num + col;//0 1 2//3 4 5 int min = Integer.MAX_VALUE; int q = -1; for (int i = index; i < row * num + num; i ++) { if (a[i] < min) { min = a[i]; q = i; } } if (q != -1) { int t = a[q]; a[q] = a[index]; a[index] = t; } list.add(a[index]); } this.list.add(list); }
递归,奇数组跟偶数组区别对待。每两组一起处理,后一组加到前一组中,删除后一组。直到合并到只有一组。
void circle(List<List<Integer>> list) { int size = list.size();//取得你有几组数 if (size == 1) { return; } if (size % 2 == 0) {//如果你有2 4 6组数,假设4组 for (int i = 0; i < size; i += 2) {//2组并到1组中,4组并到3组中 gather(list.get(i), list.get(i + 1)); } int f = 0; for (int i = 1; i < size; i += 2) {//把1 3 两组删除 list.remove(i - f); f ++; } } else {//如果你有5组数 int newSize = size - 1;//只处理前4组数 for (int i = 0; i < newSize; i += 2) { gather(list.get(i), list.get(i + 1));//2组并到1组中,4组并到3组中 } int f = 0; for (int i = 1; i < newSize; i += 2) {//把1 3 两组删除 list.remove(i); f ++; } } circle(list); }
把后面的并到前面的集合中,把后面的集合每个点都插入进去,更新搜索区间
//把b并到a中 void gather(List<Integer> a, List<Integer> b) { int aSize = a.size(); int bSize = b.size(); int start = 0;//取得a的第一个 int end = aSize - 1;//取得a的最后一个 for (int i = 0; i < bSize; i ++) { int num = b.get(i);//把每个b取一遍 start = insert(a, start, end, num);//把这个数在a的可插区间内插入,把插入点返回 end = a.size() - 1;//插入成功,结束点也要变化了 //如果循环完了也没找到大于等于n的数,上面已经是插入在最后了 if (start == 999) { if (i + 1 < bSize) {//看看下一个数是否还在区间内 for (int j = i + 1; j < bSize; j ++) {//如果在 a.add(b.get(j));//全加进去 } } break; } } }
如果找得到那就返回index,找不到就一股脑全加在后面
int insert(List<Integer> k, int start, int end, int n) { for (int i = start; i <= end; i ++) {//给定搜索区间 int num = k.get(i);//取得a中的值 if (n <= num) { k.add(i, n); return i; } } k.add(n); return 999; }
全部代码
import java.util.ArrayList;
import java.util.List;
public class Test {
List<List<Integer>> list = new ArrayList<>();
Test(int[] a) {
int len = a.length;//11
int k = (int) Math.sqrt(len);//3
int num = a.length / k;//3
for (int row = 0; row < k - 1; row ++) {//0 1 List<Integer> list = new ArrayList<>(); for (int col = 0; col < num; col ++) { //index 是你开始点,其实就是col的变形 int index = row * num + col;//0 1 2//3 4 5 int min = Integer.MAX_VALUE; int q = -1; for (int i = index; i < row * num + num; i ++) { if (a[i] < min) { min = a[i]; q = i; } } if (q != -1) { int t = a[q]; a[q] = a[index]; a[index] = t; } list.add(a[index]); } this.list.add(list); }
List<Integer> list = new ArrayList<>();
for (int i = (k - 1) * num; i < len; i ++) {
int min = Integer.MAX_VALUE;
int index = -1;
for (int j = i; j < len; j ++) {
if (a[j] < min) {
min = a[j];
index = j;
}
}
if (index != -1) {
int t = a[i];
a[i] = a[index];
a[index] = t;
}
list.add(a[i]);
}
this.list.add(list);
circle(this.list);
for (int i = 0; i < this.list.size(); i ++) {
List<Integer> mList = this.list.get(i);
for (int j = 0; j < mList.size(); j ++) {
System.out.print(mList.get(j) + " ");
}
System.out.println();
}
}
// TODO: 2017/11/29 心得 可以写的蠢一点 性能差一点 后期再优化
void circle(List<List<Integer>> list) { int size = list.size();//取得你有几组数 if (size == 1) { return; } if (size % 2 == 0) {//如果你有2 4 6组数,假设4组 for (int i = 0; i < size; i += 2) {//2组并到1组中,4组并到3组中 gather(list.get(i), list.get(i + 1)); } int f = 0; for (int i = 1; i < size; i += 2) {//把1 3 两组删除 list.remove(i - f); f ++; } } else {//如果你有5组数 int newSize = size - 1;//只处理前4组数 for (int i = 0; i < newSize; i += 2) { gather(list.get(i), list.get(i + 1));//2组并到1组中,4组并到3组中 } int f = 0; for (int i = 1; i < newSize; i += 2) {//把1 3 两组删除 list.remove(i); f ++; } } circle(list); }
//把b并到a中 void gather(List<Integer> a, List<Integer> b) { int aSize = a.size(); int bSize = b.size(); int start = 0;//取得a的第一个 int end = aSize - 1;//取得a的最后一个 for (int i = 0; i < bSize; i ++) { int num = b.get(i);//把每个b取一遍 start = insert(a, start, end, num);//把这个数在a的可插区间内插入,把插入点返回 end = a.size() - 1;//插入成功,结束点也要变化了 //如果循环完了也没找到大于等于n的数,上面已经是插入在最后了 if (start == 999) { if (i + 1 < bSize) {//看看下一个数是否还在区间内 for (int j = i + 1; j < bSize; j ++) {//如果在 a.add(b.get(j));//全加进去 } } break; } } }
int insert(List<Integer> k, int start, int end, int n) { for (int i = start; i <= end; i ++) {//给定搜索区间 int num = k.get(i);//取得a中的值 if (n <= num) { k.add(i, n); return i; } } k.add(n); return 999; }
public static void main(String[] args) throws Exception {
int[] a = new int[17];
a[0] = 7;
a[1] = 10;
a[2] = 8;
a[3] = 11;
a[4] = 50;
a[5] = 9;
a[6] = 6;
a[7] = 70;
a[8] = 1;
a[9] = 4;
a[10] = 34;
a[11] = 34;
a[12] = 4;
a[13] = 4;
a[14] = 4;
a[15] = 4;
a[16] = 4;
Test test = new Test(a);
}
}
// TODO: 2017/11/29 把一个数组分成根号n份
// TODO: 2017/11/29 把每一份都排好序,存入List<List<Integer>>中(二维数组也可以)
// TODO: 2017/11/29 写一个递归方法,功能是把List<List<Integer>>中的List合并成一个
// TODO: 2017/11/29 写一个两个List<Integer>之间合并的方法,后一个List<Integer>赋给前一个
// TODO: 2017/11/29 写一个一个值在List<Integer>插入的方法,返回index记录下次开始的位置
相关文章推荐
- U大师U盘启动盘制作教程 详细图解步骤教你怎么装统(Win7PE精简版)
- Linux下安装Nginx详细图解教程
- Linux下安装Nginx详细图解教程
- U盘安装CentOS 6.2(超级详细图解教程)
- CentOS 6.5系统安装配置图解教程(详细图文)
- 虚拟机安装详细图解教程及使用教程
- Linux 安装Nginx详细图解教程
- Linux下安装Nginx详细图解教程 (nginx-1.2.6)
- Linux下安装Nginx详细图解教程
- 详细的obs操作教程(windows/mac)图解
- Samsung Galaxy Golden( Folder) SHV-E400S/K刷机(线刷)教程详细图解
- CentOS 6.5系统安装配置图解教程(详细图文)
- CentOS 6.3安装(详细图解教程)
- “mac os + intellij idea 13 + nodejs开发环境搭建教程(附详细图解)
- RHEL 6.3安装(超级详细图解教程)
- IBM服务器系统安装傻瓜教程(详细图解39张)
- [Linux CentOS6.2] CentOS 6.2 详细安装教程(全图解,转载)
- MHDD详细图解教程一
- U盘安装centos 6.3教程(超级详细图解教程)
- CentOS 6.3安装(详细图解教程)