diff --git a/CHANGELOG.md b/CHANGELOG.md index 24138178..6914257e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.7] - 2021-02-09 +### Changed +- Optimize PriorityQueue + ## [1.2.6] - 2021-02-09 ### Added - Add PriorityQueue.TryDequeue diff --git a/Directory.Build.props b/Directory.Build.props index 1be78654..d30fa0f8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,8 +7,8 @@ https://github.com/naminodarie/ac-library-csharp https://github.com/naminodarie/ac-library-csharp/blob/master/CHANGELOG.md - 1.2.6 - 1.2.6.100 + 1.2.7 + 1.2.7.100 $(GIT_COMMIT) True diff --git a/Source/AtCoderLibrary/STL/Internal/IPriorityQueueOp.cs b/Source/AtCoderLibrary/STL/Internal/IPriorityQueueOp.cs new file mode 100644 index 00000000..1f0cbd8b --- /dev/null +++ b/Source/AtCoderLibrary/STL/Internal/IPriorityQueueOp.cs @@ -0,0 +1,12 @@ +namespace AtCoder.Internal +{ + public interface IPriorityQueueOp + { + int Count { get; } + T Peek { get; } + void Add(T value); + T Dequeue(); + bool TryDequeue(out T result); + void Clear(); + } +} diff --git a/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp.cs b/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp.cs index 9cbf644a..59250c2c 100644 --- a/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp.cs +++ b/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp.cs @@ -1,12 +1,15 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace AtCoder.Internal { + [DebuggerTypeProxy(typeof(PriorityQueueOp<,>.DebugView))] [DebuggerDisplay("Count = {" + nameof(Count) + "}")] - public class PriorityQueueOp where TOp : IComparer + public class PriorityQueueOp : IPriorityQueueOp, IEnumerable + where TOp : IComparer { protected T[] data; protected readonly TOp _comparer; @@ -88,16 +91,23 @@ protected internal void UpdateDown(int i) data[i] = tar; } public void Clear() => Count = 0; - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051", Justification = "for debugger")] - private T[] Items + + private T[] GetItems() + { + var arr = new ArraySegment(data, 0, Count).ToArray(); + Array.Sort(arr, _comparer); + return arr; + } + IEnumerator IEnumerable.GetEnumerator() => GetItems().GetEnumerator(); + private class DebugView { - get + private readonly PriorityQueueOp pq; + public DebugView(PriorityQueueOp pq) { - var arr = new ArraySegment(data, 0, Count).ToArray(); - Array.Sort(arr, _comparer); - return arr; + this.pq = pq; } + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Items => pq.GetItems(); } } } diff --git a/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp`2.cs b/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp`2.cs index 9469ad59..6020e735 100644 --- a/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp`2.cs +++ b/Source/AtCoderLibrary/STL/Internal/PriorityQueueOp`2.cs @@ -1,21 +1,52 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; namespace AtCoder.Internal { + [DebuggerTypeProxy(typeof(PriorityQueueOp<,,>.DebugView))] [DebuggerDisplay("Count = {" + nameof(Count) + "}")] - public class PriorityQueueOp - : PriorityQueueOp, KeyComparer> + public class PriorityQueueOp : + IPriorityQueueOp>, IEnumerable where TKOp : IComparer { + protected TKey[] keys; + protected TValue[] values; + protected readonly TKOp _comparer; + internal const int DefaultCapacity = 16; public PriorityQueueOp() : this(default(TKOp)) { } public PriorityQueueOp(int capacity) : this(capacity, default(TKOp)) { } - public PriorityQueueOp(TKOp comparer) : base(new KeyComparer(comparer)) { } - public PriorityQueueOp(int capacity, TKOp comparer) : base(capacity, new KeyComparer(comparer)) { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Add(TKey key, TValue value) => Add(new KeyValuePair(key, value)); + public PriorityQueueOp(TKOp comparer) : this(DefaultCapacity, comparer) { } + public PriorityQueueOp(int capacity, TKOp comparer) + { + if (comparer == null) + throw new ArgumentNullException(nameof(comparer)); + keys = new TKey[Math.Max(capacity, DefaultCapacity)]; + values = new TValue[Math.Max(capacity, DefaultCapacity)]; + _comparer = comparer; + } + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public int Count { get; private set; } = 0; + public KeyValuePair Peek => KeyValuePair.Create(keys[0], values[0]); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Resize() + { + Array.Resize(ref keys, keys.Length << 1); + Array.Resize(ref values, values.Length << 1); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(KeyValuePair pair) => Add(pair.Key, pair.Value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(TKey key, TValue value) + { + if (Count >= keys.Length) Resize(); + keys[Count] = key; + values[Count++] = value; + UpdateUp(Count - 1); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryDequeue(out TKey key, out TValue value) { @@ -28,5 +59,85 @@ public bool TryDequeue(out TKey key, out TValue value) (key, value) = Dequeue(); return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryDequeue(out KeyValuePair result) + { + if (Count == 0) + { + result = default(KeyValuePair); + return false; + } + result = Dequeue(); + return true; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyValuePair Dequeue() + { + var res = KeyValuePair.Create(keys[0], values[0]); + keys[0] = keys[--Count]; + values[0] = values[Count]; + UpdateDown(0); + return res; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void UpdateUp(int i) + { + var tar = keys[i]; + var tarVal = values[i]; + while (i > 0) + { + var p = (i - 1) >> 1; + if (_comparer.Compare(tar, keys[p]) >= 0) + break; + keys[i] = keys[p]; + values[i] = values[p]; + i = p; + } + keys[i] = tar; + values[i] = tarVal; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal void UpdateDown(int i) + { + var tar = keys[i]; + var tarVal = values[i]; + var n = Count; + var child = 2 * i + 1; + while (child < n) + { + if (child != n - 1 && _comparer.Compare(keys[child], keys[child + 1]) > 0) child++; + if (_comparer.Compare(tar, keys[child]) <= 0) + break; + keys[i] = keys[child]; + values[i] = values[child]; + i = child; + child = 2 * i + 1; + } + keys[i] = tar; + values[i] = tarVal; + } + public void Clear() => Count = 0; + + private KeyValuePair[] GetItems() + { + var keys = new ArraySegment(this.keys, 0, Count).ToArray(); + var values = new ArraySegment(this.values, 0, Count).ToArray(); + Array.Sort(keys, values, _comparer); + var arr = new KeyValuePair[Count]; + for (int i = 0; i < arr.Length; i++) + arr[i] = KeyValuePair.Create(keys[i], values[i]); + return arr; + } + IEnumerator IEnumerable.GetEnumerator() => GetItems().GetEnumerator(); + private class DebugView + { + private readonly PriorityQueueOp pq; + public DebugView(PriorityQueueOp pq) + { + this.pq = pq; + } + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public KeyValuePair[] Items => pq.GetItems(); + } } }