《算法》第二章部分程序 part 2
▶ 书中第二章部分程序,加上自己补充的代码,包括若干种归并排序,以及利用归并排序计算数组逆序数
● 归并排序
package package01; import java.util.Comparator;
import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut; public class class01
{
private class01() {} private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) // 归并两个排好序的子数组
{
for (int k = lo; k <= hi; k++)
aux[k] = a[k]; int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid) // 后段剩余
a[k] = aux[j++];
else if (j > hi) // 前段剩余
a[k] = aux[i++];
else if (less(aux[j], aux[i])) // 比较
a[k] = aux[j++];
else
a[k] = aux[i++];
}
} private static void sortTDKernel(Comparable[] a, Comparable[] aux, int lo, int hi) // 排序递归内核
{
if (hi <= lo)
return;
int mid = lo + (hi - lo) / 2;
sortTDKernel(a, aux, lo, mid);
sortTDKernel(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
} public static void sortTD(Comparable[] a) // 自顶向下的归并排序
{
Comparable[] aux = new Comparable[a.length]; // 统一分配临时内存
sortTDKernel(a, aux, 0, a.length - 1);
} private static void indexMerge(Comparable[] a, int[] index, int[] aux, int lo, int mid, int hi) // 间接排序的归并
{
for (int k = lo; k <= hi; k++)
aux[k] = index[k]; int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid)
index[k] = aux[j++];
else if (j > hi)
index[k] = aux[i++];
else if (less(a[aux[j]], a[aux[i]]))
index[k] = aux[j++];
else
index[k] = aux[i++];
}
} private static void indexSortTDKernel(Comparable[] a, int[] index, int[] aux, int lo, int hi) // 间接排序递归内核
{
if (hi <= lo)
return;
int mid = lo + (hi - lo) / 2;
indexSortTDKernel(a, index, aux, lo, mid);
indexSortTDKernel(a, index, aux, mid + 1, hi);
indexMerge(a, index, aux, lo, mid, hi);
} public static int[] indexSortTD(Comparable[] a) // 自顶向下的间接归并排序
{
int n = a.length;
int[] aux = new int[n];
int[] index = new int[n];
for (int i = 0; i < n; i++)
index[i] = i; indexSortTDKernel(a, index, aux, 0, n - 1);
return index;
} public static void sortBU(Comparable[] a) // 自底向上的归并排序,不需要递归,合并子数组的函数与前面相同
{
int n = a.length;
Comparable[] aux = new Comparable[n];
for (int len = 1; len < n; len *= 2)
{
for (int lo = 0; lo < n - len; lo += len + len)
{
int mid = lo + len - 1;
int hi = Math.min(lo + len + len - 1, n - 1);
merge(a, aux, lo, mid, hi);
}
}
} // 改良版本
private static final int CUTOFF = 7; // 小于该尺寸的数组使用插入排序 private static void merge2(Comparable[] src, Comparable[] dst, int lo, int mid, int hi) // 区分原数组和目标数组,减少拷贝
{
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid)
dst[k] = src[j++];
else if (j > hi)
dst[k] = src[i++];
else if (less(src[j], src[i]))
dst[k] = src[j++];
else
dst[k] = src[i++];
}
} private static void sort2TDKernel(Comparable[] src, Comparable[] dst, int lo, int hi)
{
if (hi <= lo + CUTOFF) // 数据较少时使用插入排序
{
insertionSort(dst, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
sort2TDKernel(dst, src, lo, mid);
sort2TDKernel(dst, src, mid + 1, hi); if (!less(src[mid + 1], src[mid])) // src[mid+1] >= src[mid],不用归并了
System.arraycopy(src, lo, dst, lo, hi - lo + 1); // 数组拷贝,快于 for (int i = lo; i <= hi; i++) dst[i] = src[i];
else
merge2(src, dst, lo, mid, hi);
} public static void sort2TD(Comparable[] a)
{
Comparable[] aux = a.clone();
sort2TDKernel(aux, a, 0, a.length - 1);
} private static void insertionSort(Comparable[] a, int lo, int hi)
{
for (int i = lo; i <= hi; i++)
{
for (int j = i; j > lo && less(a[j], a[j - 1]); j--)
exch(a, j, j - 1);
}
} private static void exch(Comparable[] a, int i, int j) // 插入排序用到的交换
{
Comparable swap = a[i];
a[i] = a[j];
a[j] = swap;
} private static void merge2(Object[] src, Object[] dst, int lo, int mid, int hi, Comparator comparator) // 自定义类型的版本(同上 5 个函数)
{
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid)
dst[k] = src[j++];
else if (j > hi)
dst[k] = src[i++];
else if (less(src[j], src[i], comparator))
dst[k] = src[j++];
else
dst[k] = src[i++];
}
} private static void sort2TDKernel(Object[] src, Object[] dst, int lo, int hi, Comparator comparator)
{
if (hi <= lo + CUTOFF)
{
insertionSort(dst, lo, hi, comparator);
return;
} int mid = lo + (hi - lo) / 2;
sort2TDKernel(dst, src, lo, mid, comparator);
sort2TDKernel(dst, src, mid + 1, hi, comparator); if (!less(src[mid + 1], src[mid], comparator))
System.arraycopy(src, lo, dst, lo, hi - lo + 1);
else
merge2(src, dst, lo, mid, hi, comparator);
} public static void sort2TD(Object[] a, Comparator comparator)
{
Object[] aux = a.clone();
sort2TDKernel(aux, a, 0, a.length - 1, comparator);
} private static void insertionSort(Object[] a, int lo, int hi, Comparator comparator)
{
for (int i = lo; i <= hi; i++)
{
for (int j = i; j > lo && less(a[j], a[j - 1], comparator); j--)
exch(a, j, j - 1);
}
} private static void exch(Object[] a, int i, int j)
{
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
} private static boolean less(Comparable a, Comparable b) // 各排序都用到的比较函数
{
return a.compareTo(b) < 0;
} private static boolean less(Object a, Object b, Comparator comparator) // 自定义类型的比较函数
{
return comparator.compare(a, b) < 0;
} private static void show(Comparable[] a)
{
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
} public static void main(String[] args)
{
String[] a = StdIn.readAllStrings();
//int[] index = class01.indexSortTD(a); class01.sortTD(a);
//class01.sortBU(a);
//class01.sort2TD(a);
//for (int i = 0; i<a.length; i++)
// StdOut.println(index[i]);
System.out.printf("\nFinish!\n");
}
}
● 利用归并排序来计算数组的逆序数,只注释了与归并排序不一样的地方
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut; public class class01
{
private class01() {} private static long merge(int[] a, int[] aux, int lo, int mid, int hi) // 限定输入为 int 数组
{
long inversions = 0; for (int k = lo; k <= hi; k++)
aux[k] = a[k]; int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (aux[j] < aux[i]) // 算术比较
{
a[k] = aux[j++];
inversions += (mid - i + 1); // 多了一步计算
}
else
a[k] = aux[i++];
}
return inversions; // 返回逆序数
} private static long count(int[] a, int[] b, int[] aux, int lo, int hi) // 部分计数函数
{
long inversions = 0;
if (hi <= lo)
return 0;
int mid = lo + (hi - lo) / 2;
inversions += count(a, b, aux, lo, mid); // 分治和归并的部分补上计算
inversions += count(a, b, aux, mid + 1, hi);
inversions += merge(b, aux, lo, mid, hi);
return inversions;
} public static long count(int[] a) // 可调用的计数函数
{
int[] b = new int[a.length];
int[] aux = new int[a.length];
for (int i = 0; i < a.length; i++)
b[i] = a[i];
return count(a, b, aux, 0, a.length - 1);
} private static long brute(int[] a, int lo, int hi) // 枚举方法计算逆序数
{
long inversions = 0;
for (int i = lo; i <= hi; i++)
{
for (int j = i + 1; j <= hi; j++)
if (a[j] < a[i])
inversions++;
}
return inversions;
} // 自定义类型版本
private static <Key extends Comparable<Key>> long merge(Key[] a, Key[] aux, int lo, int mid, int hi)
{
long inversions = 0; for (int k = lo; k <= hi; k++)
aux[k] = a[k]; int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
{
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i])) // 比较方法改回去了
{
a[k] = aux[j++];
inversions += (mid - i + 1);
}
else
a[k] = aux[i++];
}
return inversions;
} private static <Key extends Comparable<Key>> boolean less(Key v, Key w)
{
return (v.compareTo(w) < 0);
} private static <Key extends Comparable<Key>> long count(Key[] a, Key[] b, Key[] aux, int lo, int hi)
{
long inversions = 0;
if (hi <= lo)
return 0;
int mid = lo + (hi - lo) / 2;
inversions += count(a, b, aux, lo, mid);
inversions += count(a, b, aux, mid + 1, hi);
inversions += merge(b, aux, lo, mid, hi);
return inversions;
} public static <Key extends Comparable<Key>> long count(Key[] a)
{
Key[] b = a.clone();
Key[] aux = a.clone();
return count(a, b, aux, 0, a.length - 1);
} private static <Key extends Comparable<Key>> long brute(Key[] a, int lo, int hi)
{
long inversions = 0;
for (int i = lo; i <= hi; i++)
{
for (int j = i + 1; j <= hi; j++)
if (less(a[j], a[i]))
inversions++;
}
return inversions;
} public static void main(String[] args) // 使用文件名而不是重定向来作为输入
{
In in = new In(args[0]);
int[] a = in.readAllInts();
int n = a.length;
int[] b = new int[n];
for (int i = 0; i<n; i++)
b[i] = a[i]; StdOut.println(class01.count(a));
StdOut.println(class01.count(b));
}
}
《算法》第二章部分程序 part 2的更多相关文章
- 《算法》第二章部分程序 part 5
▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...
- 《算法》第二章部分程序 part 4
▶ 书中第二章部分程序,加上自己补充的代码,包括优先队列和索引优先队列 ● 优先队列 package package01; import java.util.Comparator; import ja ...
- 《算法》第二章部分程序 part 3
▶ 书中第二章部分程序,加上自己补充的代码,包括各种优化的快排 package package01; import edu.princeton.cs.algs4.In; import edu.prin ...
- 《算法》第二章部分程序 part 1
▶ 书中第二章部分程序,加上自己补充的代码,包括插入排序,选择排序,Shell 排序 ● 插入排序 package package01; import java.util.Comparator; im ...
- javascript数据结构和算法 第二章 (数组) 二
字符串表示的数组 join() 和 toString() 函数返回数组的字符串表示.这两个函数通过将数组中的元素用逗号分隔符切割,返回字符串数组表示. 这里有个样例: var names = [&qu ...
- 第二章--Win32程序运行原理 (部分概念及代码讲解)
学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...
- java版数据结构与算法第二章数组
数组由一组具有相同类型的数据元素组成,并存储在一组连续存储单元中.一维数组是常量. 二维数组:若一维数组中的数据元素又是一堆数据结构,我们称之为二维数组.二维数组可以看成是n个列向量组成的线性表. 数 ...
- ASP.NET本质论第二章应用程序对象学习笔记1
1.请求的处理参数—上下文对象HttpContext 1) 针对每一次请求,ASP.NET将创建一个处理这次请求所使用的HttpContext对象实例,这个对象实例将用来在ASP.NET服务器的处理过 ...
- 【学习总结】java数据结构和算法-第二章-数据结构和算法概述
总目录链接 [学习总结]尚硅谷2019java数据结构和算法 github:javaDSA 目录 数据结构和算法的关系 几个实际编程中的问题 线性结构和非线性结构 数据结构和算法的关系 几个实际编程中 ...
随机推荐
- 协程与多路io复用epool关系
linux上其实底层都基于libevent.so模块实现的,所以本质一样 gevent更关注于io和其它 epool只是遇到io就切换,而gevent其它等待也切换
- VarIsOrdinal,VarIsFloat,VarIsNumeric判断数字
VarIsOrdinal VarIsFloat VarIsNumeric 就三个. 第一个 是否int,boolean 第二个 是否Double,Simple,Curren ...
- 黄聪:3分钟学会sessionStorage用法
前言: 因最近移动端开发过程中遇到一个运营提出的所谓技术难点需求,对于原生APP来说轻而易举,毕竟自己的APP用户操作指哪打哪,但是H5该怎么做?H5就实现不了么?对于一个爱研究攻克这些前端棘手问题的 ...
- 黄聪:浓缩的才是精华:浅析GIF格式图片的存储和压缩(转)
http://www.cnblogs.com/qcloud1001/p/6647080.html 成文迪, 在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦. GIF格式 ...
- 【剑指offer】二进制中1的个数
输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路:将原数字与1按位进行与操作. public class Solution { public int NumberOf1(int ...
- bzoj 4866: [Ynoi2017]由乃的商场之旅
设第i个字母的权值为1<<i,则一个可重集合可以重排为回文串,当且仅当这个集合的异或和x满足x==x&-x,用莫队维护区间内有多少对异或前缀和,异或后满足x==x&-x,这 ...
- js 取一个对象的长度,取出来的是undefined,自己写的一个计算长度的函数解决了。
收藏 牙膏儿 发表于 3年前 阅读 13085 收藏 7 点赞 1 评论 1 [粉丝福利]-<web 前端基础到实战系列课程>免费在线直播教学>>> 昨晚写一段代码, ...
- 记录Linux下解压大文件时的一次奇葩经历
由于需要安装新的系统,使用wget下载tar包,8G多下载了1个多小时.解压完后的文件夹中的tar包数量明显不够,而且原始的tar包竟然不见了. 同样的操作又进行了一次,结果还是一样,不得不怀疑是哪里 ...
- go中的make和new的区别
适用范围:make 只能创建内建类型(slice map channel), new 则是可以对所有类型进行内存分配 返回值: new 返回指针, make 返回引用 填充值: new 填充零值, m ...
- es5中类与继承
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name ...