zoukankan      html  css  js  c++  java
  • List<T>列表通用过滤

    List<T>列表通用过滤模块设计

    需求描述

    数据列表如List<Customer> 加载到DataGridView后,如果记录比较多可能需要对其进行二次过滤,即客户端过滤
    过滤条件做成可由用户设置的,如下图:

    在数据源是DataTable时,使用DataView的RowFilter可以轻松按用户的配置拼接出过滤表达式字符串来,
    设置RowFilter就可以实现过滤效果,但是当数据源是List<T>这样由EF,Linq to sql 等框架返回的集合时要实现上面的功能就需要费点力气了。

    问题分析:

    首先参考上面的截图,用户设置好过滤条件后会形成:" (工号 = 222 And 部门=人力) Or 性别=女" 这样的过滤表达式,可以表示成(Exp1 And Exp2) Or Exp3 这样的形式.针对"工号=222"这样的Exp求值我们会转变成针对Employe实体的EmpId属性是否等于222的判断(Employe.EmpId==222),这个可以通过反射方式来实现,将多个Exp求值的结果通过And或Or连接并运算得出最终结果,True表示这一行(Employe)符合.

    不过考虑Exp1 Or Exp2 Or Exp3  这样的条件,如果第一个Exp1是True的话结果必定是True,这个时候还去计算Exp2,Exp3是完全多余的,如果List集合有几万条记录(当然超过几千行的列表对用户来说是没有多少意义的,一般人不会看那么多行,这个时候应该想想过滤条件设置是否合理)那么针对列表的每个实体的每个属性(字段)使用反射的方式计算一遍Exp将是一个比较大的开销,好在And与Or跟算术操作符(+,-,*,/)有所不同,And运算时只要两个操作数中有一个是False就没必要计算另外一个操作数(这里的是Exp)而Or在一个操作数是True时就可以忽略另一个操作数。不过当所的Exp都是false时针对上面"Exp1 Or Exp2 Or Exp3"这样的表达式计算每个Exp是不可避免的

    到这里我们可以看到该问题的本质就是表达式求值,而操作符只限And与Or两个二元操作符,最后结果是True或False.

    设计实现:


    首先我们将用户设置的过滤表达式转变成逆波兰式(后缀表达式),接着传入每个要判断的实体,使用后缀表达式求出该实体是否符合过滤条件,
    当然我们也可以将后缀表达式构建成Expression树,接着将该Expression编译成动态方法(委托),使用该委托对每个实体做出判断,下面的代码给出了这两种实现,
    经过测试发现两种方法速度区别不大。

    自己对逆波兰式求值时需要下面的判定表,如果构建Expression树则Expression.AndAlso或Expression.OrElse会自己判断是否对两个操作数都进行计算(参考下面的代码)

    代码:

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Linq.Expressions;
    using System.Reflection;
    namespace FIStudio.WinUI.Core
    {
        public class ExpParser
        {
            
            public ExpParser(List<Elem> midfixList)
            {
                this.MidfixList = midfixList;
    
            }
            public List<Elem> PostfixList { get; private set; }
            public List<Elem> MidfixList { get; private set; }
            private Stack<Elem> CalcStack = new Stack<Elem>();
            private void GuardMidfixListExist()
            {
                if (MidfixList == null || MidfixList.Count <= 0) throw new Exception("中序列表为null或为空!");
            }
            private void EnsurePostfixReady()
            {
                if (PostfixList == null)
                {
                    PostfixList = DoParse(MidfixList);
                    if (PostfixList == null || PostfixList.Count <= 0) throw new Exception("后序列表为null或为空!");
                }
            }
            /// <summary>
            /// 判断元素是否符合要求
            /// </summary>
            /// <param name="ent"></param>
            /// <returns></returns>
            public bool IsSatisfy(object ent)
            {
                GuardMidfixListExist();
                EnsurePostfixReady();
    
    
                CalcStack.Clear();
                foreach (var item in PostfixList)
                {
                    if (item is ExpElem)
                    {
                        CalcStack.Push(item);
                        continue;
                    }
                    #region And 运算
                    if (item is AndElem)
                    {
                        var op1 = CalcStack.Pop() as ExpElem;
                        var op2 = CalcStack.Pop() as ExpElem;
    
                        //任意一个是false则直接压入false
                        if (op1.Result == false || op2.Result == false)
                        {
                            CalcStack.Push(new ExpElem() { Result = false });
                            continue;
                        }
    
                        if (!op1.Result.HasValue && !op2.Result.HasValue)
                        {
                            op1.Compare(ent);
                            if (op1.Result.Value == false)
                            {
                                CalcStack.Push(new ExpElem() { Result = false });
                                continue;
                            }
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                            continue;
                        }
                        if (!op1.Result.HasValue && op2.Result == true)
                        {
                            op1.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op1.Result });
                            continue;
                        }
                        if (op1.Result == true && !op2.Result.HasValue)
                        {
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                            continue;
    
                        }
                        if (op1.Result == true && op2.Result == true)
                        {
                            CalcStack.Push(new ExpElem() { Result = true });
                            continue;
                        }
    
                    }
                    #endregion
                    #region Or 运算
                    if (item is OrElem)
                    {
                        var op1 = CalcStack.Pop() as ExpElem;
                        var op2 = CalcStack.Pop() as ExpElem;
    
                        //任意一个是true则直接压入true
                        if (op1.Result == true || op1.Result == true)
                        {
                            CalcStack.Push(new ExpElem() { Result = true });
                            continue;
                        }
    
                        if (!op1.Result.HasValue && !op2.Result.HasValue)
                        {
                            op1.Compare(ent);
                            if (!op1.Result == true)
                            {
                                CalcStack.Push(new ExpElem() { Result = true });
                                continue;
                            }
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                        }
                        if (!op1.Result.HasValue && op2.Result == false)
                        {
                            op1.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op1.Result });
                            continue;
                        }
                        if (op1.Result == false && !op2.Result.HasValue)
                        {
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
    
                            continue;
                        }
                        if (op1.Result == false && op2.Result == false)
                        {
                            CalcStack.Push(new ExpElem() { Result = false });
                        }
    
                    }
                    #endregion
                }
                return (CalcStack.Pop() as ExpElem).Result.Value;
    
            }
            /// <summary>
            /// 生成判断函数
            /// </summary>
            /// <returns></returns>
            public  Expression<Func<T,bool>> GenIsSatisfyFunc<T>()
            {
                GuardMidfixListExist();
                EnsurePostfixReady();
                Stack<object> stack = new Stack<object>();
    
                ParameterExpression entExp = Expression.Parameter(typeof(T), "ent");
                foreach (var elem in PostfixList)
                {
    
                    if (elem is ExpElem)
                    {
                        stack.Push(elem);
                        continue;
                    }
                    if (elem is AndElem)
                    {
                        var elem1 = stack.Pop();
                        var elem2 = stack.Pop();
    
                        var exp= Expression.AndAlso(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
                        
                        stack.Push(exp);
                        continue;
                    }
                    if(elem is OrElem)
                    {
                        var elem1 = stack.Pop();
                        var elem2 = stack.Pop();
    
                        var exp= Expression.OrElse(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
                        
                        stack.Push(exp);
                        continue;
                    }
                }
                LambdaExpression lambda= Expression.Lambda<Func<T,bool>>( stack.Pop() as Expression,entExp);
    
                return lambda as  Expression<Func<T,bool>>;
    
            }
            private Expression GetCallExpression(object elem, ParameterExpression entExp)
            {
                if (elem is ExpElem)
                {
                   return Expression.Call(Expression.Constant(elem), typeof(ExpElem).GetMethod("Compare"), entExp);
                }
                return elem as Expression;
            }
            /// <summary>
            /// 中序表达式转后缀表达式
            /// </summary>
            /// <param name="midfix"></param>
            /// <returns></returns>
            private  List<Elem> DoParse(List<Elem> midfix)
            {
                Stack<Elem> stack = new Stack<Elem>();
                var list=new List<Elem>();
                foreach (var elem in midfix)
                {
                    if (elem is ExpElem)
                    {
                        list.Add(elem);
                        continue;
                    }
                    if (elem is LBElem)
                    {
                        stack.Push(elem);
                        continue;
                    }
                    if (elem is RBElem)
                    {
                        var e = stack.Pop();
                        while (!(e is LBElem))
                        {
                            list.Add(e);
                            e = stack.Pop();
                          
    
                        }
                        continue;
                    }
                    if((elem is AndElem) || (elem is OrElem))
                    {
                        if (stack.Count > 0)
                        {
                            var e = stack.Peek();
                            while ( !(e is LBElem) && elem.Priority <= e.Priority)
                            {
                                list.Add(stack.Pop());
                                if (stack.Count <= 0) break;
                                e = stack.Peek();
                            }
                        }
                        stack.Push(elem);
                    }
    
                }
                while (stack.Count > 0)
                {
                    list.Add(stack.Pop());
                }
    
                return list;
    
            }
        }
        #region 节点定义
        public class Elem
        {
            public virtual string Name { get; set; }
            public virtual int Priority { get; set; }
            public Object Data { get; set; }
        }
        /// <summary>
        /// 左括号
        /// 注意stack中只会压入'(','And','Or'
        /// </summary>
        public class LBElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "(";
                }
            }
            public override int Priority
            {
                get
                {
                    return 59;
                }
            }
        }
        /// <summary>
        /// 右括号
        /// </summary>
        public class RBElem : Elem
        {
            public override string Name
            {
                get
                {
                    return ")";
                }
            }
            public override int Priority
            {
                get
                {
                    return 99;
                }
            }
    
        }
    
        public class AndElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "And";
                }
            }
            public override int Priority
            {
                get
                {
                    return 88;
                }
            }
        }
    
        public class OrElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "Or";
                }
            }
            public override int Priority
            {
                get
                {
                    return 77;
                }
            }
        }
        public class ExpElem : Elem
        {
    
            public override int Priority
            {
                get
                {
                    return 66;
                }
            }
            public bool Compare(object ent)
            {
                Console.WriteLine("计算了:" + Name);
                bool? ret=null;
                if (AssertType == Core.CompareType.Equal)
                {
                    ret= string.Compare(GetV(ent),Value,true)==0;
                }
                if (AssertType == Core.CompareType.NotEqual)
                {
                    ret =  string.Compare(GetV(ent), Value, true) != 0;
                }
                if (AssertType == Core.CompareType.Greate)
                {
                    ret = string.Compare(GetV(ent), Value, true) > 0;
                }
                if (AssertType == Core.CompareType.GreateOrEqual)
                {
                    ret = string.Compare(GetV(ent), Value, true) >= 0;
                }
                if (AssertType == Core.CompareType.Less)
                {
                    ret = string.Compare(GetV(ent), Value, true) < 0;
                }
                if (AssertType == Core.CompareType.LessOrEqual)
                {
                    ret = string.Compare(GetV(ent), Value, true) <= 0;
                }
                if (AssertType == Core.CompareType.Contains)
                {
                    ret = GetV(ent).Contains(Value);
                }
                if (AssertType == Core.CompareType.NoContains)
                {
                    ret =! GetV(ent).Contains(Value);
                }
                if (AssertType == Core.CompareType.StartWith)
                {
                    ret = GetV(ent).StartsWith(Value);
                }
                if (AssertType == Core.CompareType.EndWith)
                {
                    ret = GetV(ent).EndsWith(Value);
                }
                if (!ret.HasValue) throw new Exception("未知的CompareType!");
                Result = ret;
                return ret.Value;
    
               
            }
            public bool? Result { get; set; }
            public PropertyInfo Property { get; set; }
            public CompareType AssertType { get; set; }
            public string Value { get; set; }
    
            private string GetV(object ent)
            {
                var tmp= Property.GetValue(ent, null);
                if (tmp == null) tmp = string.Empty;
                return tmp.ToString();
            }
    
            
        }
    
        public enum CompareType { Equal, NotEqual, Less, LessOrEqual, Greate, GreateOrEqual, Contains, NoContains, StartWith, EndWith };
    
    #endregion
    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Linq.Expressions;
    using System.Reflection;
    namespace FIStudio.WinUI.Core
    {
        public class ExpParser
        {
            
            public ExpParser(List<Elem> midfixList)
            {
                this.MidfixList = midfixList;
    
            }
            public List<Elem> PostfixList { get; private set; }
            public List<Elem> MidfixList { get; private set; }
            private Stack<Elem> CalcStack = new Stack<Elem>();
            private void GuardMidfixListExist()
            {
                if (MidfixList == null || MidfixList.Count <= 0) throw new Exception("中序列表为null或为空!");
            }
            private void EnsurePostfixReady()
            {
                if (PostfixList == null)
                {
                    PostfixList = DoParse(MidfixList);
                    if (PostfixList == null || PostfixList.Count <= 0) throw new Exception("后序列表为null或为空!");
                }
            }
            /// <summary>
            /// 判断元素是否符合要求
            /// </summary>
            /// <param name="ent"></param>
            /// <returns></returns>
            public bool IsSatisfy(object ent)
            {
                GuardMidfixListExist();
                EnsurePostfixReady();
    
    
                CalcStack.Clear();
                foreach (var item in PostfixList)
                {
                    if (item is ExpElem)
                    {
                        CalcStack.Push(item);
                        continue;
                    }
                    #region And 运算
                    if (item is AndElem)
                    {
                        var op1 = CalcStack.Pop() as ExpElem;
                        var op2 = CalcStack.Pop() as ExpElem;
    
                        //任意一个是false则直接压入false
                        if (op1.Result == false || op2.Result == false)
                        {
                            CalcStack.Push(new ExpElem() { Result = false });
                            continue;
                        }
    
                        if (!op1.Result.HasValue && !op2.Result.HasValue)
                        {
                            op1.Compare(ent);
                            if (op1.Result.Value == false)
                            {
                                CalcStack.Push(new ExpElem() { Result = false });
                                continue;
                            }
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                            continue;
                        }
                        if (!op1.Result.HasValue && op2.Result == true)
                        {
                            op1.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op1.Result });
                            continue;
                        }
                        if (op1.Result == true && !op2.Result.HasValue)
                        {
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                            continue;
    
                        }
                        if (op1.Result == true && op2.Result == true)
                        {
                            CalcStack.Push(new ExpElem() { Result = true });
                            continue;
                        }
    
                    }
                    #endregion
                    #region Or 运算
                    if (item is OrElem)
                    {
                        var op1 = CalcStack.Pop() as ExpElem;
                        var op2 = CalcStack.Pop() as ExpElem;
    
                        //任意一个是true则直接压入true
                        if (op1.Result == true || op1.Result == true)
                        {
                            CalcStack.Push(new ExpElem() { Result = true });
                            continue;
                        }
    
                        if (!op1.Result.HasValue && !op2.Result.HasValue)
                        {
                            op1.Compare(ent);
                            if (!op1.Result == true)
                            {
                                CalcStack.Push(new ExpElem() { Result = true });
                                continue;
                            }
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
                        }
                        if (!op1.Result.HasValue && op2.Result == false)
                        {
                            op1.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op1.Result });
                            continue;
                        }
                        if (op1.Result == false && !op2.Result.HasValue)
                        {
                            op2.Compare(ent);
                            CalcStack.Push(new ExpElem() { Result = op2.Result });
    
                            continue;
                        }
                        if (op1.Result == false && op2.Result == false)
                        {
                            CalcStack.Push(new ExpElem() { Result = false });
                        }
    
                    }
                    #endregion
                }
                return (CalcStack.Pop() as ExpElem).Result.Value;
    
            }
            /// <summary>
            /// 生成判断函数
            /// </summary>
            /// <returns></returns>
            public  Expression<Func<T,bool>> GenIsSatisfyFunc<T>()
            {
                GuardMidfixListExist();
                EnsurePostfixReady();
                Stack<object> stack = new Stack<object>();
    
                ParameterExpression entExp = Expression.Parameter(typeof(T), "ent");
                foreach (var elem in PostfixList)
                {
    
                    if (elem is ExpElem)
                    {
                        stack.Push(elem);
                        continue;
                    }
                    if (elem is AndElem)
                    {
                        var elem1 = stack.Pop();
                        var elem2 = stack.Pop();
    
                        var exp= Expression.AndAlso(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
                        
                        stack.Push(exp);
                        continue;
                    }
                    if(elem is OrElem)
                    {
                        var elem1 = stack.Pop();
                        var elem2 = stack.Pop();
    
                        var exp= Expression.OrElse(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
                        
                        stack.Push(exp);
                        continue;
                    }
                }
                LambdaExpression lambda= Expression.Lambda<Func<T,bool>>( stack.Pop() as Expression,entExp);
    
                return lambda as  Expression<Func<T,bool>>;
    
            }
            private Expression GetCallExpression(object elem, ParameterExpression entExp)
            {
                if (elem is ExpElem)
                {
                   return Expression.Call(Expression.Constant(elem), typeof(ExpElem).GetMethod("Compare"), entExp);
                }
                return elem as Expression;
            }
            /// <summary>
            /// 中序表达式转后缀表达式
            /// </summary>
            /// <param name="midfix"></param>
            /// <returns></returns>
            private  List<Elem> DoParse(List<Elem> midfix)
            {
                Stack<Elem> stack = new Stack<Elem>();
                var list=new List<Elem>();
                foreach (var elem in midfix)
                {
                    if (elem is ExpElem)
                    {
                        list.Add(elem);
                        continue;
                    }
                    if (elem is LBElem)
                    {
                        stack.Push(elem);
                        continue;
                    }
                    if (elem is RBElem)
                    {
                        var e = stack.Pop();
                        while (!(e is LBElem))
                        {
                            list.Add(e);
                            e = stack.Pop();
                          
    
                        }
                        continue;
                    }
                    if((elem is AndElem) || (elem is OrElem))
                    {
                        if (stack.Count > 0)
                        {
                            var e = stack.Peek();
                            while ( !(e is LBElem) && elem.Priority <= e.Priority)
                            {
                                list.Add(stack.Pop());
                                if (stack.Count <= 0) break;
                                e = stack.Peek();
                            }
                        }
                        stack.Push(elem);
                    }
    
                }
                while (stack.Count > 0)
                {
                    list.Add(stack.Pop());
                }
    
                return list;
    
            }
        }
        #region 节点定义
        public class Elem
        {
            public virtual string Name { get; set; }
            public virtual int Priority { get; set; }
            public Object Data { get; set; }
        }
        /// <summary>
        /// 左括号
        /// 注意stack中只会压入'(','And','Or'
        /// </summary>
        public class LBElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "(";
                }
            }
            public override int Priority
            {
                get
                {
                    return 59;
                }
            }
        }
        /// <summary>
        /// 右括号
        /// </summary>
        public class RBElem : Elem
        {
            public override string Name
            {
                get
                {
                    return ")";
                }
            }
            public override int Priority
            {
                get
                {
                    return 99;
                }
            }
    
        }
    
        public class AndElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "And";
                }
            }
            public override int Priority
            {
                get
                {
                    return 88;
                }
            }
        }
    
        public class OrElem : Elem
        {
            public override string Name
            {
                get
                {
                    return "Or";
                }
            }
            public override int Priority
            {
                get
                {
                    return 77;
                }
            }
        }
        public class ExpElem : Elem
        {
    
            public override int Priority
            {
                get
                {
                    return 66;
                }
            }
            public bool Compare(object ent)
            {
                Console.WriteLine("计算了:" + Name);
                bool? ret=null;
                if (AssertType == Core.CompareType.Equal)
                {
                    ret= string.Compare(GetV(ent),Value,true)==0;
                }
                if (AssertType == Core.CompareType.NotEqual)
                {
                    ret =  string.Compare(GetV(ent), Value, true) != 0;
                }
                if (AssertType == Core.CompareType.Greate)
                {
                    ret = string.Compare(GetV(ent), Value, true) > 0;
                }
                if (AssertType == Core.CompareType.GreateOrEqual)
                {
                    ret = string.Compare(GetV(ent), Value, true) >= 0;
                }
                if (AssertType == Core.CompareType.Less)
                {
                    ret = string.Compare(GetV(ent), Value, true) < 0;
                }
                if (AssertType == Core.CompareType.LessOrEqual)
                {
                    ret = string.Compare(GetV(ent), Value, true) <= 0;
                }
                if (AssertType == Core.CompareType.Contains)
                {
                    ret = GetV(ent).Contains(Value);
                }
                if (AssertType == Core.CompareType.NoContains)
                {
                    ret =! GetV(ent).Contains(Value);
                }
                if (AssertType == Core.CompareType.StartWith)
                {
                    ret = GetV(ent).StartsWith(Value);
                }
                if (AssertType == Core.CompareType.EndWith)
                {
                    ret = GetV(ent).EndsWith(Value);
                }
                if (!ret.HasValue) throw new Exception("未知的CompareType!");
                Result = ret;
                return ret.Value;
    
               
            }
            public bool? Result { get; set; }
            public PropertyInfo Property { get; set; }
            public CompareType AssertType { get; set; }
            public string Value { get; set; }
    
            private string GetV(object ent)
            {
                var tmp= Property.GetValue(ent, null);
                if (tmp == null) tmp = string.Empty;
                return tmp.ToString();
            }
    
            
        }
    
        public enum CompareType { Equal, NotEqual, Less, LessOrEqual, Greate, GreateOrEqual, Contains, NoContains, StartWith, EndWith };
    
    #endregion
    
    }
    复制代码

    参考:

      逆波兰式构建方法

      1、从左至右扫描一中缀表达式。
                    2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
                    3、若读取的是运算符
                       (1) 该运算符为左括号"(",则直接存入运算符堆栈。
                       (2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止,此时抛弃该左括号。
                       (3) 该运算符为非括号运算符:
                           (a) 若运算符堆栈栈顶的运算符为左括号,则直接存入运算符堆栈。
                           (b) 若比运算符堆栈栈顶的运算符优先级高,则直接存入运算符堆栈。
                           (c) 若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,
                                直至运算符栈栈顶运算符低于(不包括等于)该运算符优先级,或为左括号,并将当前运算符压入运算符堆栈。
                    4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

       逆波兰表达式求值算法:

                   1、循环扫描语法单元的项目。
                   2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
                   3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
                   4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
                   5、将运算结果重新压入堆栈。
                   6、重复步骤2-5,堆栈中即为结果值。

      资源

    字符串公式解析器——使用“逆波兰式算法”及C#实现

     
     
  • 相关阅读:
    LeetCode 226. Invert Binary Tree
    LeetCode 221. Maximal Square
    LeetCode 217. Contains Duplicate
    LeetCode 206. Reverse Linked List
    LeetCode 213. House Robber II
    LeetCode 198. House Robber
    LeetCode 188. Best Time to Buy and Sell Stock IV (stock problem)
    LeetCode 171. Excel Sheet Column Number
    LeetCode 169. Majority Element
    运维工程师常见面试题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2708817.html
Copyright © 2011-2022 走看看