[英]Convert a double list to a grouped string
該程序的輸入為雙精度列表,輸出為包含按值分組的列表值的字符串。 如果列表值相等,則將它們分組。 像這樣的東西:輸入9,77,5,5,31 =>輸出9 77 2 * 5 31
為此,我在C#中創建了一個算法(在Java中,我認為幾乎是相同的),但是我不確定在速度或代碼質量方面是否可以改進它,或者是否存在一些我看不到的錯誤。 下面還有另外一些輸入,輸出示例的算法。
List<double> input = new List<double> { 11, 32, 32, 43}; // output 11 2*32 43
//List<double> input = new List<double> { 11, 11, 43, 43 }; // output 2*11 2*43
//List<double> input = new List<double> { 10, 11, 12, 13, 14, 15, 16 }; // output 10 11 12 13 14 15 16
//List<double> input = new List<double> { 11, 11, 11, 11, 11 }; // output 5 * 11
//List<double> input = new List<double> { 11, 11, 32, 22, 22, 22, 4, 10, 10 }; // output 2*11 32 3*22 4 2*10
string listAsString = string.Empty;
double nextElem = double.MinValue;
for (int i = 0; i < input.Count; i++)
{
double currentElem = input[i];
if (i + 1 < input.Count)
{
nextElem = input[i + 1];
}
int equalCount = 0;
while (currentElem.Equals(nextElem) && i < input.Count)
{
equalCount++;
i++;
currentElem = nextElem;
if (i < input.Count)
{
nextElem = input[i];
}
}
if (equalCount < 2)
{
listAsString += currentElem + " ";
}
else
{
listAsString += equalCount + "*" + currentElem + " ";
i--;
}
}
Console.WriteLine(listAsString);
如果您發現一些錯誤或看到可以完成的改進,請告訴我。
另外,如果您知道此要求的另一種實現,請添加它,以便可以對算法之間的結果,速度,代碼質量進行比較...並找到處理此問題的最佳方法。
由於要求僅對連續的相等值進行分組,因此另一個答案中提到的Dictionary
和LINQ GroupBy
方法不適用,因為它們會對諸如1,2,1
類的輸入序列產生不正確的結果。 同樣,也沒有標准的LINQ方法來進行這種分組(最終的Aggregate
方法除外,但for
/ foreach
循環等效項而言,效率不高)。
不久,您的算法最適合此類任務。 但是執行不是。
主要的瓶頸是Peroxy提到的字符串連接,該字符串連接(在其他答案中也提到了)可以通過使用StringBuilder
類輕松修復。 一旦執行此操作,性能將很好。
另一個問題我在執行看到的是特殊值(使用double.MinValue
),重復的極端情況的檢查,遞減for
身體等內部循環變量所以,雖然它可能工作,我不直接看到一個bug,它是一種很難理解算法邏輯並在閱讀實現時發現潛在的錯誤。 該算法本身非常簡單,我可以通過以下方式實現:
static string ListAsString(List<double> input)
{
var sb = new StringBuilder();
for (int i = 0; i < input.Count; )
{
var value = input[i];
int count = 1;
while (++i < input.Count && input[i] == value)
count++;
if (sb.Length > 0) sb.Append(' ');
if (count > 1) sb.Append(count).Append('*');
sb.Append(value);
}
return sb.ToString();
}
IMO比較容易理解。 請注意,沒有重復的代碼,沒有特殊的值,並且循環變量i
只能在外部循環體內的一個位置進行。 同樣,這與性能無關(這由StringBuilder
用法提供),而僅僅是可讀性,冗余消除和較少的錯誤傾向。
就個人而言,我在這里發現使用Dictionary
潛力很大,這是我使用字典實現的一個快速解決方案:
var input = new List<double> { 9, 77, 5, 5, 31 };
var dict = new Dictionary<double, int>();
var listAsString = new StringBuilder();
foreach (var item in input)
{
if (dict.ContainsKey(item))
dict[item]++;
else
dict[item] = 1;
}
foreach (var item in dict)
{
listAsString.Append(item.Value > 1 ? $"{item.Value}*{item.Key} " : $"{item.Key} ");
}
Console.WriteLine(listAsString);
如果您想要一種效率不高的LINQ單襯板解決方案:
string result = string.Join(" ", input.GroupBy(i => i)
.Select(x =>
x.Count() > 1 ?
$"{x.Count()}*{x.Key} " :
$"{x.Key} "));
但是,我相信您的方法寫得很好,雖然比字典的可讀性差,但是解決方案的主要缺點是,在構建最終字符串時您使用的是字符串,您肯定應該使用StringBuilder
,在您的方法中介紹了StringBuilder
,並對這三種方法進行了比較:
Dictionary | Your method | GroupBy method
------------------------------------------------
2 ms | 0 ms | 5 ms n=3
0 ms | 0 ms | 0 ms n=6
0 ms | 0 ms | 0 ms n=12
0 ms | 0 ms | 0 ms n=24
0 ms | 0 ms | 0 ms n=48
0 ms | 0 ms | 0 ms n=96
0 ms | 0 ms | 0 ms n=192
0 ms | 0 ms | 0 ms n=384
0 ms | 0 ms | 0 ms n=768
0 ms | 0 ms | 0 ms n=1536
1 ms | 0 ms | 1 ms n=3072
3 ms | 2 ms | 3 ms n=6144
5 ms | 4 ms | 6 ms n=12288
8 ms | 7 ms | 14 ms n=24576
14 ms | 13 ms | 25 ms n=49152
31 ms | 32 ms | 66 ms n=98304
80 ms | 59 ms | 146 ms n=196608
149 ms | 123 ms | 294 ms n=393216
246 ms | 218 ms | 504 ms n=786432
483 ms | 428 ms | 1040 ms n=1572864
999 ms | 873 ms | 2070 ms n=3145728
1995 ms | 1784 ms | 3950 ms n=6291456
您的解決方案始終是最快的,如果您想追求速度,請保留您的解決方案,但是將其更改為使用StringBuilder
,請使用listAsString.Append(currentElem + " ")
代替listAsString += currentElem + " "
。
如果僅對n < 1000
集合進行操作,則可以使用GroupBy
如果您希望在速度上提高可讀性,請使用Dictionary
解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.