《算法》第二章部分程序 part 3
▶ 书中第二章部分程序,加上自己补充的代码,包括各种优化的快排
package package01; import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom; public class class01
{
private class01() {} public static void sort1(Comparable[] a) // 基本的快排
{
StdRandom.shuffle(a); // 数组 a 随机化
sortKernel1(a, 0, a.length - 1);
} private static void sortKernel1(Comparable[] a, int lo, int hi) // 快排递归内核
{
if (hi <= lo)
return;
int j = partition1(a, lo, hi);
sortKernel1(a, lo, j - 1); // 用不到 sortKernel1 的返回值
sortKernel1(a, j + 1, hi);
} private static int partition1(Comparable[] a, int lo, int hi) // 快排分组
{
int i = lo, j = hi + 1;
for (Comparable v = a[lo];; exch(a, i, j)) // a[lo] 作为分界值,每次循环前要调整 i 和 j,所以初值要过量
{ // 没有 break 退出时调整两个错位的值
for (i++; i < hi && less(a[i], v); i++); // 寻找大于 a[lo] 的元素
for (j--; j > lo && less(v, a[j]); j--); // 寻找小于 a[lo] 的元素
if (i >= j) // 指针交叉,说明已经排好了
break;
}
exch(a, lo, j); // 安置哨兵
return j; // 返回哨兵的位置
} public static Comparable select(Comparable[] a, int k) // 从数组中选出第 k 小的数字
{
StdRandom.shuffle(a);
int lo = 0, hi = a.length - 1;
for (; lo < hi;)
{
int i = partition1(a, lo, hi); // 用 a[0] 分段,这是函数 partition 返回安置位置的原因
if (i > k) // 左段过长
hi = i - 1;
else if (i < k) // 右段过长
lo = i + 1;
else
return a[i]; // 找到了指定的分位数
}
return a[lo]; // 指针交叉或 k 不合法?
} public static void sort2(Comparable[] a) // 优化版本,三路快排
{
StdRandom.shuffle(a);
sortKernel2(a, 0, a.length - 1);
} private static void sortKernel2(Comparable[] a, int lo, int hi) // 三路路快排的递归内核,没有 partition 程序
{
if (hi <= lo)
return;
int pLess = lo, pEqual = lo + 1, pGreater = hi; // 三个指针分别指向小于部分、等于部分、大于部分,且 pEqual 指向当前检查元素
for (Comparable v = a[lo]; pEqual <= pGreater;)
{
int cmp = a[pEqual].compareTo(v);
if (cmp < 0)
exch(a, pLess++, pEqual++); // 当前元素较小,放到左边
else if (cmp > 0)
exch(a, pEqual, pGreater--); // 当前元素较大,放到右边
else
pEqual++; // 当前元素等于哨兵,仅自增相等部分的指针
}
sortKernel2(a, lo, pLess - 1);
sortKernel2(a, pGreater + 1, hi);
} private static final int INSERTION_SORT_CUTOFF = 8; // 数组规模不大于 8 时使用插入排序 public static void sort3(Comparable[] a) // 优化版本,三采样 - 插入排序
{
sortKernel3(a, 0, a.length - 1); // 没有数组随机化
} private static void sortKernel3(Comparable[] a, int lo, int hi) // 递归内核
{
if (hi <= lo)
return;
if (hi - lo + 1 <= INSERTION_SORT_CUTOFF) // 规模较小,使用插入排序
{
insertionSort(a, lo, hi + 1); //Insertion.sort(a, lo, hi + 1);,教材源码使用 edu.princeton.cs.algs4.Insertion
return;
}
int j = partition3(a, lo, hi);
sortKernel3(a, lo, j - 1);
sortKernel3(a, j + 1, hi);
} private static int partition3(Comparable[] a, int lo, int hi) // 分组
{
exch(a, median3(a, lo, lo + (hi - lo + 1) / 2, hi), lo); // 开头、中间和结尾各找 1 元求中位数作为哨兵 int i = lo, j = hi + 1;
Comparable v = a[lo]; // 注意相比函数 partition1 少了一层循环 for (i++; less(a[i], v); i++)
{
if (i == hi) // 低指针到达右端,所有元素都比哨兵小
{
exch(a, lo, hi); // 哨兵放到最后,返回哨兵位置
return hi;
}
}
for (j--; less(v, a[j]); j--)
{
if (j == lo + 1) // 高指针到达左端,所有元素都比哨兵大
return lo; // 直接返回原哨兵位置
}
for (; i < j;) // 在低指针和高指针之间分组
{
exch(a, i, j);
for (i++; less(a[i], v); i++);
for (j--; less(v, a[j]); j--);
}
exch(a, lo, j);
return j;
} private static final int MEDIAN_OF_3_CUTOFF = 40; // 数组规模较小时不采用三采样 public static void sort4(Comparable[] a) // 优化版本,使用
{
sortKernel4(a, 0, a.length - 1);
} private static void sortKernel4(Comparable[] a, int lo, int hi) // 递归内核
{
int n = hi - lo + 1;
if (n <= INSERTION_SORT_CUTOFF) // 小规模数组用插入排序
{
insertionSort(a, lo, hi + 1);
return;
}
else if (n <= MEDIAN_OF_3_CUTOFF) // 中规模数组用三采样
exch(a, median3(a, lo, lo + n / 2, hi), lo);
else // 大规模数组使用 Tukey ninther 采样,相当于 9 点采样
{
int eps = n / 8, mid = lo + n / 2;
int ninther = median3(a,
median3(a, lo, lo + eps, lo + eps + eps),
median3(a, mid - eps, mid, mid + eps),
median3(a, hi - eps - eps, hi - eps, hi)
);
exch(a, ninther, lo);
} int i = lo, p = lo, j = hi+1, q = hi + 1; // Bentley-McIlroy 三向分组
for (Comparable v = a[lo]; ;)
{
for (i++; i < hi && less(a[i], v); i++);
for (j--; j > lo && less(v, a[j]); j--); if (i == j && eq(a[i], v)) // 指针交叉,单独处理相等的情况
exch(a, ++p, i);
if (i >= j)
break; exch(a, i, j);
if (eq(a[i], v))
exch(a, ++p, i);
if (eq(a[j], v))
exch(a, --q, j);
}
i = j + 1;
for (int k = lo; k <= p; k++)
exch(a, k, j--);
for (int k = hi; k >= q; k--)
exch(a, k, i++);
sortKernel4(a, lo, j);
sortKernel4(a, i, hi);
} 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 int median3(Comparable[] a, int i, int j, int k) // 计算 3 元素的中位数
{
return less(a[i], a[j]) ?
(less(a[j], a[k]) ? j : less(a[i], a[k]) ? k : i) :
(less(a[k], a[j]) ? j : less(a[k], a[i]) ? k : i);
} private static boolean less(Comparable v, Comparable w)
{
if (v == w) // 相同引用时避免内存访问
return false;
return v.compareTo(w) < 0;
} private static boolean eq(Comparable v, Comparable w)
{
if (v == w) // 相同引用时避免内存访问
return true;
return v.compareTo(w) == 0;
} private static void exch(Object[] a, int i, int j)
{
Object swap = a[i];
a[i] = a[j];
a[j] = swap;
} private static void show(Comparable[] a)
{
for (int i = 0; i < a.length; i++)
StdOut.println(a[i]);
} public static void main(String[] args)
{
In in = new In(args[0]);
String[] a = in.readAllStrings(); class01.sort1(a);
//class01.sort2(a);
//class01.sort3(a);
//class01.sort4(a);
show(a);
/*
for (int i = 0; i < a.length; i++) // 使用函数 select 进行排序,实际上是逐步挑出 a 中排第 1、第 2、第 3 …… 的元素
{
String ith = (String)class01.select(a, i);
StdOut.println(ith);
}
*/
}
}
《算法》第二章部分程序 part 3的更多相关文章
- 《算法》第二章部分程序 part 5
▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...
- 《算法》第二章部分程序 part 4
▶ 书中第二章部分程序,加上自己补充的代码,包括优先队列和索引优先队列 ● 优先队列 package package01; import java.util.Comparator; import ja ...
- 《算法》第二章部分程序 part 2
▶ 书中第二章部分程序,加上自己补充的代码,包括若干种归并排序,以及利用归并排序计算数组逆序数 ● 归并排序 package package01; import java.util.Comparato ...
- 《算法》第二章部分程序 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 目录 数据结构和算法的关系 几个实际编程中的问题 线性结构和非线性结构 数据结构和算法的关系 几个实际编程中 ...
随机推荐
- excel技巧--单元格合并与拆分
如果要将上图的地区列做成下图的合并单一列: 有如下做法: (以下图表格为例) 1.选择要排序的表格,点击“开始”-->排序和筛选-->自定义排序.在对话框选择“业务项目”进行排序: 2.选 ...
- Intellij IDEA神器值得收藏的小技巧
概述 Intellij IDEA真是越用越觉得它强大,它总是在我们写代码的时候,不时给我们来个小惊喜.出于对Intellij IDEA的喜爱,我决定写一个与其相关的专栏或者系列,把一些好用的Intel ...
- python-selenium并发执行测试用例(方法一 各模块每一条并发执行)
总执行代码: # coding=utf-8import unittest,os,timeimport HTMLTestRunnerimport threadingimport syssys.path. ...
- 弹性势能,position,min用法,获取元素的宽
弹性势能: 网页div移动的mousemove的次数,跟div移动的距离没有关系,跟鼠标移动的快慢有关,浏览器自身有个计数事件,几毫秒 _this.seed*=0.95 //摩擦系数的写法 posit ...
- Selector空轮询处理(转载)
https://www.cnblogs.com/my_life/articles/5556939.html Selector空轮询处理 在NIO中通过Selector的轮询当前是否有IO事件,根据JD ...
- tar -zxvf中的z是个什么鬼
习惯了tar -czvf和tar -zxvf.今天在本地用压缩软件7z打了一个tar包,在docker容器中用tar -zxvf怎么都解压不了,报错gzip: stdin: not in gzip f ...
- 关于微信小程序如何解决多层循环嵌套
http://www.jianshu.com/p/87cdf985b2b9 附加:小程序里面一些功能示例 http://blog.csdn.net/column/details/13721.html
- [UE4]把枪打飞addImpulse
一.武器如果没有开启模拟物理,会漂浮在空中 二.武器开启模拟物理,运行游戏的时候就会掉到地上了.之所以要加“Delay”延迟,是因为创建武器在先(没有持有人),持有武器动作在后,加上“delay”延迟 ...
- php安装imagemagick扩展 常见问题与解决方案(win平台)
1.写在前面 1-1.ImageMagick介绍 ImageMagick是一套功能强大.稳定而且开源的工具集和开发包,可以用来读.写和处理超过89种基本格式的图片文件,包括流行的TIFF.JPEG.G ...
- Mybatis 系列10-结合源码解析mybatis 的执行流程
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...