【问题标题】:Is there a kind of sorting algorithm use binary tree by bits of number?有没有一种按位数使用二叉树的排序算法?
【发布时间】:2019-12-04 20:49:49
【问题描述】:

我已经查看了我能找到的排序算法,但我没有看到任何类型的算法使用数字位。我想,我创造了一种新型的排序算法。我命名为 Bitsort。描述在github


你知道这样的排序算法吗?


复杂度为 O (nk)。 k 是数组元素的位大小。数组顺序并不重要。 每个时间复杂度都是 O (nk)。但它使用了一点点内存。它取决于N。但是当N增加时,内存相对减少。如果 N 为 1,则为最大内存比率(R = Node/N)(=bitsize)。如果 N 是最大值,则内存比率降低到 R = 2。所以 R*N 是存储整个位树所需的节点数。如果 N 等于最大值->(整数为 2^32),我们需要 2N 个节点来存储所有数组。每个节点都有 2 个地址指针。

同时 N 不是数组中数字的计数。 N唯一 个数字计数。

如果数组中的所有元素都相同,则N等于1。

总结

    Memory = P*N*R (P: pointer size, N: unique count, R: NodeCount(C)/N)


    I create a formula for R for 32 bit integer.
    R = 31 - 3.3*LOG10(N)

我将数字从 MSB(最高有效位)到 LSB(最低有效位)放入二叉树。如果之前添加了它们,我正在增加价值叶的数量。

我只在源数组上从头到尾移动了 1 次,并且“排序树”已被填充。

void bitSort(int * array, int arraySize) {
    int i, j;
    Block* block;
    clock_t start, end;

    //create a buffer. root node is first node in buffer. 
    root = initBlockBuffer();
    const unsigned long long digit = ((unsigned long long) 1) << (ARRAY_ELEMENT_TYPE_SIZE_1);

    //for every array element (n !IMPORTANT)
    for (i = 0; i < arraySize; i++) {
        // start at root
        Block* activeBlock = root;

        register int value = array[i];
        register int bit;

        //for every bit of value (k !IMPORTANT)
        for (j = 0; j < ARRAY_ELEMENT_TYPE_SIZE_1; j++){

            //from msb to lsb get the bit   
            bit = (digit & (value << j)) >> (ARRAY_ELEMENT_TYPE_SIZE_1);

            // get the related node from bit 
            block = activeBlock->node[bit];


            // if the node is not exists
            if (block == 0) {

                // get next blank node from the buffer.
                block = nextFreeBlock();

                //connect new node to previous node  
                activeBlock->node[bit] = block;
            }

            // jump to new node.
            activeBlock = block;
        }

        //after all last node is leaf. 
        //Getting from last bit of value
        if(activeBlock->cnt[value & 1] == 0) leafCount++;  
        //and count of this leaf, increasing 1
        activeBlock->cnt[value & 1]++;  

    }
}


一些结果


如果我们创建一个有 1000000(一百万)个 4 字节整数的数组: - 相同的号码 - 从 1 增加到 1000000 - 随机均匀分布


相同的数字

Leaf Count    : 1
Node Count    : 31
Node Size     : 16 byte
Total Memory  : 512 byte
Duration Sort : 178721 us (0.02s)
Duration Read : 4994 us (0.005s)


从 1 增加到 1000000

Leaf Count    : 1000000
Node Count    : 1000018
Node Size     : 16 byte
Total Memory  : 16000304 byte (16MB)
Duration Sort : 218556 us (0.2s)
Duration Read : 14321 us (0.01s)


随机均匀分布

Leaf Count    : 999768 (uniq numbers, >%0,02 repetition)
Node Count    : 11181318
Node Size     : 16 byte
Total Memory  : 178913456 byte (179MB)
Duration Sort : 1460578 us (1.4s)
Duration Read : 666933 us (0.7s)


你知道这样的算法吗?


示例

示例: 我们假设我们有 3 位 长度的数字。

array = {7, 3, 2, 5, 0, 7, 3, 2, 7};

L   : level
msb : most significant bit
lsb : least significant bit

              msb       lsb
               L1   L2   L3
7 = 111  -->    1    1    1
3 = 011  -->    0    1    1
2 = 010  -->    0    1    0
5 = 101  -->    1    0    1
0 = 000  -->    0    0    0
7 = 111  -->    1    1    1
3 = 011  -->    0    1    1
2 = 010  -->    0    1    0
7 = 111  -->    1    1    1

首先二叉树只有根节点。

                        0_____________________|_____________________1                          
                       /                                             \                         

使用从 msb 到 lsb 的自己的位将第一个数字添加到二叉树。 (加数: 7 =>(111)(3bit space))

             0_____________________|_____________________1                          
L1  ----->                                                \                         
                                                 0_________\_________1              
L2  ----------------------------------------->                        \             
                                                                   0___\___1        
L3  ---------------------------------------------------------->             \       
                                                                            [1]     

然后是其他人。 (加数:3、2、5、0)

                        0_____________________|_____________________1                          
                       /                                             \                         
            0_________/_________1                           0_________\_________1              
           /                     \                         /                     \             
      0___/___1               0___\___1               0___/___1               0___\___1        
     /                       /         \                       \                       \       
   [1]                     [1]         [1]                     [1]                      1     

如果一个数字已经在树中,它的计数增加 1。 (数字:7、3、2、7)

                        0_____________________|_____________________1                          
                       /                                             \                        
            0_________/_________1                           0_________\_________1              
           /                     \                         /                     \            
      0___/___1               0___\___1               0___/___1               0___\___1        
     /                       /         \             /         \             /         \       
   [1]                     [2]         [2]                     [1]                     [3]     

递归读取时,可以得到有序数组。

sorted_array =  [1x(000), 2x(010), 2x(011), 1x(101), 3x(111)]
sorted_array =  [1x0, 2x2, 2x3, 1x5, 3x7]
sorted_array =  [0, 2, 2, 3, 3, 5, 7, 7, 7]

【问题讨论】:

  • 我认为您正在寻找基数排序
  • 我同意@AndyG,你似乎(重新)发明了一种基数排序。是的,大多数好主意之前都已经被其他人想到过......
  • 当然,如果您不寻找二进制基数排序,那么它有点难以找到。
  • 我不同意你的看法。我认为,如果我们说数字以 2 为底,那么只有它们相交的方面是它们使用数字。没有别的。如果我们说数字以 2 为底,是的,它似乎有点基数或桶。但不是。我正在使用树和按位运算。但他们不是!另外排序,我只是把数字放到树上。任何数字都不会直接与另一个数字进行比较。只是,每个循环都有一个数字在树上从根流向相关叶。当每个数字找到它们的位置时,数组就会被排序。

标签: c algorithm sorting


【解决方案1】:

你错了。你设计了一个排序的二叉树(这不是你的发明)。实际上,您不需要拥有所有密钥(就像您在示例中所做的那样)来完成树。因此,它的平均运行树为O(n*log(n)),因为每次插入都需要覆盖所有树节点,直到您要放置密钥的位置。此外,如果你得到一个已经排序的集合,树会在一个列表中退化(你总是去正确的分支,直到你到达插入的地方)退化成O(n²)

此外,如果您要考虑集合中的每个键(就像您在所有示例中所做的那样),您可以通过实现一个大小为 2^n 的数组来节省指针空间,并考虑 @ 的左儿子987654324@ 是索引为2*n 的单元格,右儿子是索引为2*n+1 的单元格。在这种情况下,您只需存储与键关联的数据,而不是指针。如果您考虑构建多维数组(与您拥有的位数一样多的维度)并且每个索引从01 的可能性,您也会采用这种方法。在这种情况下,考虑到全尺寸数组,您可以将数据直接线性存储到数组单元中,将数组视为线性数组。

【讨论】:

  • 这不是二叉排序树。节点不是数字或不包含数字。这是一棵 1-0 的树。树中的每个节点都可以只是 bit。最大深度与 n(数组数量)无关。最大深度恒定为 k(数字的位大小)。所以 O(n*log(n)) 是不正确的。 O(nk) 是我算法的正确复杂度。所以我不同意:... (you always go to the right branch until you reach the place of insertion) degenerating into O(n²) 我正在使用可扩展缓冲区。缓冲区包括指向另一个块的块,直到叶子。 Leaf 是整个分支定义的值的“计数”。
  • 那么你不能谈论 O(n*k) 因为这正是 k = log(number of keys) 并且这是你可以存储的最大密钥数......你的 k 总是 大于我的log(n) 作为 n(存储在数组中的数字或键)始终小于键空间基数。键空间的基数是树中可以拥有的最大元素数......而二叉平衡树(avl 树)具有最大 log(n) 深度,其中深度与 log(元素数存储在树中)而不是最大日志(可用的键数)
  • 在最好的情况下,你会得到一个完美平衡的树,因为你把钥匙放进去,这意味着你得到 O(nlog(n)) (好吧,如果你有一个 32 位的键空间,你得到一个最大值 O(nlog(2^32)) ~= O(n*32) :) 但是为此,树必须是平衡的,这在 avl 中完成树(或红/黑树)。考虑一下,您会发现它们是相同的(除了您缺乏平衡)您正在构建一棵完全平衡的树...在这种情况下,但这需要您知道关键空间的基数以及问题已经为 avl 树解析了。
  • 无论如何,在平均情况下,你得到的 O(n*log(n)) 与快速排序相同。 (您在 O() 函数中使用的常量 k 是 --- 你这么说,这是真的--- 与 log(keyspace 中的键数)成正比,它大于 log(in容器。
  • (稍后 =)) 是的,你是对的,正如你所说,如果我们在叶子中只存储 1 个元素,k >= log(n)。但我能够在树中存储 more 超过 2^k 个数字。例如,让我们考虑 n 个数字的 3 位空间。我们假设我们的数组长度是 2^64。我可以存储的整数只有 8 个叶子和 7 个父节点。因为我只存储数字。小于 k (= 3) 小于 log(n) (= 64) k >= log(n) 如果数组是唯一的 k
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 1970-01-01
  • 2011-06-22
  • 1970-01-01
相关资源
最近更新 更多