Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#插入和删除下半部分最有效的数据结构_C#_Data Structures_Binary Search Tree_Avl Tree - Fatal编程技术网

C#插入和删除下半部分最有效的数据结构

C#插入和删除下半部分最有效的数据结构,c#,data-structures,binary-search-tree,avl-tree,C#,Data Structures,Binary Search Tree,Avl Tree,假设我有一个大的整数排序列表(>1000项)。我需要能够对这个列表执行两个操作:删除下半部分,并通过插入随机整数再次填充列表到其原始大小。因为我做这些操作大约一百万次,所以我需要它尽可能的高效 我做的第一件事就是使用一个列表,通过在正确的位置添加新项目来保持排序。虽然删除排序列表的下半部分非常容易,但插入要花费相当多的时间 我尝试实现一个跳过列表,但是在一些测试之后,似乎列表的大小必须至少为10000才能真正起作用,否则它的性能甚至比我的正常列表还要差 这就是为什么我决定使用AVL树,这样我可以

假设我有一个大的整数排序列表(>1000项)。我需要能够对这个列表执行两个操作:删除下半部分,并通过插入随机整数再次填充列表到其原始大小。因为我做这些操作大约一百万次,所以我需要它尽可能的高效

我做的第一件事就是使用一个
列表
,通过在正确的位置添加新项目来保持排序。虽然删除排序列表的下半部分非常容易,但插入要花费相当多的时间

我尝试实现一个跳过列表,但是在一些测试之后,似乎列表的大小必须至少为10000才能真正起作用,否则它的性能甚至比我的正常列表还要差

这就是为什么我决定使用AVL树,这样我可以更快地插入项目。但问题是,我不知道任何有效的方法来删除这样一个二叉搜索树的下半部分

我的问题是:有没有一种有效的方法来做到这一点?是否有其他更易于使用的数据结构

编辑 我做了一个小测试,显示了列表、跳过列表和AVL树之间的性能差异。我使用msdn上的本教程制作了跳过列表:。AVL树来自此处:。我将测试上传到Pastebin:


在测试中,我在计时时向每个数据结构添加了100000项。在我的电脑上,列表耗时约1秒,跳过列表耗时0.5秒,AVL树耗时0.045秒。如果我想这样做一百万次,列表将需要大约11.5天,但是AVL树只需要大约半天。这个时差清楚地说明了我为什么希望它高效。

为什么假设您需要不同的数据结构?说:

我做的第一件事就是使用一个列表,通过在正确的位置添加新项目来保持排序。虽然删除排序列表的下半部分非常容易,但插入要花费相当多的时间

我很担心,因为您可能使用了正确的[1]数据结构,但算法很差。我强烈建议你看一看,并把它包括在你的问题中

但是列表插入很慢O(n)

不要插入

正如@usr所解释的,更好的算法可能是:

  • 拆下下下半部分
  • 添加随机整数以恢复原始大小
  • 对列表排序
  • 不需要更改数据结构,但需要对解决问题的方式进行重大更改

    这一点尤其重要,因为正如@atlaste所重申的,并非每个系统都是平等的,无论O(?)

    使用现代处理器,事情就不再那么容易了。我见过 相同算法的不同实现为您提供 由于分支预测、矢量化和 缓存位置。如果你比较的话,计算是很好的 苹果与苹果——但不幸的是,情况可能并非如此。我 希望每个人都能观看这段视频: youtube.com/watch?v=GPpD4BBtA1Y

    但我仍然是O(logn)在O(n)数据结构上

    好的,在我们结束这里并开始实际概述您使用的算法和衡量性能(目前这似乎太多了)之前,让我问您一个问题:

    假设我们有一个“大的整数排序列表(>1000项)”。事实上,让我们假设这个列表有10000个项目

    在O(?)方面,哪一个具有更好的插入性能

    A) 名单

    B) 链表

    C) 二叉树

    准备好后,看看答案:

    他们都有O(1)!O(n)只告诉你事物的伸缩程度(相对于它们本身,并且仅在广义上)。由于列表是固定大小的“10000个项目”,因此没有缩放(所有项目都被视为“常量因子”)。请注意,我并不是说这些结构具有相同的性能。。。只是O(?)的描述有局限性。更多信息

    基准

    以下是插入排序与添加所有新随机项后排序的基准:

    结果(默认设置)

    请注意,即使我们有一个更有效的方法,在O(?)方面,结果并不遥远,因为在这个大小的CPU的速度惊人

    注意事项:

  • OP有相对较小的[2]个整数集合,这些整数应该很容易放入CPU缓存[3]甚至一级缓存中。在这些情况下,小型、连续和可预测的内存使用(例如数组和列表(在基于数组的C#中))可能非常有效
  • 假设上界为10000,则32位和64位系统的上界分别为~40KB或~80KB
  • “英特尔Skylake”每个核心有64千字节的一级缓存和256千字节的二级缓存:

  • 我想你想一直保持列表的排序。用随机整数替换下半部分的最佳方法如下:

  • 拆下下下半部分
  • 添加随机整数以恢复原始大小
  • 对列表排序
  • 之前,您已在正确的位置插入。这有效地实现了一种非常慢的选择排序。相反,让内置的高效排序算法来完成繁重的工作

    这应该是
    O(n*logn)
    。以前它是
    O(n^2)


    您可以通过不删除前半部分的恒定因子对此进行优化。相反,用随机数替换它,然后进行排序。

    关于这个问题,我想指出一些事情。首先,让我们了解一些关于性能和C#的事情,因为在仍然存在误解的情况下,很难解释这些东西

    接下来,我将把我要讨论的所有问题都应用到这里的具体问题上

    一般来说,C#中的性能

    大O符号Testing performance of filling a list to 10000 items 1000 times, discarding 1/2 of items after every fill! Old list: 3248ms New list: 547ms DONE
    void set(int[] array, int index) 
    {
        array[index] = 0;
    }
    
    if (index < 0 || index >= array.Length) 
    { 
        throw new IndexOutOfRangeException(); 
    }
    
    static void Main(string[] args)
    {
        Random rnd = new Random(12839);
    
        SortedSet<int> list = new SortedSet<int>();
    
        for (int i = 0; i < 5000; ++i)
        {
            list.Add(rnd.Next());
        }
    
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 10000; ++i)
        {
            for (int j = 0; j < 5000; ++j)
            {
                list.Add(rnd.Next());
            }
            int n = 0;
            list.RemoveWhere((a) => n++ < 5000);
        }
    
        Console.WriteLine(sw.ElapsedMilliseconds);
    
        Console.ReadLine();
    }
    
    static void Main(string[] args)
    {
        Random rnd = new Random(12839);
        List<int> list = new List<int>();
    
        for (int i = 0; i < 5000; ++i)
        {
            list.Add(rnd.Next());
        }
    
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 10000; ++i)
        {
            for (int j = 0; j < 5000; ++j)
            {
                list.Add(rnd.Next());
            }
    
            list.Sort((a, b) => a.CompareTo(b)); // #1
            list.RemoveRange(0, 5000);
        }
    
        Console.WriteLine(sw.ElapsedMilliseconds);
        Console.ReadLine();
    }
    
    list.Sort((a, b) => a - b);
    
    list.Sort((a, b) => (int)((float)a - (float)b));
    
    static void Main(string[] args)
    {
        Random rnd = new Random(12839);
    
        int[] list = new int[10000];
    
        for (int i = 0; i < 5000; ++i)
        {
            list[i] = rnd.Next();
        }
    
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 10000; ++i)
        {
            for (int j = 0; j < 5000; ++j)
            {
                list[j + 5000] = rnd.Next();
            }
            Array.Sort(list, (a, b) => b - a);
        }
    
        Console.WriteLine(sw.ElapsedMilliseconds);
    
        Console.ReadLine();
    }
    
    list.Sort();
    
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < 10000; ++i)
    {
        for (int j = 0; j < 5000; ++j)
        {
            list.Add(rnd.Next());
        }
    
        // Sort the second half:
        list.Sort(5000, 5000, Comparer<int>.Default);
                
        // Both the lower and upper half are sorted. Merge-join:
        int lhs = 0;
        int rhs = 5000;
        while (list.Count < 15000)
        {
            int l = list[lhs];
            int r = list[rhs];
            if (l < r)
            {
                list.Add(l);
                ++lhs;
            }
            else if (l > r)
            {
                list.Add(r);
                ++rhs;
            }
            else
            {
                while (list.Count < 15000 && list[lhs] == l)
                {
                    list.Add(l);
                    ++lhs;
                }
                while (list.Count < 15000 && list[rhs] == r)
                {
                    list.Add(r);
                    ++rhs;
                }
            }
        }
    
        list.RemoveRange(0, 10000);
    }
    
    list.Clear();
    
    var tmp = list;
    list = list2;
    list2 = tmp;
    
    public static List<int> orig()
    {
        Random rnd = new Random(12839);
    
        List<int> list = new List<int>(10000);
    
        for (int i = 0; i < 5000; ++i)
        {
            list.Add(rnd.Next());
        }
    
        for (int i = 0; i < 10000; ++i)
        {
            for (int j = 0; j < 5000; ++j)
            {
                list.Add(rnd.Next());
            }
    
            // Sort the second half:
            list.Sort(5000, 5000, Comparer<int>.Default);
    
            // Both the lower and upper half are sorted. Merge-join:
            int lhs = 0;
            int rhs = 5000;
            while (list.Count < 15000)
            {
                int l = list[lhs];
                int r = list[rhs];
                if (l < r)
                {
                    list.Add(l);
                    ++lhs;
                }
                else if (l > r)
                {
                    list.Add(r);
                    ++rhs;
                }
                else
                {
                    while (list.Count < 15000 && list[lhs] == l)
                    {
                        list.Add(l);
                        ++lhs;
                    }
                    while (list.Count < 15000 && list[rhs] == r)
                    {
                        list.Add(r);
                        ++rhs;
                    }
                }
            }
    
            list.RemoveRange(0, 10000);
        }
    
        return list;
    }
    
    
    public static int[] altered()
    {
        Random rnd = new Random(12839);
    
        int HALFSIZE = 5000;
        int SIZE = 2 * HALFSIZE;
        int TESTLOOPS = 10000;
    
        int[] list = new int[SIZE];
        int[] list2 = new int[SIZE];
    
        for (int i = 0; i < HALFSIZE; ++i)
        {
            list[i] = rnd.Next();
        }
    
        for (int i = 0; i < TESTLOOPS; ++i)
        {
            for (int j = HALFSIZE; j < list.Length; ++j)
            {
                list[j] = rnd.Next();
            }
    
            // Sort the second half:
            Array.Sort(list, HALFSIZE, HALFSIZE, Comparer<int>.Default);
    
            // Both the lower and upper half are sorted. Merge-join:
            int lhs = 0;
            int rhs = HALFSIZE;
            int i2 = 0;
            while (i2 < HALFSIZE)
            {
                int l = list[lhs];
                int r = list[rhs];
                if (l <= r)
                {
                    list2[i2++] = l;
                    ++lhs;
                }
                if (l >= r)
                {
                    list2[i2++] = r;
                    ++rhs;
                }
            }
    
            var tmp = list;
            list = list2;
            list2 = tmp;
        }
    
        return list;
    }
    
    public static int[] altered2()
    {
        Random rnd = new Random(12839);
    
        int HALFSIZE = 5000;
        int SIZE = 2 * HALFSIZE;
        int TESTLOOPS = 10000;
    
        int[] list = new int[SIZE];
        int[] list2 = new int[SIZE];
    
        for (int i = 0; i < HALFSIZE; ++i)
        {
            list[i] = rnd.Next();
        }
    
        for (int i = 0; i < TESTLOOPS; ++i)
        {
            for (int j = HALFSIZE; j < list.Length; ++j)
            {
                list[j] = rnd.Next();
            }
    
            // quicksort one level to skip values >= maxValue
            int maxValue = list[HALFSIZE - 1];
            int ll = HALFSIZE;
            int rr = SIZE - 1;
            do
            {
                while (ll <= rr && list[ll] < maxValue) { ++ll; }
                while (ll < rr && list[rr] >= maxValue) { --rr; }
                if (ll < rr)
                {
                    int swap = list[ll];
                    list[ll] = list[rr];
                    list[rr] = swap;
                    ++ll;
                    --rr;
                }
            }
            while (ll < rr);
    
            // Sort the second half:
            Array.Sort(list, HALFSIZE, ll - HALFSIZE, Comparer<int>.Default);
    
            // Both the lower and upper half are sorted. Merge-join:
            int lhs = 0;
            int rhs = HALFSIZE;
            int i2 = 0;
            while (i2 < HALFSIZE)
            {
                int l = list[lhs];
                int r = list[rhs];
                if (l <= r)
                {
                    list2[i2++] = l;
                    ++lhs;
                }
                if (l >= r)
                {
                    list2[i2++] = r;
                    ++rhs;
                }
            }
    
            var tmp = list;
            list = list2;
            list2 = tmp;
        }
    
        return list;
    }
    
    public static int[] random()
    {
        Random rnd = new Random(12839);
    
        int HALFSIZE = 5000;
        int SIZE = 2 * HALFSIZE;
        int TESTLOOPS = 10000;
    
        int[] list = new int[SIZE];
        for (int i = 0; i < HALFSIZE; ++i)
        {
            list[i] = rnd.Next();
        }
    
        for (int i = 0; i < TESTLOOPS; ++i)
        {
            for (int j = HALFSIZE; j < list.Length; ++j)
            {
                list[j] = rnd.Next();
            }              
        }
    
        return list;
    }
    
    static void Main(string[] args)
    {
        int HALFSIZE = 5000;
        var origTest = orig();                       
        Stopwatch sw = Stopwatch.StartNew();
        orig();
        sw.Stop();
        Console.WriteLine("Orig time: " + sw.ElapsedMilliseconds);
    
        var alteredTest = altered();
        sw = Stopwatch.StartNew();
        altered();
        sw.Stop();
        Console.WriteLine("Altered time: " + sw.ElapsedMilliseconds);
        Console.WriteLine("Test: " + (origTest.Take(HALFSIZE).SequenceEqual(alteredTest.Take(HALFSIZE)) ? "OK" : "BAD"));
    
        var altered2Test = altered2();
        sw = Stopwatch.StartNew();
        altered2();
        sw.Stop();
        Console.WriteLine("Altered2 time: " + sw.ElapsedMilliseconds);
        Console.WriteLine("Test: " + (origTest.Take(HALFSIZE).SequenceEqual(altered2Test.Take(HALFSIZE)) ? "OK" : "BAD"));
    
        sw = Stopwatch.StartNew();
        random();
        sw.Stop();
        Console.WriteLine("Just random time: " + sw.ElapsedMilliseconds);
        
        Console.ReadKey();
    }