??xml version="1.0" encoding="utf-8" standalone="yes"?>
1?堆排序定?br> n个关键字序列KlQK2Q?#8230;QKnUCؓ堆,当且仅当该序列满_下性质(UCؓ堆性质)Q?br> (1) ki≤K2i且ki≤K2i+1 ?2)Ki≥K2i且ki≥K2i+1(1≤i≤ )
若将此序列所存储的向量R[1..n]看做是一完全二叉树的存储结构,则堆实质上是满如下性质的完全二叉树Q树中Q一非叶l点的关键字均不大于(或不于)其左叛_?若存?l点的关键字?br>【例】关键字序列(10Q?5Q?6Q?5Q?0Q?0)?70Q?6Q?0Q?5Q?5Q?0)分别满堆性质(1)?2)
2、大根堆和小根堆
根结?亦称为堆?的关键字是堆里所有结点关键字中最者的堆称为小根堆?br> 根结?亦称为堆?的关键字是堆里所有结点关键字中最大者,UCؓ大根堆?br> 注意Q?br> ①堆中Q一子树亦是堆?br> ②以上讨论的堆实际上是二叉堆(Binary Heap)Q类似地可定义k叉堆?/p>
3、堆排序特点
堆排?HeapSort)是一树Ş选择排序?br> 堆排序的特点是:在排序过E中Q将R[l..n]看成是一完全二叉树的顺序存储结构,利用完全二叉树中双亲l点和孩子结点之间的内在关系【参见二叉树的顺序存储结构】,在当前无序区中选择关键字最?或最?的记录?/p>
4、堆排序与直接插入排序的区别
直接选择排序中,Z从R[1..n]中选出关键字最的记录Q必进行n-1ơ比较,然后在R[2..n]中选出关键字最的记录Q又需要做n-2?比较。事实上Q后面的n-2ơ比较中Q有许多比较可能在前面的n-1ơ比较中已经做过Q但׃前一排序时未保留这些比较结果,所以后一排序时又重复执 行了q些比较操作?br> 堆排序可通过树Şl构保存部分比较l果Q可减少比较ơ数?/p>
5、堆排序
堆排序利用了大根?或小根堆)堆顶记录的关键字最?或最?q一特征Q得在当前无序Z选取最?或最?关键字的记录变得单?/p>
Q?Q用大根堆排序的基本思想
?先将初始文gR[1..n]建成一个大根堆Q此堆ؓ初始的无序区
?再将关键字最大的记录R[1](卛_?和无序区的最后一个记录R[n]交换Q由此得到新的无序区R[1..n-1]和有序区R[n]Q且满R[1..n-1].keys≤R[n].key
?׃交换后新的根R[1]可能q反堆性质Q故应将当前无序区R[1..n-1]调整为堆。然后再ơ将R[1..n-1]中关键字最大的记录R[1]和该?间的最后一个记录R[n-1]交换Q由此得到新的无序区R[1..n-2]和有序区R[n-1..n]Q且仍满_pR[1..n-2].keys≤R [n-1..n].keysQ同栯R[1..n-2]调整为堆?br> ……
直到无序区只有一个元素ؓ止?/p>
Q?Q大根堆排序法的基本操作:
?初始化操作:R[1..n]构造ؓ初始堆;
?每一排序的基本操作Q将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后新的无序区调整为堆(亦称重徏??br> 注意Q?br>①只需做n-1排序,选出较大的n-1个关键字卛_以得文仉增有序?br>②用根堆排序与利用大根堆类|只不q其排序l果是递减有序的。堆排序和直接选择排序相反Q在M时刻Q堆排序中无序区L在有序区之前Q且有序区是在原向量的尾部由后往前逐步扩大x个向量ؓ止?/p>
Q?Q堆排序的算法:
void HeapSort(SeqIAst R)
{ //对R[1..n]q行堆排序,不妨用R[0]做暂存单?br> int iQ?br> BuildHeap(R)Q?//R[1-n]建成初始?br> for(i=n;i>1Qi--){ //对当前无序区R[1..i]q行堆排序,共做n-1?br> R[0]=R[1]QR[1]=R[i];R[i]=R[0]Q?//堆和堆中最后一个记录交?br> Heapify(RQ?Qi-1)Q?//R[1..i-1]重新调整为堆Q仅有R[1]可能q反堆性质
} //endfor
} //HeapSort
Q?Q?BuildHeap和Heapify函数的实?br> 因ؓ构造初始堆必须使用到调整堆的操作,先讨论Heapify的实现?br>?Heapify函数思想Ҏ
每趟排序开始前R[l..i]是以R[1]为根的堆Q在R[1]与R[i]交换后,新的无序区R[1..i-1]中只有R[1]的值发生了变化Q故除R [1]可能q反堆性质外,其余Ml点为根的子树均是堆。因此,当被调整区间是R[low..high]Ӟ只须调整以R[low]为根的树卛_?br>"{选法"调整?br> R[low]的左、右子树(若存?均已是堆Q这两棵子树的根R[2low]和R[2low+1]分别是各自子树中关键字最大的l点。若R[low]. key不小于这两个孩子l点的关键字Q则R[low]未违反堆性质Q以R[low]为根的树已是堆,无须调整Q否则必dR[low]和它的两个孩子结?中关键字较大者进行交换,即R[low]与R[large](R[large].key=max(R[2low].keyQR[2low+1]. key))交换。交换后又可能ɾl点R[large]q反堆性质Q同L于该l点的两子?若存?仍然是堆Q故可重复上q的调整q程Q对以R [large]为根的树q行调整。此q程直至当前被调整的l点已满_性质Q或者该l点已是叶子为止。上q过E就象过{子一P把较的关键字逐层{下 去,而将较大的关键字逐层选上来。因此,有h此ҎUCؓ"{选法"?br> 具体的算法【参见教材?/p>
②BuildHeap的实?br> 要将初始文gR[l..n]调整Z个大根堆Q就必须它所对应的完全二叉树中以每一l点为根的子树都调整为堆?br> 昄只有一个结点的树是堆,而在完全二叉树中Q所有序?的结炚w是叶子,因此以这些结点ؓ根的子树均已是堆。这P我们只需依次以序号?Q?nbsp; -1Q?#8230;Q?的结点作为根的子树都调整为堆卛_?br> 具体法【参见教材】?/p>
5、大堆排序实现
void HeapSort ( Elem R[], int n ) ...{
// 对记录序列R[1..n]q行堆排序?br>for ( i=n/2; i>0; --i )
// 把R[1..n]建成大顶?br> HeapAdjust ( R, i, n );
for ( i=n; i>1; --i ) ...{
R[1]←→R;
// 堆记录和当前未经排序子序?br> // R[1..i]中最后一个记录相互交?br>HeapAdjust(R, 1, i-1);
// R[1..i-1] 重新调整为大堆
}
} // HeapSort
其中{选的法如下所C。ؓR[s..m]调整?#8220;大顶?#8221;Q算法中“{?#8221;应沿关键字较大的孩子l点向下q行?br>void HeapAdjust (Elem R[], int s, int m) ...{
// 已知R[s..m]中记录的关键字除R[s].key?br>// 外均满堆的定义Q本函数调整R[s] 的关
// 键字QR[s..m]成ؓ一个大堆Q对其中
// 记录的关键字而言Q?br>rc = R[s];
for ( j=2*s; j<=m; j*=2 ) ...{// 沿key较大的孩子结点向下筛?br> if ( j<m && R[j].key<R[j+1].key) ++j;
if ( rc.key >= R[j].key ) break; // rc应插入在位置s?br> R[s] = R[j]; s = j;
}
R[s] = rc; // 插入
} // HeapAdjust
6?法分析
堆排序的旉Q主要由建立初始堆和反复重徏堆这两部分的旉开销构成Q它们均是通过调用Heapify实现的?br> 堆排序的最坏时间复杂度为O(nlgn)。堆排序的^均性能较接q于最坏性能?br> ׃建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件?br> 堆排序是地排序Q辅助空间ؓO(1)Q?br> 它是不稳定的排序Ҏ?/p>
判断点与U段的包含关p,也就是判断点与线的最短距L否位于容差范围内。造型中常用的U段有三U:
Q?Q直U段Q(2Q圆锥曲U段Q主要是圆弧Q,Q?Q参数曲U(主要是BezierQBh与NURBS曲线Q。点与面的包含判定也cM地分ZU情c下面分别讨论?/font>
1、点与直U段的包含判?/font>
假设点坐标ؓP(x, y, z)Q直U段端点为P1(x1, y1, z1)QP2(x2,y2,z2)Q则点P到线DP1P2的距ȝqx?/font>
d2=(x-x1)2+(y-y1)2+(z-z1)2-[(x2-x1)(x-x1)+(y2-y1)(y-y1)+(z2-z1)(z-z1)]2/[(x2-x1)2+(y2-y1)2+(z2-z1)2]
当d2<a2Ӟ认ؓ点在U段Q或其g长线Q上Q这时还需q一步判断点是否落在直线D늚有效区间内。只要对坐标分量q行比较Q假讄D两端点的x分量不等Q否则所有分量均相等Q那么线D两端点重合Q线D退化ؓ一点)Q那么当x-x1与x-x2反号Ӟ点P在线D늚有效区间内?/font>
2、点与圆锥曲U段的包含判?/font>
以圆弧ؓ例,假设点的坐标?x, y, z)Q圆弧的中心为(x0, y0, z0Q,半径为rQv始角a1Q终止角a2。这些角度都是相对于局部坐标系x轴而言。圆弧所在^面ؓ
ax+by+cz+d=0
先判断点是否在该q面上,若不在,则点不可能被包含。若在,则通过坐标变换Q把问题转换Cl的问题?/font>
l定中心为(x0, y0Q,半径为rQv始角a1Q终止角a2的圆弧,对^面上一点PQx, yQ,判断P是否在圆弧上Q可分二步进行。第一步判断P是否在圆心ؓQx0, y0Q,半径为r的圆的圆周上Q即下式是否成立Q?/font>
W二步判断P是否在有效的圆弧D内?/font>
3、点与参数曲U的包含判定
讄坐标为P(x, y, z)Q参数曲UؓQ(t)=(x(t), y(t), z(t))。点也参数曲U的求交计算包括三个步骤Q?/font>
Q?Q计参数t|使P到Q(t)的距L;
Q?Q判断t是否在有效参数区间内Q通常为[0Q?]Q;
Q?Q判断Q(t)与P的距L否小于a 。若Q?Q,Q?Q步的判断均?#8220;?#8221;Q则点在曲线上;否则点不在曲U上?/font>
W一步应计算tQ得|P-Q(t)|最,
?R(t)=(P-Q(t))(P-Q(t))=|P-Q(t)|2最?/font>
Ҏ微积分知识,在该处R'(t)=0?/font>
Q'(t)[P-Q(t)]=0
用数值方法解出t|再代入曲U参数方E可求出曲线上对应点的坐标。第Q?Q、(3Q步的处理比较简单,不再详述?/font>
4、点与^面区域的包含判定
讄坐标为P(x, y, z)Q^面方Eؓax+by+cz+d=0。则点到q面的距Mؓ
d=
若d<a Q则认ؓ点在q面上,否则认ؓ点不在^面上。在造型pȝ中,通常使用q面上的有界区域作ؓ形体的表面。在q种情况下,对落在^面上的点q应q一步判别它是否落在有效区域内。若点落在该区域内,则认为点与该面相交,否则不相交。下面以q面区域多边形ؓ例,介绍有关法?/font>
判断q面上一个点是否包含在同q面的一个多边Ş内,有许多种法Q这里仅介绍常用的三U:叉积判断法、夹角之和检验法以及交点计数验法?/font>
Q?Q叉U判断法
假设判断点ؓP0。多边Ş点按顺序排列ؓP1P2…Pn。如?.5.2所C。oVi=Pi-P0, i=1, 2, …, n, Vn+1=V1?/font>
那么QP0在多边Ş内的充要条g是叉UViXVi+1(i=1, 2, …, n)的符L同。叉U判断法仅适用于凸多边形。当多边形ؓҎQ尽点在多边Ş内也不能保证上述叉积W号都相同。这时可采用后面介绍的两U方法?/font>
?.5.2 叉积判断?/font>
Q?Q夹角之和检验法
假设某^面上有点P0和多边ŞP1P2P3P4P5Q如?.5.3所C。将点P0分别与Pi相连Q构成向量Vi=P-P0。假设角 PiP0Pi+1=ai。如?img height=45 src="file:///F:/软g开?游戏开?数学/2.5.3包含判定法.files/Image101.gif" width=38>=0Q则点P0在多边Ş之外Q如?.5.3(a)所C。如?img height=45 src="file:///F:/软g开?游戏开?数学/2.5.3包含判定法.files/Image102.gif" width=38>=2πQ则点P0在多边Ş之内Q如?.5.3(b)所C。ai可通过下列公式计算QoSi=Vi? Vi+1, Ci=Vi·Vi+1Q则tg(ai)=Si/CiQ所以ai=arctg(Si/Ci)?/font>
ai的符号即代表角度的方向?/font>
?.5.3 夹角之和验法
在多边ŞҎ不太多(<44Q的情况下,可以采用下列q似公式计算ai?/font>
?|Si|≤|Ci|
?|Si|>|Ci|
其中d=0.0355573为常数。当Σαi≥π
Ӟ可判P0在多边Ş内。当ΣαiQ?#960; Ӟ可判P0在多边Ş外。证明略?/font>
Q?Q交点计数检验法
当多边Ş是凹多边形,甚至q带孔时Q可采用交点计数法判断点是否在多边Ş内。具体做法是Q从判断点作一线xIQ?/font>
求射U与多边形边的交点个数。若个数为奇敎ͼ则点在多边Ş内,否则Q点在多边Ş外?/font>
如图2.5.4所C,线a, c分别与多边Ş交于二点和四点,为偶敎ͼ故判断点AQC在多边Ş外。而射Ub, d与多边Ş交三点和一点,为奇敎ͼ所以BQD在多边Ş内?/font>
当射U穿q多边Ş点Ӟ必须Ҏ对待。如?.5.4所C,线fq顶点,若将交点计数?Q则会错误地判断F在多边Ş外。但是,若规定射U过点Ӟ计数?Q则又会错误地判断点E在多边Ş内。正的Ҏ是,若共享顶点的两边在射U的同一侧,则交点计数加2Q否则加1。按q种ҎQE点计?Q所以在多边形外QF点计CؓQ,所以在多边形内。读者可以自己另取一些点来验证?/font>
?.5.4 交点计数?/font>
5、点与二ơ曲?参数曲面的包含判?/font>
假设点坐标ؓP(x0, y0, z0)Q二ơ曲面方EؓQ(x, y, z)=0Q则当|Q(x0, y0, z0)|<? Ӟ认ؓ点在该二ơ曲面上Q在造型pȝ中,通常使用裁剪的二ơ曲面。在q种情况下,q要判断Ҏ否在有效范围内。裁剪的二次曲面通常用有理Bezier或有理Bh的参数空间上的闭合曲U来定义曲面的有效范_故要把点所对应的参数空间的参数坐标计算出来Q再判断该参数坐标是否在参数I间有效区域上?/font>
6、点与三lŞ体的包含判定
判断Ҏ否被三维形体所包含Q可先用前面的方法判断点是否在三lŞ体的表面上,然后判断Ҏ否在形体内部Q其Ҏ因Ş体不同而异。下面以凸多面体Z说明?/font>
讑և多面体某个面的^面方Eؓax+by+cz+d=0Q调整方E系数的W号Q当ax+by+cz+d<0Ӟ?x,y,z)位于该^面两侧中包含该凸多面体的一侧。于是要验一个点是否在凸多面体内部,只要验是否它对凸多面体的每一个面均满以上的不等式即可?/font>