▶ 书中第二章部分程序,加上自己补充的代码,包括各种优化的快排

 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的更多相关文章

  1. 《算法》第二章部分程序 part 5

    ▶ 书中第二章部分程序,加上自己补充的代码,包括利用优先队列进行多路归并和堆排序 ● 利用优先队列进行多路归并 package package01; import edu.princeton.cs.a ...

  2. 《算法》第二章部分程序 part 4

    ▶ 书中第二章部分程序,加上自己补充的代码,包括优先队列和索引优先队列 ● 优先队列 package package01; import java.util.Comparator; import ja ...

  3. 《算法》第二章部分程序 part 2

    ▶ 书中第二章部分程序,加上自己补充的代码,包括若干种归并排序,以及利用归并排序计算数组逆序数 ● 归并排序 package package01; import java.util.Comparato ...

  4. 《算法》第二章部分程序 part 1

    ▶ 书中第二章部分程序,加上自己补充的代码,包括插入排序,选择排序,Shell 排序 ● 插入排序 package package01; import java.util.Comparator; im ...

  5. javascript数据结构和算法 第二章 (数组) 二

    字符串表示的数组 join() 和 toString() 函数返回数组的字符串表示.这两个函数通过将数组中的元素用逗号分隔符切割,返回字符串数组表示. 这里有个样例: var names = [&qu ...

  6. 第二章--Win32程序运行原理 (部分概念及代码讲解)

    学习<Windows程序设计>记录 概念贴士: 1. 每个进程都有赋予它自己的私有地址空间.当进程内的线程运行时,该线程仅仅能够访问属于它的进程的内存,而属于其他进程的内存被屏蔽了起来,不 ...

  7. java版数据结构与算法第二章数组

    数组由一组具有相同类型的数据元素组成,并存储在一组连续存储单元中.一维数组是常量. 二维数组:若一维数组中的数据元素又是一堆数据结构,我们称之为二维数组.二维数组可以看成是n个列向量组成的线性表. 数 ...

  8. ASP.NET本质论第二章应用程序对象学习笔记1

    1.请求的处理参数—上下文对象HttpContext 1) 针对每一次请求,ASP.NET将创建一个处理这次请求所使用的HttpContext对象实例,这个对象实例将用来在ASP.NET服务器的处理过 ...

  9. 【学习总结】java数据结构和算法-第二章-数据结构和算法概述

    总目录链接 [学习总结]尚硅谷2019java数据结构和算法 github:javaDSA 目录 数据结构和算法的关系 几个实际编程中的问题 线性结构和非线性结构 数据结构和算法的关系 几个实际编程中 ...

随机推荐

  1. 持续集成之Jenkins+Gitlab实现持续集成 [二]

    持续集成之Jenkins+Gitlab实现持续集成 [二] 项目:使用git+jenkins实现持续集成 开始构建  General  源码管理 我们安装的是Git插件,还可以安装svn插件  我们将 ...

  2. RichEdit选中文字右键菜单的实现

    procedure TForm1.RichEdit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: ...

  3. BlockingQueue队列

    1.BlockingQueue定义的常用方法如下     抛出异常 特殊值 阻塞 超时 插入 add(e) offer(e) put(e) offer(e,time,unit) 移除 remove() ...

  4. [蓝桥杯]ALGO-122.算法训练_未名湖边的烦恼

    问题描述 每年冬天,北大未名湖上都是滑冰的好地方.北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩. 每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个.现在 ...

  5. 【redis】之centos6.x安装redis3.0.x

    centos6.9_x86_64 1.下载redis安装包 http://download.redis.io/releases/redis-3.2.9.tar.gz 2.解压 编译到指定得目录 mak ...

  6. Bitmap BitmapData

    var sp:Sprite=new Sprite(); sp.graphics.beginFill(0xffccdd); sp.graphics.drawRect(0,0,100,100); sp.g ...

  7. QT中实现应用程序的单例化

    一介绍 通过编写一个QSingleApplication类,来实现Qt程序的单例化,原文的作者是在Windows Vista + Qt4.4 下实现的,不过应用在其他平台上是没问题的.(本文是我在ht ...

  8. 面试总结之Google

    准备Google面试的总结 - 知乎 https://zhuanlan.zhihu.com/p/40866467 [不周山之算法与数据结构]壹 总览 | 小土刀 https://wdxtub.com/ ...

  9. 修改OBS为仅直播音频

    准备工作: 1. VS2013 的最新更新版或者VS2015 2. QT Creater 5.7   https://www.qt.io/ 3.  CMake (cmake-gui) 4. obs 依 ...

  10. PHP 多态理解

    PHP 多态   多态性是指相同的操作或函数.过程可作用于多种类型的对象上并获得不同的结果.不同的对象,收到同一消息将可以产生不同的结果,这种现象称为多态性. 多态性允许每个对象以适合自身的方式去响应 ...