按预定义排列顺序合并List<T>元素的实现问题
解决列表元素按预定义排列合并的索引错误问题
我明白你的问题了——你试图通过预定义的排列序列来控制列表元素的合并顺序,但当前用i - count计算索引的方式在非左到右合并时会失效,导致索引越界或者逻辑错误。核心问题在于你把排列中的元素当成了原始索引,但每次合并后列表结构变化,原始索引和当前索引的映射关系已经被破坏了。
问题根源分析
举个例子,看你提供的第二个排列{0,2,1}:
- 第一次循环
i=0,count=0,合并索引0和1的元素,列表变成["AB","C","D"],count递增到1。 - 第二次循环
i=2,i-count=1,合并索引1和2的元素,列表变成["AB","CD"],count递增到2。 - 第三次循环
i=1,i-count=1-2=-1,直接出现负索引错误。
这是因为count只是合并次数,无法准确反映原始索引在当前列表中的位置——当你合并中间或右侧的元素时,前面的元素位置并没有被“偏移”足够的量,导致计算出的索引完全错误。
解决方案:跟踪块的原始索引
既然你的排列序列对应的是原始元素的合并优先级(比如排列中的数字代表要合并包含该原始索引的块和它右侧的块),我们可以通过跟踪每个当前块包含的原始索引,来找到正确的合并位置。这样不管列表怎么变化,都能精准定位到要合并的块。
修改后的代码
private void FixedExample() { var permutations = new List<List<int>> { new List<int> { 0, 1, 2 }, // AB + C + D, ABC + D, ABCD new List<int> { 0, 2, 1 }, // AB + C + D, AB + CD, ABCD new List<int> { 1, 0, 2 }, // A + BC + D, ABC + D, ABCD new List<int> { 1, 2, 0 }, // A + BC + D, A + BCD, ABCD new List<int> { 2, 0, 1 }, // A + B + CD, AB + CD, ABCD new List<int> { 2, 1, 0 } // A + B + CD, A + BCD, ABCD }; foreach (var permutation in permutations) { var variables = new List<string> { "A", "B", "C", "D" }; // 跟踪每个当前块包含的原始索引,初始每个块对应一个原始元素 var blockOriginalIndices = variables .Select((_, idx) => new HashSet<int> { idx }) .ToList(); foreach (var targetOriginalIndex in permutation) { // 找到包含目标原始索引的当前块的位置 int blockIndexToMerge = blockOriginalIndices.FindIndex(block => block.Contains(targetOriginalIndex)); // 合并当前块和右侧的块 variables[blockIndexToMerge] += variables[blockIndexToMerge + 1]; variables.RemoveAt(blockIndexToMerge + 1); // 更新块的原始索引记录:合并两个块的索引集合 blockOriginalIndices[blockIndexToMerge].UnionWith( blockOriginalIndices[blockIndexToMerge + 1]); blockOriginalIndices.RemoveAt(blockIndexToMerge + 1); } Console.WriteLine(variables.Single()); } }
代码说明
blockOriginalIndices:这个列表中的每个元素是一个集合,记录当前块包含的原始元素索引。初始状态下,每个块只包含一个原始索引(对应初始列表的每个元素)。- 定位合并块:每次遍历排列中的数字时,我们通过
FindIndex找到包含该原始索引的当前块的位置,这个位置就是我们要合并的左侧块的索引。 - 合并与更新:合并对应位置的元素后,同步更新块的索引集合,确保后续能正确找到目标块。
验证效果
运行这段代码后,所有6个排列都会正确输出ABCD,且中间合并步骤完全符合你注释中的预期:
- 排列
{0,2,1}会先合并A+B,再合并C+D,最后合并AB+CD。 - 排列
{1,0,2}会先合并B+C,再合并A+BC,最后合并ABC+D。
这种方式不依赖元素的内容(比如字符串识别),完全适用于你的自定义类场景——你只需要把字符串拼接的逻辑替换成自定义类的合并方法即可。
内容的提问来源于stack exchange,提问作者Derrick Moeller
相关产品推荐
相关产品推荐

