cad.net 排序

行式排序

// 容差需要set,而Point3d的无法set,直接暴露字段或者封装到构造函数内.
public class XYZD {
    public double X;
    public double Y;
    public double Z;
    public ObjectId ObjectId;

    public XYZD(double x, double y, double z, ObjectId id, double tol = 0.1) {
        if (tol <= 0)
            throw new ArgumentException("XYZD Tolerance <= zero.");
        if (double.IsNaN(tol))
            throw new ArgumentException("XYZD Tolerance is NaN."); 
        if (double.IsInfinity(tol))
            throw new ArgumentException("XYZD Tolerance is Infinity.");
        // 公差修正
        X = RoundToNearest(x, tol);
        Y = RoundToNearest(y, tol);
        Z = RoundToNearest(z, tol);
        ObjectId = id;
    }

    // 公差修正数值的方法
    double RoundToNearest(double value, double tol) {
        return Math.Round(value / tol) * tol;
    }

    // 内部比较器写法
    public int CompareTo(XYZD other) {
        // 如果X相同,比较Y,如果Y也相同,比较Z
        if (this.X != other.X) return this.X.CompareTo(other.X);
        if (this.Y != other.Y) return this.Y.CompareTo(other.Y);
        return this.Z.CompareTo(other.Z);
    }  
}

// 外部补充比较器写法
public class XYZDComparer : IComparer<XYZD> {
    public int Compare(XYZD a, XYZD b) {
        // 如果X相同,比较Y,如果Y也相同,比较Z
        if (a.X != b.X) return a.X.CompareTo(b.X);
        if (a.Y != b.Y) return a.Y.CompareTo(b.Y);
        return a.Z.CompareTo(b.Z);
    }
}

// 调用:
public List<XYZD> SortDbPoints(List<ObjectId> ids) {
    var tr = DBTrans.Top;
    List<XYZD> list = new();
    for(int i = 0; i<ids.Count; i++) {
        using var ent = tr.GetObject<DBPoint>(ids[i], OpenMode.ForRead);
        if(ent is null) continue;
        list.Add(new(ent.Position.X, ent.Position.Y, ent.Position.Z, ids[i]);
    }

    // 1 内部比较器,它只会修改原有数组
    list.Sort();

/*
    // 2 外部比较器需要new,它只会修改原有数组
    // 先行后列,先列后行...比较是千变万化的,因而外部设计其实比较好
    list.Sort(new XYZDComparer());

    // 3 使用Linq进行排序,它会生成新的数组.
    list = list.OrderBy(item => item.X)
        .ThenBy(item => item.Y)
        .ThenBy(item => item.Z)
        .ToList();
*/
     return list;
}

列式排序

这应该是最快的方法了,
原理是准备多个共同长度的数组,它们垂直对齐,因而叫列式数据.
利用快排算法把全部数组一起比较和交换.

1,结构是SOA,当排序完数组后,利用SIMD或者SIMT矩阵运算.
全部+1.0不就是平移吗.

2,第一个数组是有序的,可以使用二分法Array.BinarySearch,
其他数组是依附排序的,不过这个自带的二分法没有很好的处理重复元素.

3,两个XYZD类型比较时候是大量比较xList,触发CPU预读数组效果好.
对比两份图是否一致,快速找到不同位置,检查两个数组是否一样,
就是xList1[i]==xList2[i],就可以粗过滤,而yList就是细过滤.

4,储存的时候需要按照磁盘4k(4096byte)对齐,4k就是512个double(8字节/个).
(linux预读文件是一次128K,并可调整,win没有资料啊)
512/3=170.66,多出来不用(放校验码)
每个XYZD就是一页,然后Link<XYZD>就表示多页,
因为非连续结构在磁盘上面也是有意义的,
修改不需要全部写入,而是写入一个页并调整索引表就好了.

其他优化方向:
非托管数组1
非托管数组2
循环展开和SIMD

public class XYZD<T> {

enum SortOrder {
    None, // 无排序模式
    Asc, // 升序,从小到大
    Des // 降序,从大到小
}

class MyData {
    // 维度,载入顺序有优先级
    public List<List<double>> Mult = new(3); //限定Capacity
    // 排序方式
    public List<SortOrder> SortOrders = new(3);
    public int Count => SortOrders.Count;
    public int InfoCount => Mult.Count>0 ? Mult[0].Count : 0;
    public MyData Add(List<double> p, SortOrder so){
        Mult.Add(p);
        SortOrders.Add(so);
        return this;
    }
}

/*
    // 暴露成只读的数组视图
    public ReadOnlyCollection<double> X => _xList.AsReadOnly();
    public ReadOnlyCollection<double> Y => _yList.AsReadOnly();
    public ReadOnlyCollection<double> Z => _zList.AsReadOnly();
*/
    public List<double> _xList = [];
    public List<double> _yList = [];
    public List<double> _zList = [];
    public List<T> _idList = [];
    double _tol;
    bool _removeTrace = false; // 消除微量偏差

    public XYZD(double tol = 0.1, bool removeTrace = false) {
        if (tol <= 0)
            throw new ArgumentException("XYZD Tolerance <= zero.");
        if (double.IsNaN(tol))
            throw new ArgumentException("XYZD Tolerance is NaN."); 
        if (double.IsInfinity(tol))
            throw new ArgumentException("XYZD Tolerance is Infinity.");
        _tol = tol;
        _removeTrace = removeTrace;
    }

    public XYZD(List<Point3d> pts, List<T> ids,
        double tol = 0.1, bool removeTrace = false) :this(tol, removeTrace) {
        if(pts.Count != ids.Count)
            throw new ArgumentException("XYZD 你做咩唔对齐啊");

        // 这里应该写一个循环展开进行批量插入.
        for(int i = 0; i < ids.Count; i++) {
/*
            // 公差修正,可以不写在这里,而是线性比较的时候顺便处理,还可以减少除法器.
            _xList.Add(RoundToNearest(pts[i].X, _tol));
            _yList.Add(RoundToNearest(pts[i].Y, _tol));
            _zList.Add(RoundToNearest(pts[i].Z, _tol));
*/
            _xList.Add(pts[i].X);
            _yList.Add(pts[i].Y);
            _zList.Add(pts[i].Z);
            _idList.Add(ids[i]);
        }
    }

    // 加入时候展开数据,使得内部可以替换Point字段
    public void Add(Point3d pt, T id) {
        _xList.Add(pt.X);
        _yList.Add(pt.Y);
        _zList.Add(pt.Z);
        _idList.Add(id);
    }

    // 01下上分行,行左起
    public void SortRowToBTLR() {
        var md = new MyData()
            .Add(_yList, SortOrder.Asc)
            .Add(_xList, SortOrder.Asc)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 02下上分行,行右起
    public void SortRowToBTRL() {
        var md = new MyData()
            .Add(_yList, SortOrder.Asc)
            .Add(_xList, SortOrder.Des)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 03上下分行,行右起(图像和01一样,只是逆序)
    public void SortRowToTBRL() {
        var md = new MyData()
            .Add(_yList, SortOrder.Des)
            .Add(_xList, SortOrder.Des)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 04上下分行,行左起
    public void SortRowToTBLR() {
        var md = new MyData()
            .Add(_yList, SortOrder.Des)
            .Add(_xList, SortOrder.Asc)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 05左右分列,列下起
    public void SortColumnToLRBT() {
        var md = new MyData()
            .Add(_xList, SortOrder.Asc)
            .Add(_yList, SortOrder.Asc)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 06左右分列,列上起
    public void SortColumnToLRTB() {
        var md = new MyData()
            .Add(_xList, SortOrder.Asc)
            .Add(_yList, SortOrder.Des)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }
    
    // 07右左分列,列上起(图像和05一样,只是逆序)
    public void SortColumnToRLTB() {
        var md = new MyData()
            .Add(_xList, SortOrder.Des)
            .Add(_yList, SortOrder.Des)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    // 08右左分列,列下起
    public void SortColumnToRLBT() {
        var md = new MyData()
            .Add(_xList, SortOrder.Des)
            .Add(_yList, SortOrder.Asc)
            .Add(_zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount - 1);
    }

    /// <summary>
    /// 排序
    /// </summary>
    /// <param name="md">数据集</param>
    /// <param name="mdIndex">md索引,指定排序子集</param>
    /// <param name="start">排序的起始位置</param>
    /// <param name="end">排序的结束位置</param>
    void Sort(MyData md, int mdIndex, int start, int end) {
        if (end == start) return; // 一个就不需要排序了
        if (end - start < 0) throw new ("错误索引");
        if (md.InfoCount-1 - end  < 0) throw new ("错误索引超出");

        var so = md.SortOrders[mdIndex];
        if (so == SortOrder.None) return;

        var xyzList = md.Mult[mdIndex];
        var des = so == SortOrder.Des; // 降序
        QuickSort(xyzList, start, end, des);
        
        // 排序完之后,同X排序Y,同Y排序Z
        // 连续相同范围,起始索引
        int sameIndexStart = -1;

        // 运行中判断循环可能编译器没有循环展开和SIMD优化,
        // 我这么写只是为了demo更简短,最好应该独立写出正序循环和倒序循环

        // 排序方式翻转顺序,降序就倒序遍历
        if (des) (start,end)=(end,start);
        // 步长
        int step = des ? -1 : 1;

        for (int i = start; i != end; i += step) {
            int sp = i + step;

            // 因为已经排序和倒序循环,都是大-小,不需要abs的
            var sub = xyzList[sp] - xyzList[i];
            if (sub > _tol) {
                if (sameIndexStart != -1) {
                    // 行内排序
                    int sameIndexEnd = i;
                    // 索引保持左小右大,让排序方式进行翻转
                    if (sameIndexStart > sameIndexEnd){
                        (sameIndexStart,sameIndexEnd) = (sameIndexEnd,sameIndexStart);
                    }
                    Sort(md, mdIndex+1/*下一个排序数组*/, sameIndexStart, sameIndexEnd);
                    sameIndexStart = -1;
                }
                continue;
            }
            // 抹平微量偏差,斜线变成阶梯
            if (_removeTrace)
                xyzList[sp] = xyzList[i];
            if (sameIndexStart == -1) 
                sameIndexStart = i;
        }

        // 处理最后一个区间
        if (sameIndexStart != -1) {
            int sameIndexEnd = end;
            if (sameIndexStart > sameIndexEnd){
                (sameIndexStart,sameIndexEnd) = (sameIndexEnd,sameIndexStart);
            }
            Sort(md, mdIndex + 1, sameIndexStart, sameIndexEnd);
            sameIndexStart = -1;
        }
    }

    // 快排算法
    void QuickSort(List<double> list, int low, int high, bool descending) {
        if (low < high) {
            int pi = Partition(list, low, high, descending);
            QuickSort(list, low, pi - 1, descending);
            QuickSort(list, pi + 1, high, descending);
        }
    }

#if NET45_OR_GREATER
    [MethodImpl(MethodImplOptions.AggressiveInlining)]  
#endif
    int Partition(List<double> list, int low, int high, bool descending) {
        var pivot = list[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            // 这里存在相等条件,所以必须要大于小于
            if (descending ? list[j] > pivot : list[j] < pivot) {
                i++;
                Swap(_xList, i, j);
                Swap(_yList, i, j);
                Swap(_zList, i, j);
                Swap(_idList, i, j);
            }
        }
        Swap(_xList, i+1, high);
        Swap(_yList, i+1, high);
        Swap(_zList, i+1, high);
        Swap(_idList, i+1, high);
        return i + 1;
    }

#if NET45_OR_GREATER
    [MethodImpl(MethodImplOptions.AggressiveInlining)]  
#endif
    // 元组交换
    void Swap<T>(List<T> list, int index1, int index2) {
        (list[index1], list[index2]) = (list[index2], list[index1]);
    }

}

命令测试

[CommandMethod(nameof(TestCmdXYZD))]
public void TestCmdXYZD()
{
    var pso = new PromptSelectionOptions();
    var ssPsr = Env.Editor.GetSelection(pso);
    if (ssPsr.Status != PromptStatus.OK) return;

    using DBTrans tr = new();
    XYZD<ObjectId> xyzd = new(1.0); // 公差数值

    var ids = ssPsr.Value.GetObjectIds();
    for (int i = 0; i < ids.Length; i++)
    {
        using var ent = tr.GetObject<DBPoint>(ids[i], OpenMode.ForRead);
        if(ent is null) continue;
        xyzd.Add(ent.Position, ids[i]);
    }

    // 利用多段线查看排序
    if (xyzd._xList.Count < 2) return;

    xyzd.SortRowToBTLR();
    PolylinePrint(xyzd, 1);
    xyzd.SortRowToBTRL();
    PolylinePrint(xyzd, 2);
    xyzd.SortRowToTBRL();
    PolylinePrint(xyzd, 3);
    xyzd.SortRowToTBLR();
    PolylinePrint(xyzd, 4);
    xyzd.SortColumnToLRBT();
    PolylinePrint(xyzd, 5);
    xyzd.SortColumnToLRTB();
    PolylinePrint(xyzd, 6);
    xyzd.SortColumnToRLTB();
    PolylinePrint(xyzd, 7);
    xyzd.SortColumnToRLBT();
    PolylinePrint(xyzd, 8);
}

void PolylinePrint<T>(XYZD<T> xyzd, int color) {
    var tr = DBTrans.Top;
    // 多段线绘制排序路径
    Polyline pl = new();
    pl.SetDatabaseDefaults();
    pl.ColorIndex = color;
    for (int i = 0; i < xyzd._xList.Count; i++) 
        pl.AddVertexAt(i, new Point2d(xyzd._xList[i], xyzd._yList[i]), 0, 0, 0);
    tr.CurrentSpace.AddEntity(pl);

    // 用一个圆标注起点,方便测试
    Circle circle = new();
    circle.Center = new Point3d(xyzd._xList[0], xyzd._yList[0], 0); // 起点
    circle.Radius = 20;
    circle.ColorIndex = color;
    tr.CurrentSpace.AddEntity(circle);

    // 逆序的线看不出差别,用文字数据来区别.
    StringBuilder sb = new();
    for (int i = 0; i < xyzd._xList.Count; i++)
        sb.AppendLine($"({xyzd._xList[i]},{xyzd._yList[i]},{xyzd._zList[i]})");
    Env.Printl(sb.ToString());
    Env.Printl("**********************");
}

合并循环

因为存在运行时判断,因此这种方式C++编译器估计会,
无法循环展开和没有自动SIMD优化,
最好还是写成两个正序和倒序循环.

using System;
class Program {
    static void Main() {
        int[] array = { 1,2,3 };
        Console.WriteLine("true表示降序,false表示升序");
        bool des = true;

        int start = 0;
        int end = array.Length - 1;
        // 步长
        int step = des? -1 : 1;
        if (des) {
            int temp = start;
            start = end;
            end = temp;
        }
        Console.WriteLine();
        Console.WriteLine("合并1:");
        // 按照设定的顺序(正序或倒序)遍历数组元素并输出
        // 遍历每个
        for (int i = start; i != end + step; i += step)
            Console.Write($"({array[i]}) ");
        Console.WriteLine();
        // 两个两个
        for (int i = start; i != end; i += step)
            Console.WriteLine($"({array[i]}, {array[i + step]}) ");
        Console.WriteLine();

        Console.WriteLine("正序遍历数组:");
        for (int i = 0; i < array.Length; i++)
            Console.WriteLine(array[i]);
        Console.WriteLine("倒序遍历数组:");
        for (int i = array.Length - 1; i > -1; i--)
            Console.WriteLine(array[i]);
        Console.WriteLine("合并2:");
       start = 0;
       end = array.Length;
       if (des) {
         (start,end)=(end,start);
         start--;
         end--;
       }
       for (int i = start; des?i>end:i<end; i += step) {
            Console.WriteLine(array[i]);
        }
    }
}

输出结果:
(3) (2) (1)
(3, 2) (2, 1)

块排测试,从小到大

using System;

public class Program
{
    public static void Main(string[] args)
    {
        int[] arr = { 3, 6, 8, 10, 1, 2, 1 };
        QuickSort(arr, 0, arr.Length - 1);
        foreach (var item in arr)
        {
            Console.Write(item + " ");
        }
    }

    public static void QuickSort(int[] arr, int low, int high)
    {
        if (low < high)
        {
            int pi = Partition(arr, low, high);

            QuickSort(arr, low, pi - 1);  // Before pi
            QuickSort(arr, pi + 1, high); // After pi
        }
    }

    private static int Partition(int[] arr, int low, int high)
    {
        int pivot = arr[high];
        int i = (low - 1);

        for (int j = low; j < high; j++)
        {
            if (arr[j] < pivot)
            {
                i++;

                // Swap arr[i] and arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        // Swap arr[i+1] and arr[high] (or pivot)
        int temp2 = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp2;

        return i + 1;
    }
}

快排测试,从大到小

using System;
public class Program
{
    public static void Main()
    {
        int[] arr = { 3, 6, 8, 10, 1, 2, 1 };
        QuickSortDescending(arr, 0, arr.Length - 1);
        foreach (var item in arr)
        {
            Console.Write(item + " ");
        }
    }

    private static void QuickSortDescending(int[] arr, int left, int right)
    {
        if (left < right)
        {
            int pivotIndex = PartitionDescending(arr, left, right);
            QuickSortDescending(arr, left, pivotIndex - 1);
            QuickSortDescending(arr, pivotIndex + 1, right);
        }
    }

    private static int PartitionDescending(int[] arr, int left, int right)
    {
        int pivot = arr[right]; // 选择最右侧的元素作为基准
        int i = left - 1;
        for (int j = left; j < right; j++)
        {
            if (arr[j] > pivot) // 降序排序,只有这里不同,是大于
            {
                i++;
                // 交换arr[i]和arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // 交换arr[i+1]和arr[right](基准值)
        int temp2 = arr[i + 1];
        arr[i + 1] = arr[right];
        arr[right] = temp2;

        return i + 1; // 返回基准值的最终位置
    }
}

树排序

红黑树,AVL树,跳表,B树家族(B树/B+树/BW树/LSM树)
C#的有序结构都是不能重复的
SortedSet/SortedList/SortedDictionary

public class Point3dComparer : IComparer<Point3d> {
    public int Compare(Point3d a, Point3d b)  {
        // 容差一致
        if (a.IsEqualTo(b, Tolerance.Global)) return 0;
        // 如果X相同,比较Y,如果Y也相同,比较Z
        if (a.X != b.X) return a.X.CompareTo(b.X);
        if (a.Y != b.Y) return a.Y.CompareTo(b.Y);
        return a.Z.CompareTo(b.Z);
    }
}

// 调用
SortedSet set = new();
set.Add(new Point3d(1,1,1));
set.Add(new Point3d(21,1,1));
set.Add(new Point3d(1,1,1));

组合数

把int32分成三块,32/3=10bit,多出来2bit不用.
[空][X][Y][Z]
这样就能同X比较Y,同Y比较Z.
能利用上><=比较了.
缺点: 局限精度,10bit表达范围太少了

public struct Point3d {
    public override int GetCode() {
        // 公差值用于四舍五入
        double tol = 0.0001;
        // 四舍五入X, Y, Z到最接近的整数
        int roundedX = (int)Math.Round(X / tol) * (int)tol;
        int roundedY = (int)Math.Round(Y / tol) * (int)tol;
        int roundedZ = (int)Math.Round(Z / tol) * (int)tol;
        // 组合数
        return = roundedX << 20 | roundedY << 10 | roundedZ;
    }
}

公差原理

using System;

class Program
{
    static void Main()
    {
        // 千位舍入的例子
        double value1 = 2011;
        double tol1 = 1000;
        double roundedValue1 = RoundToNearest(value1, tol1);
        Console.WriteLine($"{value1} rounded to the nearest {tol1} is {roundedValue1}"); 
// 输出:2011 rounded to the nearest 1000 is 2000

        // 小数点后一位舍入的例子
        double value2 = 1.5;
        double tol2 = 0.1;
        double roundedValue2 = RoundToNearest(value2, tol2);
        Console.WriteLine($"{value2} rounded to the nearest {tol2} is {roundedValue2}"); 
// 输出:1.5 rounded to the nearest 0.1 is 1.5

        // 更多测试
        double value3 = 1234.5678;
        double tol3 = 0.05; // 保留两位小数
        double roundedValue3 = RoundToNearest(value3, tol3);
        Console.WriteLine($"{value3} rounded to the nearest {tol3} is {roundedValue3}"); 
// 输出:1234.5678 rounded to the nearest 0.05 is 1234.60
    }

    // 公差修正数值的方法
    static double RoundToNearest(double value, double tol)
    {
        if (tol == 0)
            throw new ArgumentException("Tolerance cannot be zero.");

        return Math.Round(value / tol) * tol;
    }
}

数据结构

为了加速我们用一些有序结构和轻量级结构:

StaticLinkedList

静态链表插入和删除速度非常快.

using System;

public class StaticLinkedList {
    private Node[] list;
    private int headIndex;
    private const int EMPTY_FLAG = -2;

    // 链表节点
    struct Node {
        public int data;
        public int next;
    }

    // 初始化链表长度,创建指定长度的节点数组,
    // 并对链表头和各节点的初始状态进行设置
    public StaticLinkedList(int length) {
        list = new Node[length];
        headIndex = 0;
        list[headIndex].next = -1;

        // 初始化所有节点为空闲状态(用-2表示空闲)
        for (int i = 0; i < length; i++) {
            list[i].next = EMPTY_FLAG;
        }
    }

    // 插入一个新节点,
    // 先找到链表末尾位置,
    // 然后查找空闲节点插入给定值的新节点,
    // 若插入成功则返回新节点在数组中的索引,
    // 若链表已满则返回 -1
    public int Insert(int value) {
        int i = headIndex;
        while (list[i].next!= -1) {
            i = list[i].next;
        }

        int newIndex = FindEmptyIndex();
        if (newIndex == -1) {
            Console.WriteLine("链表已满,无法插入");
            return -1;
        }
        list[newIndex].data = value;
        list[newIndex].next = list[i].next;
        list[i].next = newIndex;
        return newIndex;
    }

    // 查找返回链表中处于空闲状态的节点索引,
    // 若没有空闲节点则返回 -1
    private int FindEmptyIndex() {
        for (int i = 0; i < list.Length; i++) {
            if (list[i].next == EMPTY_FLAG) {
                list[i].next = -1;
                return i;
            }
        }
        return -1;
    }

    // 遍历整个静态链表,从链表头开始,
    // 顺着next指针依次输出每个节点的数据域值,
    // 直到next指针为 -1(表示链表末尾)
    public void Traverse() {
        int current = list[headIndex].next;
        while (current!= -1) {
            Console.Write(list[current].data + " ");
            current = list[current].next;
        }
        Console.WriteLine();
    }

    // 查找值为target的节点,
    // 若找到则返回其在链表中的位置索引(从0开始计数)
    // 若未找到则返回 -1
    public int Search(int target) {
        int current = list[headIndex].next;
        int index = 0;
        while (current!= -1) {
            if (list[current].data == target) {
                return index;
            }
            index++;
            current = list[current].next;
        }
        return -1;
    }

    // 删除值为target的节点,
    // 通过遍历链表查找目标节点,
    // 找到后调整指针来移除该节点,并将该节点设置为空闲状态,
    // 若成功删除返回true,否则返回false
    public bool Delete(int target) {
        int prev = headIndex;
        int current = list[headIndex].next;
        while (current!= -1) {
            if (list[current].data == target) {
                list[prev].next = list[current].next;
                list[current].next = EMPTY_FLAG;
                return true;
            }
            prev = current;
            current = list[current].next;
        }
        return false;
    }
}

class Program {
    static void Main() {
        StaticLinkedList staticLinkedList = new(10);
        staticLinkedList.Insert(5);
        staticLinkedList.Insert(10);
        staticLinkedList.Insert(15);

        staticLinkedList.Traverse();
        staticLinkedList.Delete(10);
        staticLinkedList.Traverse();
        int searchIndex = staticLinkedList.Search(10);
        if (searchIndex!= -1) {
            Console.WriteLine($"值10在静态链表中的索引是: {searchIndex}");
        }
        else {
            Console.WriteLine("未找到指定值");
        }
    }
}

有序不重复

官方C#自带结构都是不允许重复的
SortedSet/SortedList/SortedDictionary

获取范围不是O(logN)吗?高版本才有? https://cloud.tencent.com/developer/ask/sof/109384237
// var act = new Actuator(null,Sequence.StartFirst);
// var acts = _set.GetViewBetween(act, act);

using System;
using System.Collections.Generic;

class Program {
    class MyNumber {
        public int Value { get; set; }
        public MyNumber(int value) { Value = value; }
    }

    class Comparer1 : IComparer<MyNumber> {
        public int Compare(MyNumber x, MyNumber y) {
            return x.Value.CompareTo(y.Value);
        }
    }

    static void Main() {
        var s = new SortedSet<MyNumber>(new Comparer1());
        s.Add(new MyNumber(6));
        s.Add(new MyNumber(2));
        s.Add(new MyNumber(3));
        s.Add(new MyNumber(3));
        s.Add(new MyNumber(4));
        s.Add(new MyNumber(5));
        foreach (MyNumber num in s) {
            Console.Write(num.Value + " ");
        }
    }
}

有序重复

官方的二分法BinarySearch有问题,
只会返回首次出现,如果重复就会搜错位置,
需要再向左遍历,否则插入丢失的情况.

既然如此不如自己写一个二分法,
二分法用-1开始会令代码变得短少.
改用不安全数组会更好...貌似也没有必要优化那么极限.

官方之所以没写这种数据结构,估计是因为插入之后再快排会更快.
并且不可重复的,可以通过SortedList和SortedDictionary结构的value进行收集.
但是链式选择时候,需要[ab,bc,cd,de]进行(i+1)/2确定位置.

using System;
using System.Collections;
using System.Collections.Generic;

public class OrderList<T> : IEnumerable<T> {
    private List<T> _list = new List<T>(); 
    private IComparer<T> _cmp;
    public int Count => _list.Count;

    public OrderList() { _cmp = null; }
    public OrderList(IComparer<T> cmp = null) { _cmp = cmp; }
    IEnumerator IEnumerable.GetEnumerator() {
        return ((IEnumerable<T>)_list).GetEnumerator();
    }
	IEnumerator<T> IEnumerable<T>.GetEnumerator() {
        return _list.GetEnumerator();
    }

    // 二分法找最左边(修复了官方的无法找重复)
    public int FindFirst(T item) {
        int left = -1; int right = _list.Count;
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            int compareResult = _cmp != null ?
                _cmp.Compare(item, _list[mid]) : 
                Comparer<T>.Default.Compare(item, _list[mid]);
            if (compareResult == 0) right = mid;
            else if (compareResult < 0) right = mid;
            else left = mid;
        }
        int tempLeft = left + 1;
        while (tempLeft - 1 >= 0 && (_cmp != null ? 
            _cmp.Compare(item, _list[tempLeft - 1]) == 0 : 
            Comparer<T>.Default.Compare(item, _list[tempLeft - 1]) == 0)) {
            tempLeft--;
        }
        return tempLeft;
    }

    // 二分法找最右边
    public int FindLast(T item) {
        int left = -1; int right = _list.Count;
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            int compareResult = _cmp != null ?
                _cmp.Compare(item, _list[mid]) : 
                Comparer<T>.Default.Compare(item, _list[mid]);
            if (compareResult == 0) right = mid;
            else if (compareResult < 0) right = mid;
            else left = mid;
        }
        int tempRight = right - 1;
        while (tempRight + 1 < _list.Count && (_cmp != null ?
            _cmp.Compare(item, _list[tempRight + 1]) == 0 :
            Comparer<T>.Default.Compare(item, _list[tempRight + 1]) == 0)) {
            tempRight++;
        }
        return tempRight;
    }

    public void Add(T item) {
        var right = FindFirst(item);
        _list.Insert(right, item);
    }

    public void RemoveAt(int index) {
        if (index >= _list.Count)
            throw new IndexOutOfRangeException($"索引 {index} 超出了列表的有效范围");
        _list.RemoveAt(index);
    }

    public bool Remove(T item) {
        var left = FindFirst(item);
        var right = FindLast(item);
        if (left == -1 || right == _list.Count || left > right) {
            return false; 
        }
        for (int i = right; i >= left; i--) {
            _list.RemoveAt(i);
        }
        return true;
    }

    // 自带Contains是O(n),这个是O(log2(n))
    public bool Contains(T item) {
        var mid = FindFirst(item);
        int compareResult = _cmp != null ?
            _cmp.Compare(item, _list[mid]) : 
            Comparer<T>.Default.Compare(item, _list[mid]);
        return compareResult == 0;  
    }

    // 为了之后能用上SIMD,所以此处不要封装迭代器
    public List<T> GetList() {
        return _list;
    }

    public T this[int index] {
        get {
            if (index < 0 || index >= _list.Count)
                throw new IndexOutOfRangeException($"索引 {index} 超出了列表的有效范围");
            return _list[index];
        }
        set {
            if (index < 0 || index >= _list.Count)
                throw new IndexOutOfRangeException($"索引 {index} 超出了列表的有效范围");
            _list[index] = value;
        }
    }
}

class Program {
    static void Main() {
        OrderList<int> orderList = new OrderList<int>();
        orderList.Add(5);
        orderList.Add(6);
        orderList.Add(55);
        orderList.Add(3);
        orderList.Add(3);
        orderList.Add(3);
        orderList.Add(9);
        orderList.Add(6);

        orderList.GetList().ForEach(a=>Console.Write(a + " "));
        Console.WriteLine("");

        var left = orderList.FindFirst(3); // 重复
        var right = orderList.FindLast(3);
        Console.WriteLine($"重复 Left {left}, Right {right}");
        var left2 = orderList.FindFirst(5); // 唯一
        var right2 = orderList.FindLast(5);
        Console.WriteLine($"唯一 Left {left2}, Right {right2}");
        var none =  orderList.FindFirst(1); // 找不到,会指示插入位置是0;
        Console.WriteLine($"找不到插入位置:  {none}");
        Console.WriteLine($"找不到标识: {orderList.Contains(1)}");

        orderList.Remove(1);
        orderList.GetList().ForEach(a=>Console.Write(a + " "));
        Console.WriteLine("");

        orderList.Remove(3);
        orderList.GetList().ForEach(a=>Console.Write(a + " "));
        Console.WriteLine("");
    }
}

SimdList

由于List不能Simd指令,但是我前期需要动态扩容...


posted @ 2024-11-20 16:28  惊惊  阅读(194)  评论(0编辑  收藏  举报