中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么在C#中使用二叉樹計算用戶積分

發布時間:2021-03-22 16:44:05 來源:億速云 閱讀:207 作者:Leah 欄目:編程語言

這篇文章將為大家詳細講解有關怎么在C#中使用二叉樹計算用戶積分,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

假設積分范圍是0-5,我們對它不斷進行中位分區直到不能分為止,形成如下一棵二叉樹:

怎么在C#中使用二叉樹計算用戶積分

其中每個樹節點包含2個信息:節點范圍range[min,max) 和命中數量計數器count ,可以看到葉子節點的range一定是相鄰的2個數。

假如現在有一個積分3要插入到樹中,該如何操作呢?當前節點從根節點開始,分別判斷是否包含于左右子節點,如果包含的話當前節點改為這個子節點,同時計數器加1,然后再次進行相同判斷,直到遍歷到葉子節點為止,遍歷順序如下:

怎么在C#中使用二叉樹計算用戶積分

再依次插入1和4,二叉樹的演變情況為:

怎么在C#中使用二叉樹計算用戶積分

怎么在C#中使用二叉樹計算用戶積分

數據放進去后怎么判斷它是排名多少呢?還是從根節點開始,判斷它是否包含于左子節點,如果包含的話說明它比右子節點中count個數小(在count名之外),然后再往下一級做同樣的判斷;如果包含于右子節點那就繼續往下判斷,直到碰到葉子節點為止。依次累加count最后加上葉子節點占的一位就得到了它在這棵樹里的排名,以1為例演示判斷步驟(排名為2+1=3):

怎么在C#中使用二叉樹計算用戶積分

好了,一切就緒,只欠代碼。

擼碼實現

樹結構由節點構成,那首先設計一個節點類:

  /// <summary>
  /// 樹節點對象
  /// </summary>
  public class TreeNode
  {
    /// <summary>
    /// 節點的最小值
    /// </summary>
    public int ValueFrom { get; set; }

    /// <summary>
    /// 節點的最大值
    /// </summary>
    public int ValueTo { get; set; }

    /// <summary>
    /// 在節點范圍內的數量
    /// </summary>
    public int Count { get; set; }

    /// <summary>
    /// 節點高度(樹的層級)
    /// </summary>
    public int Height { get; set; }

    /// <summary>
    /// 父節點
    /// </summary>
    public TreeNode Parent { get; set; }

    /// <summary>
    /// 左子節點
    /// </summary>
    public TreeNode LeftChildNode { get; set; }

    /// <summary>
    /// 右子節點
    /// </summary>
    public TreeNode RightChildNode { get; set; }
  }

樹節點的屬性主要包含范圍值ValueFrom、ValueTo、計數器Count、左子節點LeftChildNode和右子節點RightChildNode,由此組成一個有層次的樹結構。
然后就是定義我們的樹對象了,它的核心字段就是代表源頭的根節點:

  public class RankBinaryTree
  {
    /// <summary>
    /// 根節點
    /// </summary>
    private TreeNode _root;

  }

根據前面的算法思想,創建樹的時候要用積分范圍初始化所有節點,這里約定了最小積分為0,通過構造函數傳入最大值并創建樹結構:

   /// <summary>
    /// 構造函數初始化根節點
    /// </summary>
    /// <param name="max"></param>
    public RankBinaryTree(int max)
    {
      _root = new TreeNode() { ValueFrom = 0, ValueTo = max+1, Height = 1 };
      _root.LeftChildNode = CreateChildNode(_root, 0, max / 2);
      _root.RightChildNode = CreateChildNode(_root, max / 2, max);
    }

    /// <summary>
    /// 遍歷創建子節點
    /// </summary>
    /// <param name="current"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    private TreeNode CreateChildNode(TreeNode current, int min, int max)
    {
      if (min == max) return null;
      var node = new TreeNode() { ValueFrom = min, ValueTo = max, Height = current.Height + 1 };
      node.Parent = current;
      int center = (min + max) / 2;
      if (min < max - 1)
      {
        node.LeftChildNode = CreateChildNode(node, min, center);
        node.RightChildNode = CreateChildNode(node, center, max);
      }
      return node;
    }

有了樹以后下一步就是往里面插入數據,根據前面介紹的邏輯:

  /// <summary>
    /// 往樹中插入一個值
    /// </summary>
    /// <param name="value"></param>
    public void Insert(int value)
    {
      InnerInsert(_root, value);
      _data.Add(value);
    }

    /// <summary>
    /// 子節點判斷范圍遍歷插入
    /// </summary>
    /// <param name="node"></param>
    /// <param name="value"></param>
    private void InnerInsert(TreeNode node, int value)
    {
      if (node == null) return;
      //判斷是否在這個節點范圍內
      if (value >= node.ValueFrom && value < node.ValueTo)
      {
        //更新節點總數信息
        node.Count++;
        //更新左子節點
        InnerInsert(node.LeftChildNode, value);
        //更新右子節點
        InnerInsert(node.RightChildNode, value);
      }
    }

下一步提供方法獲取指定值在樹中的排名:

   /// <summary>
    /// 從樹中獲取總排名
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public int GetRank(int value)
    {
      if (value < 0) return 0;
      return InnerGet(_root, value);
    }

    /// <summary>
    /// 遍歷子節點獲取累計排名
    /// </summary>
    /// <param name="node"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    private int InnerGet(TreeNode node, int value)
    {
      if (node.LeftChildNode == null || node.RightChildNode == null) return 1;
      if (value >= node.LeftChildNode.ValueFrom && value < node.LeftChildNode.ValueTo)
      {
        //當這個值存在于左子節點中時,要累加右子節點的總數(表示這個數在多少名之后)
        return node.RightChildNode.Count + InnerGet(node.LeftChildNode, value);
      }
      else
      {
        //如果在右子節點中就繼續遍歷
        return InnerGet(node.RightChildNode, value);
      }
    }

到這里,核心功能已經實現了。考慮到有積分更新的情況,我們可以加上節點更新和刪除的方法。刪除很容易,和插入逆向操作就行,更新就更容易了,把舊節點刪除再計算出新值插入即可,完整代碼已經上傳到Github。
這棵樹究竟效率如何,下面我們跑個分看看。

測試走起來

在測試程序中,我模擬了積分范圍0-1000000的場景,這個范圍幾乎覆蓋了真實業務中90%的積分值,100萬積分以上的會員系統應該比較少見了。

而會員的積分值分布也是不均勻的,一般來說擁有小額積分的用戶比例最大,積分值越高所占用戶比例越小。
在程序中我假設有100萬個會員,其中50W用戶積分都在100以內,30W用戶積分在100-10000,15W用戶積分在10000-50000,5W用戶積分在50000以上。

下面是各個操作的耗時時間:

怎么在C#中使用二叉樹計算用戶積分

可以看到,這個效率不是一般的快啊,其中獲取排名的查詢時間幾乎可以忽略不計。
這時候有人問了,這么多數據會不會非常吃內存,下面用任務管理器分別查看不使用樹和使用樹的內存情況:

怎么在C#中使用二叉樹計算用戶積分

怎么在C#中使用二叉樹計算用戶積分

運行環境是.NetCore3.0 Console,測試主機配置情況:

怎么在C#中使用二叉樹計算用戶積分

關于怎么在C#中使用二叉樹計算用戶積分就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

古交市| 昭觉县| 双桥区| 勃利县| 浦县| 英德市| 什邡市| 东源县| 荆州市| 古田县| 宜章县| 涞源县| 正宁县| 克东县| 浪卡子县| 都江堰市| 顺平县| 含山县| 隆安县| 申扎县| 阿拉善右旗| 荔波县| 特克斯县| 依兰县| 民县| 杨浦区| 鹤岗市| 鞍山市| 赤峰市| 包头市| 沙湾县| 筠连县| 苍山县| 铁岭县| 双江| 会泽县| 定陶县| 双鸭山市| 西安市| 长丰县| 台江县|