2016-08-25 61 views
3

我有一個字符串,簡化爲"12345"它是排序的。字符串couild包含數字(0-9)或字母(a-z)。如果混合使用自然排序順序。我需要一種方法來驗證這是否屬實。檢查一個字符串是否排序

嘗試使用LINQ技術:

string items1 = "2349"; //sorted 
string items2 = "2476"; //not sorted, 6<>7 

bool sorted1 = Enumerable.SequenceEqual(items1.OrderBy(x => x), items1); //true 
bool sorted2 = Enumerable.SequenceEqual(items2.OrderBy(x => x), items2); //false 

但可能還按降序排序。

是否有更好的方法,然後

string items3 = "4321"; 
bool sorted3 = Enumerable.SequenceEqual(items3.OrderBy(x => x), items3) || Enumerable.SequenceEqual(items3.OrderByDescending(x => x), items3); 

檢查一個字符串排序?也許有些內置解決方案?

+0

http://stackoverflow.com/questions/6396378/c-sharp-linq-orderby-numbers-that-are-string-and-you-cannot-convert-them-to-int – Oluwafemi

+0

如果字符串包含,該怎麼辦你的信?您剛剛向我們展示了最佳案例場景。如果字符不是英文字母(例如'-'或'?')? – user3185569

+0

@ c0rd因此'123abc'和'abc123'和'ab12cd34'被認爲是分類的? – user3185569

回答

2

我曾經檢查過類似於您的案例,但數據流量很大,因此性能很重要。我想出了這個小的擴展類,它有很好的表現:

public static bool IsOrdered<T>(this IEnumerable<T> enumerable) where T: IComparable<T> 
{ 
    using (var enumerator = enumerable.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
      return true; //empty enumeration is ordered 

     var left = enumerator.Current; 
     int previousUnequalComparison = 0; 

     while (enumerator.MoveNext()) 
     { 
      var right = enumerator.Current; 
      var currentComparison = left.CompareTo(right); 

      if (currentComparison != 0) 
      { 
       if (previousUnequalComparison != 0 
        && currentComparison != previousUnequalComparison) 
        return false; 

       previousUnequalComparison = currentComparison; 
       left = right; 
      } 
     } 
    } 

    return true; 
} 

使用它,顯然是非常簡單的:

var items1 = "2349"; 
var items2 = "2476"; //not sorted, 6<>7 
items1.IsOrdered(); //true 
items2.IsOrdered(); //false 
+1

這確實是正確的方法。比任何「簡潔」的純LINQ嘗試都要好得多。 DRY和**用法**更加簡潔和可讀。 –

0
var items = "4321"; 
var sortedItems = items.OrderBy(i => i); // Process the order once only 
var sorted = sortedItems.SequenceEqual(items) || sortedItems.SequenceEqual(items.Reverse()); // Reverse using yield return 
+1

'OrderBy'的結果是IEnumberable,應該在每次調用時處理 – c0rd

6

您的解決方案精細且非常易讀。其中一個問題是,它需要訂購stringwhich is O(n * log(n)),這可以通過迭代string而不對其進行排序來解決。

例如:

var firstDifs = items1.Zip(items1.Skip(1), (x, y) => y - x); 

Linq項目每2項第一string了一些這表明它們的區別,所以,如果你有items1 = "1245"輸出將是:

firstDifs: {1, 2, 1}

現在您所需要做的就是驗證德那firstDifs是按升序或降序:

bool firstSorted = firstDifs.All(x => x > 0) || firstDifs.All(x => x < 0); //true 

目前:

  • SkipO(1)由於跳過1個細胞所需的動作量是 恆定。
  • ZipO(n)
  • AllO(n)

所以整個溶液爲爲O(n)

注意這將是更有效的用一個簡單的循環,還當第一All返回false因爲第三千四百八十七項目改變其方向(例如:1234567891),第二All會無緣無故跑Zip運行兩次以上(直到All要求) - 因爲有兩個迭代AllLinq懶惰地評估它們。

+0

您確定要迭代多次?是不是因爲ZIP只是迭代真正需要的元素,所以如果我在ALL的第三次迭代中發現結果是錯誤的,並且因此ALL的其餘部分沒有迭代,其餘的也沒有壓縮? –

+0

@HaraldDutch由於有兩個'All'和'Linq'迭代懶惰地評估它們,因此壓縮將被執行兩次。 –

+0

@TamirVered All()停止處理,如果發現一個無效字符,直到** O(n)**正確? – c0rd

0

我會去簡單迭代所有元素:

string str = "whatever123"; 
Func<char, char, bool> pred; 
bool? asc = str.TakeWhile((q, i) => i < str.Length - 1) 
       .Select((q, i) => str[i] == str[i+1] ? (bool?)null : str[i] < str[i+1]) 
       .FirstOrDefault(q => q.HasValue); 
if (!asc.HasValue) 
    return true; //all chars are the same 
if (asc.Value) 
    pred = (c1, c2) => c1 <= c2; 
else 
    pred = (c1, c2) => c1 >= c2; 

for (int i = 0; i < str.Length - 1; ++i) 
{ 
    if (!pred(str[i], str[i + 1])) 
     return false; 
} 
return true; 
+0

__Operator'> ='不能應用於'bool'和'char'類型的操作數__ – c0rd

+0

我總是得到'Func'聲明錯誤,我改了它,請立即嘗試 – slawekwin

+0

確定現在可以正常工作,但是返回'「aabcdefghijklmnopqrstuvwxyz」'false – c0rd

3

它需要一個reducer。在C#中,它是Enumerable.Aggregate。這是O(n)算法。

var query = "123abc".Aggregate(new { asceding = true, descending = true, prev = (char?)null }, 
(result, currentChar) => 
    new 
    { 
     asceding = result.prev == null || result.asceding && currentChar >= result.prev, 
     descending = result.prev == null || result.descending && currentChar <= result.prev, 
     prev = (char?)currentChar 
    } 
); 
Console.WriteLine(query.asceding || query.descending); 
1

您可以不必比較所有的做多接受的答案要好得多的元素:

var s = "2349"; 
var r = Enumerable.Range(1, s.Length - 1); 
//var isAscending = r.All(i => s[i - 1] <= s[i]); 
//var isDescending = r.All(i => s[i - 1] >= s[i]); 
var isOrdered = r.All(i => s[i - 1] <= s[i]) || r.All(i => s[i - 1] >= s[i]); 
相關問題