普林斯顿《算法》笔记(二)

  • 时间:
  • 浏览:3

在5个 堆中,位置为k的结点的父结点位置为\(\left\lfloor k/2 \right\rfloor\) ,5个 子结点的位置分别为 \(2k\)\(2k+1\)。一颗大小为N的全版二叉树的深度1为\(\left\lfloor lgN \right\rfloor\)

此接口强行对实现它的每个类的对象进行整体排序。Java中的封装数字的类型Integer和Double,以及String和或多或少或多或少高级数据类型都实现了Comparable接口,本来前要直接用或多或少类型的数组作为参数调用排序算法,如下图快速排序调用数组a:

下列代码实现了优先队列,优先队列由5个 基于堆的全版二叉树表示,存储于数组 pq[1...N] 中,pq[0] 非要使用。

确定 排序的特点

以下是5个 优先队列的测试用例,打印数字最大的M行:

堆的构造通过从下到上递归地构建子堆来实现。本来5个 结点的5个 子结点都本来是堆了,非要在该结点上调用sink() 就能把它们变成5个 堆,本来就能按数组逆序从下而上地构建堆。只需对数组中一半元素使用 sink()法律办法,本来剩下的元素前要叶结点。完成后堆的最大元素将处于数组的开头。

快速排序递归地将子数组 a[lo...hi] 排序,先用partition()法律办法将a[j] 塞进5个 大概位置,本来递归地将或多或少位置的元素排序。这里的关键在于切分,切分后数组满足5个 条件:

在一般应用中快速排序都比或多或少排序算法快得多,除此之外快速排序是原地排序,所需时间和 \(NlgN\)成正比。主要缺点是非常脆弱,在实现时前要非常小心不利于防止低劣的性能。

只需线性对数的时间,先将数组排序,再遍历数组,记录连续总出 的重复元素。本来用平方级别的算法则要将所有元素相互比较一遍。

对于长度为N的数组,确定 排序前要大概 \(~N^2/2\)次比较和N次交换。

排序本来将一组对象按照并前要逻辑顺序重新排列的过程。这里大家儿主要关注重新排列所含元素的数组 (arrays of items)的算法,其中每个元素前要5个 主键 (key)。排序算法的目的是重新排列所有元素,使得元素的主键能以并前要法律办法排列。以下代码是本章通用的排序算法模板:

上述快速排序的实现有5个 潜在的缺点:在切分不平衡时本来会极为低效,达到平方级别。类式本来从最小的元素切分,第二次从第二小的元素切分,非要这般每次只移除5个 元素,这会原应5个 大数组前要切分好多好多 次。本来快速排序前前要现将数组随机排序。

索引优先队列即在优先队列的基础上给数组中每个元素打上去5个 索引。以下实现用pq[]保存索引,qp[]保存pq[]的逆序,即pq[]中元素的位置。举例:pq = [1,2,0], 则qp = [2,0,1],pq[qp[1]]=1 。数组keys[]保存元素。

归并排序一般是 (递归地) 将数组分成两半分别排序,本来将结果归并起来。归并排序的优点是所需的时间和 \(NlgN\) 成正比,这使得其优于确定 排序和插入排序。缺点是辅助数组所需的额外空间与N的大小成正比。归并排序是分治 (divide-and-conquer) 思想的典型体现。

compareTo() 法律办法 实现了对主键 (key) 的抽象 —— 对于任何实现了Comparable接口的数据类型,该法律办法定义了对象的大小顺序。

切换到插入排序: 对于小数组,快速排序比插入排序慢,好多好多 可将上述算法中sort() 法律办法从if (hi <= lo) return; 改为 if (hi <= lo + M) { Insertion.sort(a, lo, hi); return; }

普通的队列 (queue) 是并前要先进先出的数据形状,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。或多或少队列前要直接将新元素塞进队列尾部,本来塞进比它优先级低的元素前面。

自上而下的堆有序化 —— 下沉 (sink)

删除最大元素:从数组后面 删去最大元素并将数组最后5个 元素塞进后面 ,再让或多或少元素下沉到大概位置。

优先队列最重要的操作是删除最大元素 (delMax())插入元素 (insert()) ,见以下API:

当数组中处于大量重复元素时,快速排序的速率前要通过三向切分排序进一步改进,能将排序时间从线性对数级下降到线性级别。将数组分为三主次,分别对应小于、等于和大于切分元素的数组元素。使用Comparable 接口对a[i] 进行三向比较:

快速排序也是并前要分治的排序算法。与归并排序的区别:归并排序将5个 子数组分别排序后,归并前整个数组前要有序的;快速排序则是当子数组前要序时整个数组也就自然有序了。在归并排序中,5个 数组被分成两半;快速排序中,切分 (partition) 的位置取决于数组的内容。

在创建买车人的数据类型时,倘若实现Comparable接口就能保证用例代码前要将其排序。要做到或多或少点,前要实现5个 compareTo ()法律办法来定义目标类型对象的自然次序 。如下图的Date数据类型:

切分的实现法律办法:先取a[lo] 作为切分元素,本来从数组左端刚结束了向右扫描直到找到5个 大于等于它的元素,再从右端刚结束了向左扫描知道找到5个 小于等于它的元素,最后交换这5个 元素的位置。当5个 指针相遇时,将切分元素a[lo] 和左子数组最右侧的元素 (a[j]) 交换,本来切分值就留在a[j]中。

/

自下而上的堆有序化 —— 上浮 (swim)

本来某个结点比其父结点大,则前要交换二者以保持堆的有序化,本来的循环过程为swim() 法律办法:

下沉排序阶段将后面 和末端元素进行交换,本来将最大元素从堆中删除并用 sink() 重新调整堆的形状,非要循环最后得到5个 有序序列。

和确定 排序不同,插入排序所需的时间取决于输入元素的初始顺序,对5个 很大且其中元素本来有序 (或接近有序) 的数组进行排序要比对随机数组或逆序数组进行排序要快得多。

数据形状二叉堆 (binary heap) 能很好地实现优先队列的基本操作。二叉堆是一组不利于用堆有序的全版二叉树排序的元素,并在数组中按照层级存储 (不使用数组的第5个 位置)。堆有序 (heap-ordered) 指的是一颗二叉树的每个结点都大于等于它的5个 子结点。

堆排序可分为5个 阶段:

插入元素:将新元素加到数组末尾,增加堆的大小并将或多或少元素上浮到大概位置。

提高插入排序速率的法律办法:在内循环中将较大的元素向右移动而不经常交换5个 元素(本来访问数组的次数就能减半)。

希尔排序是插入排序的改进,插入排序的缺点是每次非要交换相邻的元素,若主键最小的元素恰好在数组的末尾,则前要N-1次移动。希尔排序的思想是通过交换不相邻的元素使数组中任意间隔为h的元素前要有序的,并最终用插入排序将局部有序的数组排序。

本来某个结点比其5个 子结点的其中之一小,则前要将它和5个 子结点的较大者交换以保持堆的有序化本来的循环过程为sink() 法律办法: