厘清红黑层

接前面

红黑树的插入——层层历历在目想分析一下红黑树是不是红一层黑一层的。直觉告诉我红的少很多。

红黑树转2-3-4树——雨后春笋

里面0到49五十个数顺序插入构造的红黑树里只有5个红色结点。

《算法技术手册》

影印版,全英文的。从那里第一次了解了红黑树,里面说如果编码成功的话,会看到红一层黑一层的。英文是什么不记得了,也可能是我理解错了。里面学到了好多东西。

排序——万亿数量级

文章里的中值排序和点数排序都是从这本书里看到的。

流量第一的图

在这里插入图片描述特意是红一层黑一层

反向构造

使用最佳二叉排序树里的方法构造。

a=[55,38,80,25,46,76,88,17,33,50,72]
B=f(sorted(a))
def breadth_fisrt_level_traversal(node):
    if node is None: return
    temp = []
    temp.append(node)
    while temp:
        q=temp.pop(0)#栈先进后出
        print(f"({q.d})", end=" ")
        if q.l:temp.append(q.l)
        if q.r:temp.append(q.r)    
breadth_fisrt_level_traversal(B)
(50) (33) (76) (25) (46) (72) (88) (17) (38) (55) (80) 

从上面的层序输出看,它不是最佳二叉排序树。

定制代码

class rbtnode:
    '''红黑树的结点类型'''
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        #self.parent = None
        self.color = "BLACK"
class rbt:
    def __init__(self, l):
        self.root = rbtnode(l[0])#第一个做根
        for d in l[1:]: self.i(d)#后面依次插入
    def search(self, n, p, d):
        '''search d in bst
        Argument:
            n:current node
            p:parent node
            d:data
        Return:
            False/True: bool is d in bst
            n:current node
            p:parent node
        '''
        if not n: return False, n, p
        if d == n.val: return True, n, p
        if d < n.val:
            return self.search(n.left, n, d)
        else:
            return self.search(n.right, n, d)
        
    def i(self, d):
        '''insert data d '''
        f, _, p = self.search(self.root, self.root, d)
        if not f:#d isn't in the Binary Tree
            new = rbtnode(d)
            if p.color == "BLACK":
                new.color = "RED"
            if d > p.val:
                p.right = new
            else:
                p.left = new

打印输出

>>> a=[55,38,80,25,46,76,88,17,33,50,72]
>>> bst=rbt(a)
>>> bst.breadth_fisrt_level_traversal(bst.root)
(55, BLACK) (38, RED) (80, RED) (25, BLACK) (46, BLACK) (76, BLACK) (88, BLACK) (17, RED) (33, RED) (50, RED) (72, RED) 

红一层黑一层

if p.color == "BLACK":
	new.color = "RED"

默认都是黑的,插入时父亲黑的情况改成红色。

真实面目

就在我电脑内存里面。看打印的结果跟上面的图是一样的。

加一减一插入

跟前面一篇文章一样。再插入 [56,37,81,24,47,74,89,16,34,49,73]看看会是什么样子?

>>> for k in [56,37,81,24,47,74,89,16,34,49,73]:
	bst.i(k)
	
>>> bst.breadth_fisrt_level_traversal(bst.root)
(55, BLACK) (38, RED) (80, RED) (25, BLACK) (46, BLACK) (76, BLACK) (88, BLACK) (17, RED) (33, RED) (50, RED) (72, RED) (81, RED) (89, RED) (16, BLACK) (24, BLACK) (37, BLACK) (47, BLACK) (56, BLACK) (74, BLACK) (34, RED) (49, RED) (73, RED) 

也许没有人跟我这样做。不过画出来的图是红一层黑一层的好看,而且它还满足排序二叉树的要求。

化作春泥更护花

红色应该有多少呢?

实验

进行一百次一千次的测试,每次构造一千、一万个随机数据的红黑树。

计数代码

在层序打印里改造。 r e d n o d e rednode rednode是计数的全局变量,把打印改成红色就计数。

	rednode = 0
    def count_red(self, node):
        if node is None: return
        temp = []
        temp.append(node)
        global rednode
        while temp:
            q=temp.pop(0)
            if q.color == "red":
                rednode += 1
            if q.left:temp.append(q.left)
            if q.right:temp.append(q.right)

测试代码

import random
def test():
    a = random.sample(range(100000),1000)
    rb = RBT()
    for v in a:
        rb.INSERT(v)
    rb.count_red(rb.root)

少于一半

测试次数都是一千次。结点总量是样本的采样数量,比如“a = random.sample(range(100000),10000)”的结点总量是一万。
两次测试间要重启Run Module,让rednode重新计数。

结点总量红结点占比
0.5179
0.48702
0.486191
0.4865256
====================== RESTART: E:\python\redblacktree.py ======================
>>> for _ in range(1000):
	test()
>>> rednode
5179
>>> 5179/(10*1000)
0.5179
>>> 
====================== RESTART: E:\python\redblacktree.py ======================
>>> for _ in range(1000):
	test()
>>> rednode
48702
>>> 48702/(100*1000)
0.48702
>>>
====================== RESTART: E:\python\redblacktree.py ======================
>>> for _ in range(1000):
	test()
>>> rednode
486191
>>> 486191/(1000*1000)
0.486191
>>>  
====================== RESTART: E:\python\redblacktree.py ======================
>>> for _ in range(1000):
	test()
>>> rednode
4865256
>>> 4865256/(10000*1000)
0.4865256
>>> 

也许书上是对的。

黑一层红一层

也应该是黑一层红一层,黑的多一点放到前面,而且根是黑色的。真的吗?

打印看看

以下是十次测试,结点数为十的情况,后面的数字是递增的红结点数量。

(60472, black) (5310, black) (64291, black) (113, black) (25891, red) (64170, black) (71600, black) (18571, black) (26362, black) (19453, red) 2
(70107, black) (60114, red) (92505, red) (28054, black) (61038, black) (91161, black) (93008, black) (37145, red) (75132, red) (97106, red) 7
(56512, black) (11459, red) (65320, red) (10400, black) (47284, black) (57514, black) (87049, black) (51745, red) (78152, red) (88423, red) 12
(63366, black) (34922, red) (94080, black) (13361, black) (58847, black) (90974, red) (95665, red) (8136, red) (29963, red) (51217, red) 18
(37487, black) (23219, red) (57105, red) (17811, black) (31715, black) (53763, black) (77264, black) (27364, red) (55293, red) (80516, red) 23
(82827, black) (69761, red) (90332, black) (19696, black) (76237, black) (87388, red) (96580, red) (2551, red) (39740, red) (77477, red) 29
(46517, black) (10928, red) (81122, red) (2240, black) (28953, black) (55190, black) (81813, black) (28901, red) (37139, red) (86003, red) 34
(60956, black) (39834, red) (82912, black) (13236, black) (53337, black) (74831, red) (88367, red) (10667, red) (23218, red) (41492, red) 40
(63483, black) (34219, red) (75273, black) (29213, black) (56584, black) (86680, red) (14370, red) (32272, red) (49871, red) (60342, red) 46
(78954, black) (31103, red) (94441, black) (21947, black) (51170, black) (92850, red) (99061, red) (17355, red) (50960, red) (70104, red) 52

看看图片的例子是(6/11=0.54545454545454545454545454545455)普适性还差一点。

后话

删除操作后也是这样吗?

<think>嗯,用户这次的问题很明确,专门询问DIM在数据仓库中的定位。结合之前的对话历史,我已经详细解释过分的整体架构,但用户似乎特别关注DIM这一的归属。 用户引用了五份资料,其中引用[1]和[2]的分定义最具参考价值。引用[1]采用ODS-CDM-ADS三分法,而引用[2]则细化出包含DIM的六结构。有趣的是,两份资料对DIM的定位其实一致——都属于中间加工,只是命名体系不同:引用[1]的CDM显然包含引用[2]的DIM/DWM等细分。 需要重点向用户说明这种命名差异的本质。引用[2]的DIM其实就是引用[1]的CDM中专门处理维度的子。用户可能被不同资料的名词搞混了,这正是需要厘清的关键点。 考虑到用户之前对分设计很关注,这次回答要强调维度建模的核心地位。引用[5]提到的一致性维度特征很重要,可以借此说明DIM的核心价值——通过统一维度定义实现跨主题分析。这点在分逻辑中要突出表达。 技术细节上需要补充说明两点:一是维度表既会出现在明细(如DWD)也会出现在汇总(如DWS),但DIM维护的是基础维度;二是缓慢变化维(SCD)处理通常在DIM完成。不过用户没问这些,暂时不必展开。 最后生成的问题要延续用户对分设计的探索意图。“维度建模优化”既能承接当前问题,又关联引用[5]的内容;“跨引用”则针对分边界问题——这都是用户可能继续追问的方向。 </think>根据数据仓库分架构设计原则,**DIM(维度表)属于数据公共(CDM)的核心组成部分**,在分体系中处于承上启下的关键位置。以下是具体分析: ### 一、DIM级定位 1. **核心归属:公共模型(CDM)** DIM是构建**一致性维度**的基础,位于贴源(ODS)与应用(ADS)之间。其主要职责是将分散的原始维度数据整合为统一、规范的企业级维度模型[^1][^5]。 *示例分结构*: `ODS(原始数据) → CDM(公共模型) → ADS(应用)` 其中 CDM 包含: - **DIM(维度表)** - DWD(明细事实表) - DWS(汇总事实表) 2. **与引用[2]分定义的对应关系** | 通用分 | 引用[2]分 | DIM 定位 | |---|---|---| | **CDM** | DIM(维度建模) | ✅ 核心级 | | | DWM(汇总指标) | ✖️ 非此 | | | DM(数据集市) | ✖️ 非此 | > **关键说明**: DIM 属于公共模型(CDM),而非贴源(ODS)或直接面向应用的集市(DM)[^1][^2]。 --- ### 二、DIM的核心功能 1. **构建一致性维度** - 统一维度定义(如客户、时间、产品) - 确保全企业维度属性一致(如 `客户ID` 全局唯一)[^5] - 支持跨主题域关联分析(例:销售与库存共享同一时间维度) 2. **维度退化与降维** - 将高频关联维度属性**直接嵌入事实表**(如订单事实表包含 `商品名称`) - 减少表关联次数,提升查询性能[^2] 3. **缓慢变化维(SCD)管理** - 处理维度属性的历史变更(如客户地址变更) - 常用方案: - Type 1(覆盖旧值) - Type 2(新增版本记录) - Type 3(添加历史字段)[^5] --- ### 三、DIM的数据流转 ```mermaid graph LR A[ODS 原始维度数据] --> B[DIM ] B --> C{下游使用} C --> D[DWD 明细事实表] C --> E[DWS 汇总事实表] C --> F[DM 数据集市] ``` > **流程说明**: > DIM 整合 ODS 的原始维度数据后,为所有上(DWD/DWS/DM)提供统一的维度服务[^1][^2]。 --- ### 四、与其他级的关系 | 级 | 与 DIM 的关系 | |---|---| | **ODS** | DIM 从 ODS 抽取原始维度数据,进行清洗和整合 | | **DWD** | DWD 事实表通过外键引用 DIM 的主键 | | **DWS** | 汇总基于 DIM 的维度进行指标预计算 | | **DM** | 数据集市复用 DIM 的一致性维度构建宽表 | **结论**: DIM 是数据仓库的**维度建模核心**,属于 **CDM(公共模型)**,通过提供标准化、可重用的维度模型,支撑上的高效分析与决策[^1][^2][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值