C# 面试测试题

1. C#的委托是什么?有何用处?

  委托是存储方法的引用 

  安全,委托可以使方法作为参数传递到另一个方法内

2. 排序方式有哪些?

  

第一种:冒泡排序

冒泡排序我相信是每个程序员,都会学到的一种比较排序算法。非常简单,通过多次重复比较每对相邻元素,并按规定的顺序交换他们,最终把数列进行排序。

复制代码
public static IList<int> BubbleSort(IList<int> list)
        {
            try
            {
                //获取集合数量,比较排序,所以取倒数第二个
                int n = list.Count - 1;
                //大方向从前往后,一直到倒数第二个
                for (int i = 0; i < n; i++)
                {
                    //小方向从后往前,一直到大方向的索引
                    for (int j = n; j > i; j--)
                    {
                        //强转比较类型,从最后往前比较一位
                        if (((IComparable)list[j - 1]).CompareTo(list[j]) > 0)
                        {
                            //利用优先级
                            list[j - 1] = list[j] + (list[j] = list[j - 1]) * 0;
                        }
                    }
                }
                return list;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
复制代码

 

可以看到,是两个循环,大方向是从0到最后,小方向是从后往前。从后往前分别比较前后的数字,数字小的往前。

这种方法,是最慢的方法。因为只是一个一个比较,尾部小数问题严重影响速度。下面,上测试结果

一万就5秒了,十万和百万,就不测试了。总而言之,非常慢。

 

第二种:双向冒泡

双向冒泡是在冒泡排序的基础上由两个方向同时进行。只是解决了尾部小数问题,还是比较排序算法。效率也不高。

复制代码
public static IList<int> BiDerectionalBubleSort(IList<int> list)
        {
            try
            {
                //获取集合数量
                int limite = list.Count;
                int st = -1;
                bool swapped = false;
                do
                {
                    swapped = false;
                    st++;
                    limite--;
                    //从左开始往右循环
                    for (int j = st; j < limite; j++)
                    {
                        //强转排序类型比较,如果左边比右边大
                        if (((IComparable)list[j]).CompareTo(list[j + 1]) > 0)
                        {
                            list[j] = list[j + 1] + (list[j + 1] = list[j]) * 0;
                            swapped = true;
                        }
                    }
                    //从右开始往左循环
                    for (int j = limite - 1; j >= st; j--)
                    {
                        //强转排序类型比较,如果左边比右边大
                        if (((IComparable)list[j]).CompareTo(list[j + 1]) > 0)
                        {
                            list[j] = list[j + 1] + (list[j + 1] = list[j]) * 0;
                            swapped = true;
                        }
                    }
                } while (st < limite && swapped);
                return list;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
复制代码

首先是定义集合总数与-1。分别代表两个循环的方向,然后使用do while,进行初步循环。

在do while里面,分别对定义的变量,进行增减操作。一直到相交为止。定义两个方向的循环,然后就行前后比较,交换位置。

因为终究也是比较性排序,所以效率也不是很高。我们看一下

 

第三种:桶排序

桶排序顾名思义,就是把数列划分成若干个桶的一种算法。属于分布排序算法。在每个桶内各自进行排序,每个桶内各自排序方式不限。

复制代码
public static IList<int> BucketSort(IList<int> list)
        {
            int max = list[0];
            int min = list[0];
            //找集合中,最小值与最大值
            for (int i = 0; i < list.Count; i++)
            {
                if (((IComparable)list[i]).CompareTo(max) > 0)
                {
                    max = list[i];
                }

                if (((IComparable)list[i]).CompareTo(min) < 0)
                {
                    min = list[i];
                }
            }
            //定义一个足够大的容器。因为是最大值-最小值。所以肯定是足够装下所有集合。
            //注意事项:数组数量溢出
            ArrayList[] holder = new ArrayList[max - min + 1];

            //让数组变成二维数组
            for (int i = 0; i < holder.Length; i++)
            {
                holder[i] = new ArrayList();
            }
            //把集合的数据,付给二维数组
            for (int i = 0; i < list.Count; i++)
            {
                holder[list[i] - min].Add(list[i]);
            }
            int k = 0;
            //循环容器
            for (int i = 0; i < holder.Length; i++)
            {
                //判断是否有值
                if (holder[i].Count > 0)
                {
                    //重新给list进行赋值操作
                    for (int j = 0; j < holder[i].Count; j++)
                    {
                        list[k] = (int)holder[i][j];
                        k++;
                    }
                }
            }

            return list;
        }
复制代码

首先第一步就是创建一个桶,也就是一个交叉数组(数组的数组)。那么我们找集合中,最大与最小,来创建一个能够完全保存进去的集合。

然后循环进行交叉数组初始化操作。

接着遍历一遍集合,holder[list[i] - min].Add(list[i]); 这句话是关键,把集合的值,当作数组的索引,进行Add添加。因为是二维数组,所以相同的数据,再多也没事。

最后一步就简单了,循环遍历然后给list,进行复制操作。因为已经把list的值,放到桶里面了,所以操作数据,不会受到影响。

其实这也算是,插入排序算法。只不过声明这种非常大的容器是很消耗内存的。并不是很推荐这种方法。我们看一下性能

 

第四种:梳排序

梳排序中,是保持间距并不断减少的过程。开始的时候间距设定为列表长度,然后每一次都会除以损耗因子(一般为1.3)。间距可以四舍五入,不断重复,直到间距变为1。最后在进行一次冒泡排序。

复制代码
 public static IList<int> CombSort(IList<int> list)
        {
            //获取集合数量
            int gap = list.Count;
            int swaps = 0;

            do
            {
                //计算递减率,必须大于1
                gap = (int)(gap / 1.3);
                if (gap < 1)
                {
                    gap = 1;
                }
                int i = 0;
                swaps = 0;
                do
                {
                    //每次循环1与另一个数进行调换,直到循环尾部为止
                    if (((IComparable)list[i]).CompareTo(list[i + gap]) > 0)
                    {
                        list[i] = list[i + gap] + (list[i + gap] = list[i]) * 0;
                        swaps = 1;
                    }
                    i++;
                } while (!(i + gap >= list.Count));
            } while (!(gap == 1 && swaps == 0));
            return list;
        }
复制代码

首先计算递减率,集合总数除以损耗因子,递减率必须大于1。

从0开始 与 间隔值继续比较,调换位置。一直到间隔位置大于集合总数。

并且每次进行间隔递减,每次间隔都除以1.3。

其实重点也是比较排序,只不过是进行间隔排序基础上。性能也是比较好的。

 

 第五种:圈排序

圈排序是一种不稳定的排序算法,是一种理论上最优的比较算法。他的思想是要把数列分解为圈,可以分别旋转得到排序结果。

与其他排序不同的是,元素不会被放入数组的任何位置,如果这个值在正确位置,则不动。否则只会写一次即可。

复制代码
public static IList<int> CycleSort(IList<int> list)
        {
            //循环每一个数组
            for (int cycleStart = 0; cycleStart < list.Count; cycleStart++)
            {
                int item = list[cycleStart];
                int pos = cycleStart;
                do
                {
                    int to = 0;
                    //循环整个数组,找到其相应的位置
                    for (int i = 0; i < list.Count; i++)
                    {
                        if (i != cycleStart && ((IComparable)list[i]).CompareTo(item) < 0)
                        {
                            to++;
                        }
                    }
                    if (pos != to)
                    {
                        while (pos != to && ((IComparable)item).CompareTo(list[to]) == 0)
                        {
                            to++;
                        }
                        int temp = list[to];
                        list[to] = item;
                        item = temp;
                        pos = to;
                    }
                } while (cycleStart != pos);
            }
            return list;
        }
复制代码

 

首先进行从前往后的循环。获取不同位置的数据,当获取到数据以后,会循环整个数组找到其相应的位置。然后进行位置插入。

看一下,具体的性能。对于非常杂乱无章的序列来讲,真的好慢。

 

第六种:堆排序

堆排序是从数据集构建一个数据堆,然后提取最大元素,放到有序数列末尾。然后重新构造新的数据堆,一直到没有数据为止。属于插入排序。

复制代码
public static IList<int> HeapSort(IList<int> list)
        {
            //循环因为每次都能取出最大和最小,所以循环次数折中
            for (int i = (list.Count - 1) / 2; i >= 0; i--)
            {
                Adjust(list, i, list.Count - 1);
            }
            for (int i = list.Count - 1; i >= 1; i--)
            {
                list[i] = list[0] + (list[0] = list[i]) * 0;
                Adjust(list, 0, i - 1);
            }
            return list;
        }

public static void Adjust(IList<int> list, int i, int m)
        {
            int temp = list[i];//获取该标识值
            int j = i * 2 + 1;//获取对应尾部标识
            while (j <= m) //循环直到标识 <= 总数
            {
                if (j < m) //尾部标识 小于 总数
                {
                    //如果左边小于右边,右边标识加一位
                    if (((IComparable)list[j]).CompareTo(list[j + 1]) < 0)
                    {
                        j = j + 1;
                    }
                }
                if (((IComparable)temp).CompareTo(list[j]) < 0)
                {
                    //交换位置
                    list[i] = list[j];
                    i = j;
                    j = 2 * i + 1;
                }
                else
                {
                    //结束循环
                    j = m + 1;
                }
            }
            list[i] = temp;
        }
复制代码

 

看一下性能

 

第七种:插入排序

插入排序的原理是构造一个有序数列,对未排序的数据,从后向前扫描,找到相应的位置并插入。需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

复制代码
public static IList<int> InsertionSort(IList<int> list)
        {
            for (int i = 1; i < list.Count; i++)
            {
                int val = list[i];
                int j = i - 1;
                bool done = false;
                do
                {
                    if (((IComparable)list[j]).CompareTo(val) > 0)
                    {
                        list[j + 1] = list[j];
                        j--;
                        if (j < 0)
                        {
                            done = true;
                        }
                    }
                    else
                    {
                        done = true;
                    }
                } while (!done);
                list[j + 1] = val;
            }
            return list;
        }
复制代码

 

首先是从前往后进行循环,将数据与前一个比较并交换位置。

看一下性能:

 

第八种:奇偶排序

通过比较相邻的奇偶数进行排序,对存在错误的顺序进行交换。并一直重复这个过程,直到列表有序。

复制代码
public static IList<int> OddEventSort(IList<int> list)
        {
            bool sorted = false;
            while (!sorted)
            {
                sorted = true;
                for (int i = 1; i < list.Count - 1; i += 2)
                {
                    if (((IComparable)list[i]).CompareTo(list[i + 1]) > 0)
                    {
                        list[i] = list[i + 1] + (list[i + 1] = list[i]) * 0;
                        sorted = false;
                    }
                }
                for (int i = 0; i < list.Count - 1; i += 2)
                {
                    if (((IComparable)list[i]).CompareTo(list[i + 1]) > 0)
                    {
                        list[i] = list[i + 1] + (list[i + 1] = list[i]) * 0;
                        sorted = false;
                    }
                }
            }
            return list;
        }
复制代码

 

看一下性能

 

第九种:鸽巢排序(大数据量中最快的排序方法)

鸽巢排序假设有个待排序的数组,给它建立一个空的辅助数组(俗称鸽巢)。把原始数组的每个值作为格子(鸽巢的索引),遍历原始数据,根据每个值放入辅助数组对应的格子中。

顺序遍历鸽巢数组,把非空的鸽巢中的元素放回原始数组。这种排序方式适合在差值很小的范围内使用。

复制代码
public static IList<int> PigeonHoleSort(IList<int> list)
        {
            int min = list[0], max = list[0];
            foreach (int x in list)
            {
                if (((IComparable)min).CompareTo(x) > 0)
                {
                    min = x;
                }
                if (((IComparable)max).CompareTo(x) < 0)
                {
                    max = x;
                }
            }
            int size = max - min + 1;
            int[] holes = new int[size];
            foreach (int x in list)
            {
                holes[x - min]++;
            }
            int i = 0;
            for (int count = 0; count < size; count++)
            {
                while (holes[count]-- > 0)
                {
                    list[i] = count + (int)min;
                    i++;
                }
            }
            return list;
        }
复制代码

 

看一下性能

 

第十种:快速排序(小数据量中最快方法)

快速排序会把集合分为两个集合,并选择一个元素作为基准。把小于基准的数据排到基准前面,大于放到后面。

复制代码
public static IList<int> QuickSort(IList<int> list, int left, int right)
        {
            right = right == 0 ? list.Count - 1 : right;
            int i = left, j = right;
            double privotValue = (left + right) / 2;
            int x = list[(int)privotValue];
            while (i <= j)
            {
                while (((IComparable)list[i]).CompareTo(x) < 0)
                {
                    i++;
                }
                while (((IComparable)x).CompareTo(list[j]) < 0)
                {
                    j--;
                }
                if (i <= j)
                {
                    list[i] = list[j] + (list[j] = list[i]) * 0;
                    i++;
                    j--;
                }
            }
            if (left < j)
            {
                QuickSort(list, left, j);
            }
            if (i < right)
            {
                QuickSort(list, i, right);
            }
            return list;
        }
复制代码

 

看一下性能

 

第十一种:选择排序

在未排序的列表中找到最小或最大的元素,存放到排序序列的起始位置,然后,再从剩余的排序元素中继续找寻最小(大)元素,放到末尾。

复制代码
public static IList<int> SelectionSort(IList<int> list)
        {
            int min;
            for (int i = 0; i < list.Count; i++)
            {
                min = i;
                for (int j = i + 1; j < list.Count; j++)
                {
                    if (((IComparable)list[j]).CompareTo(list[min]) < 0)
                    {
                        min = j;
                    }
                }
                list[i] = list[min] + (list[min] = list[i]) * 0;
            }
            return list;
        }
复制代码

 

看一下性能

 

第十二种:希尔排序

通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素一次性地朝最终位置前进一大步。然后步伐越来越小,最后就是普通的插入排序。

复制代码
 int length = list.Length;
            for (int h = length / 2; h > 0; h = h / 2)
            {
                for (int i = h; i < length; i++)
                {
                    int temp = list[i];
                    if (temp.CompareTo(list[i - h]) < 0)
                    {
                        for (int j = 0; j < i; j += h)
                        {
                            if (temp.CompareTo(list[j]) < 0)
                            {
                                temp = list[j];
                                list[j] = list[i];
                                list[i] = temp;
                            }
                        }
                    }
                }
            }
            return list;
复制代码

 

 

看一下性能

3.Heap与Stack有何区别?

  Heap 堆     堆的内存大  堆存储速度慢 存储的是引用类型  

  Stack 栈  栈的内存小  栈存储速度快  存储的是值类型

4.值类型和引用类型有何区别?

  存储的位置: 引用类型的实际值存储在堆中, 值存储在栈中

  传递的方式: 引用类型传递的值的地址,  值类型传的是值

5.请写出斐波那契数列任意一位的值的算法?

  

 

6.什么是里氏代换原则?

 

7.接口与抽象类有什么区别?

  接口中的方法全都是方法的定义 ,不能有实体,  不能有变量 ,不能有构造函数

  抽象类的方法可以有实体   ,可以有变量  , 可以有构造函数

8.结构体与类有什么区别?

  结构体是值类型    值类型存储在栈中

  类是引用类型    引用类型存储在堆中

9.请简述访问修饰符及其作用

  public   公有的  对其访问没有限制

  private   私有的  只有在它的类和结构中可以访问

  protected 受保护的  只能在它的类和它的派生类中访问

  Internal  内部的   同一个程序集中的所有类都可以访问

10.请使用代码实现你理解的多态?

  

 

 

11.面向对象三大特征?

  封装  、 继承、 多态

12.请简述你了解的数据结构?

  Array    数组    存放同一种数据类型

   ArrayList    动态存放不同类型的数据   object

   LIst     列表  动态存放同一种数据类型

  LinkList  链表  (单链、双链 、环链) 每一位存储下一位所在的地址

    Dictionary 字典   键值对   每个键仅能有一个  

    Stack   栈       先进先出  后进后出   |   |

   Queue    队列        先进后出   后进先出  |——|

    二叉树     有广度查找    和 深度查找

13.请简述你了解的设计模式?

  现存的共有 23 种设计模式   而熟悉的大概是  单例    观察者   工厂   策略  

  详细的解释在下一篇c#笔记中

14.值传递与引用传递的区别?

  值传递  传递的是数据      基本的数据类型都是值传递    string除外,它是特殊的引用传递

  引用传递  传递的是数据在内存中的地址    

  更详细的解释

  https://www.cnblogs.com/Suanzhai/p/4052545.html

15.out和ref参数的区别与相同点?

  相同点:   能使值类型发生引用传递

  不同点:    ref  在使用前需要赋值,    out 在方法体内需要赋值

16.什么是装箱和拆箱?

  值  —— object  装箱      安全的

  object—— 值    拆箱      不安全的

17.字符串是引用类型还是值类型?

  字符串是特殊的引用类型,它在内存空间中的值不会被更改,

  例如: string  str = "aaa";      str = "bbb";    "aaa"在内存空间中不会被更改,它会遍历空间内,找不到“bbb” 就创建一个“bbb”,让str 指向“bbb”

18.什么是GC(Garbage Collection)?

  C#自带的垃圾回收

 

19.请简述GC的垃圾回收的流程?

  c#会自己标记有引用的对象,定期清理未被标记的对象,在清理后把剩下的对象整合在一起

 

20.请简述一个引用对象的生命周期?

  构造——操作对象——资源整理——GC

posted @ 2019-06-18 15:09  D个人笔记  阅读(841)  评论(0编辑  收藏  举报