(三) 红黑树 红黑树 自身具有优秀的平衡性,具有很高效的检索速度,很适于对有权重的数据进行组织和查找。红黑树首先是一种二叉搜索树,因而具有“左下最
(三)红黑树
红黑树自身具有优秀的平衡性,具有很高效的检索速度,很适于对有权重的数据进行组织和查找。红黑树首先是一种二叉搜索树,因而具有“左下最小、右下最大”的性质。红黑树的每个节点(node)至少包括了5个域: 父节点指针、左孩子指针、右孩子指针、关键字、颜色,红黑树具有如下特性,才使得它具有如此优秀的性质:
[1]红黑树的节点要么是黑色,要么是红色。
(2)对父节点进行右旋操作,并且保持树的颜色分布,这样颜色分布不变,仅仅是父节点进行了右旋
(四)基数排序
基数排序的思想是将n个待排元素对应到一个多位的整数,一共有d位,每一位都可以取值k个值; 对每一位为标准位进行一次稳定的排序,整个元素的顺序按照这个标准位进行排列,继续从低位排序到高位,并对这个元素重新排序;
如果每一次稳定排序的时间是O(n+k),那么最终时间是O(d*(n+k))。
(五)桶排序
桶排序是针对在一定区间内均匀分布(或近似于均匀分布)的数据进行排序的,所以是一种特殊(非普适)的算法。我们假设分布的区间是[0, 1),对这个长度为1的区间分成n个小区间(我们称之为桶),每个区间长度为1/n,每一个小区间对应一个链表,当新数据输入时,我们将输入数据逐个地和桶的标准分界值比较,当输入数据落入某个区间时,将它插入到对应的链表中去,在插入的时候又可以进行比较从而使得链表内数据有序。当所有输入完成后,将所有链表(除了头节点)链接起来,形成一个总的链表。
(六)动态规划
算法基本思想是分治:将问题分解为若干个子问题,但是要进行中间结果(即子问题的解)的记录,在继续计算时,会多次使用前面的中间结果,将这些子问题的解有序地组合起来,一般可以组成一个二维数组,备存便用,这成为了动态规划的最大特征。可见动态规划是一种牺牲空间换取时间为代价的,中间结果放在存储表中。利用这种思想对递归方法进行改进得到了记忆型递归,即每次递归到子问题时先查询是否有中间解,没有继续递归,否则直接使用已得结果,如矩阵链乘法。
动态规划的使用还有一个重要的原则:即子问题的原问题的最优解的必要条件,即如果最终的解是最优的,那么解的每一个细分(按照子问题划分)或每一个步骤都是最优的。比如最短路径问题,它的子步骤是到达终点的路径分段,即包含终点的子路径的选择(从后向前),要确保每一个字分段都是最优的。即如果是最优的,则有是最优的。这里0-1背包问题和最短路径问题都是最优解问题。
(七)0-1背包问题
算法的思想简单,是基本的递归思路,只不过在每一层递归时,考虑下一个物品的取舍,以及下一物品是否“超重”,总是遵循“累计”价值最大以及“超重不取”的原则,而不是“能装则取”。
不过在构造存储表时需要更多的技巧,这是一个矩阵(二维数组),i下标代表剩余的物品数,j下标代表剩余的容量。由于二维数组下标是连续变化的,所以对每一个单位的剩余重量都要分配存储空间和初始化值,这里考虑的临界条件比较巧妙,对数组元素赋的初值经过了精心设计:a.对于取最后一个物品的,即剩余n个物品的初值,假设W[n]
<= C,即能装的下最后一个物品(最后一个指的是第n个而不是时间上的概念),则映射到这个冗余的存储数组时,从M[n][W[n]]到M[n][C]都要赋一样的初&#20540;,因为这时只能取一个物品n,并且取完之后使得M[n][.](.代表上述范围内的&#20540;)是,考虑了第n个物品的取舍之后的,所能取得物品的最大价&#20540;,即M[n][W[n]...C] = W[n],;这里进一步考虑对于第n个物品,元素下标j为0到W[n]-1的情况,虽然在目前看来不可能,因为我们的物品n重量W[n]
重复以上思路,对于0...C(...表“到”)即全部的重量范围,无论还剩余多少个n-1以内的物品,都无法估算出,考虑了这些物品后,能够获得的最大价&#20540;;因为在考虑这些物品时,至少还要考虑对是否选择第n个物品,由此产生的最大价&#20540;的比较问题(第n个物品有可能选有可能在装不下时不选),从而无法赋初&#20540;。其中又可以分为两种情况:a.W[i] <= C,可以放得下第i个物品,这时与第n个物品的取舍不同,要考虑取还是不取第i件物品,因为这直接影响到在子问题的解当中做的选择,所以有 M[i][j] = max(M[i&#43;1][j-W[i]] &#43; V[i], M[i&#43;1][j]),j = W[i]...C; M[i][j] = M[i&#43;1][j],j = 0...W[i]-1(这里考虑到为了提供剩余容量在选择下一个物品后可能会受到限制,而存储的可供选择的数&#20540;,即物品i没有被选择时容量不变)。b.W[i] > C,放不下物品i,M[i][j] = M[i&#43;1][j],j = 0...C。归纳来说,就是MaxJ = min(W[i] - 1, C),有M[i][j] = M[i&#43;1][j],j = 0...MaxJ;M[i][j] = max(M[i&#43;1][j-W[i]] &#43; V[i], M[i&#43;1][j]),j = W[i]...C。
在这里,又有一个细节性的关键问题,在这个算法中,取第i件物品影响的不是还没有的取得物品,而是自身的问题最大价&#20540;,这里要考虑的是选取物品i使得剩余空间减少的情况下子问题的最大&#20540; 和 不选取物品i的剩余空间下子问题的最大&#20540;,而不是第i&#43;1件物品已经确定,剩余容量大小和i的增减之间也没有正相关的关系,只是第i&#43;1件物品的子问题提供了在不同剩余空间下最大价&#20540;的子问题解的选择而已;具体地说,这里用到的反向思维方式,即我们的问题的最优解取决于子问题的最优解,而子问题的最优解是已经解决的问题的解和当前考虑的新元素对应的情况处理组合,而不是仅仅是在子问题解中选择最大价&#20540;的解,这里我们要在i&#43;1物品的考虑点上,对两个不同的剩余容量存储&#20540;对应的价&#20540;之间进行选择,即选择采用M[i&#43;1][j]还是M[i&#43;1][j-W[i]]。这有悖于正常思维,因为我们可能认为第i&#43;1个物品已经选过,而对它的选择取还是不取,考虑时剩余的背包容量空间应该固定的,而这里却受到第i个物品的取舍的影响。这里,正是这个算法的精髓所在,即在考虑某个物品时,要考虑所有的容量空间,在这些容量空间下,取舍该物品所能得到的总的价&#20540;都要计算。 这样,在选取之后的第i件物品时,第i件物品的取舍直接影响到对于第i&#43;1个子问题解的选择范围,这里的选择范围是取或不取第i件物品造成的剩余容量的限制,具体地说,就是选取第i件物品时在剩余容量下取最大价&#20540;的子问题的解,和在不取第i件物品时在剩余容量下取最大价&#20540;的子问题的解,在这基础上再考虑第i件物品的选取带来的价&#20540;,由此作出选择。
用数学表达式表示如下: