【校招面经】计算机基础

1、java中Array和ArrayList区别html

1)精辟阐述:java

能够将 ArrayList想象成一种“会自动扩增容量的Array”。python

2)Array([]):最高效;可是其容量固定且没法动态改变;算法

     ArrayList:  容量可动态增加;但牺牲效率;数组

3)建议:安全

基于效率和类型检验,应尽量使用Array,没法肯定数组大小时才使用ArrayList!app

不过当你试着解决更通常化的问题时,Array的功能就可能过于受限。ide

 

2、java中hashmap和hashtable的差异模块化

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口。主要的区别有:线程安全性,同步(synchronization),以及速度。函数

1.Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。

2.HashMap容许将null做为一个entry的key或者value,而Hashtable不容许。

3.HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程能够共享一个Hashtable;而若是没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。(在多个线程访问Hashtable时,不须要本身为它的方法实现同步,而HashMap 就必须为之提供外同步(Collections.synchronizedMap))

4.另外一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。因此当有其它线程改变了HashMap的结构(增长或者移除元素),将会抛出ConcurrentModificationException,但迭代器自己的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并非一个必定发生的行为,要看JVM。这条一样也是Enumeration和Iterator的区别。fail-fast机制若是不理解原理,能够查看这篇文章:http://www.cnblogs.com/alexlo/archive/2013/03/14/2959233.html

5.因为HashMap非线程安全,在只有一个线程访问的状况下,效率要高于HashTable。

6.HashMap把Hashtable的contains方法去掉了,改为containsvalue和containsKey。由于contains方法容易让人引发误解。 

7.Hashtable中hash数组默认大小是11,增长的方式是 old*2+1。HashMap中hash数组的默认大小是16,并且必定是2的指数。

8..二者经过hash值散列到hash表的算法不同

 

3、Python中的赋值(复制)、浅拷贝、深拷贝之间的区别

 1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间。

  2.浅拷贝: 建立新对象,其内容是原对象的引用。

    浅拷贝有三种形式:切片操做,工厂函数,copy模块中的copy函数。

    如: lst = [1,2,3,[4,5]] 

    切片操做:lst1 = lst[:] 或者 lst1 = [each for each in lst]

    工厂函数:lst1 = list(lst)

    copy函数:lst1 = copy.copy(lst)

    浅拷贝之因此称为浅拷贝,是它仅仅只拷贝了一层,在lst中有一个嵌套的list[4,5],若是咱们修改了它,状况就不同了。

  3.深拷贝:只有一种形式,copy模块中的deepcopy函数。

     和浅拷贝对应,深拷贝拷贝了对象的全部元素,包括多层嵌套的元素。

     深拷贝出来的对象是一个全新的对象,再也不与原来的对象有任何关联。

复制代码

>>> import copy >>> a = [1,2,3,4,['a','b']] #定义一个列表a >>> b = a #赋值 >>> c = copy.copy(a) #浅拷贝 >>> d = copy.deepcopy(a) #深拷贝 >>> a.append(5) >>> print(a) [1, 2, 3, 4, ['a', 'b'], 5] #a添加一个元素5 >>> print(b) [1, 2, 3, 4, ['a', 'b'], 5] #b跟着添加一个元素5 >>> print(c) [1, 2, 3, 4, ['a', 'b']] #c保持不变 >>> print(d) [1, 2, 3, 4, ['a', 'b']] #d保持不变 >>> a[4].append('c') >>> print(a) [1, 2, 3, 4, ['a', 'b', 'c'], 5] #a中的list(即a[4])添加一个元素c >>> print(b) [1, 2, 3, 4, ['a', 'b', 'c'], 5] #b跟着添加一个元素c >>> print(c) [1, 2, 3, 4, ['a', 'b', 'c']] #c跟着添加一个元素c >>> print(d) [1, 2, 3, 4, ['a', 'b']] #d保持不变 #说明以下: #1.外层添加元素时, 浅拷贝c不会随原列表a变化而变化;内层list添加元素时,浅拷贝c才会变化。 #2.不管原列表a如何变化,深拷贝d都保持不变。 #3.赋值对象随着原列表一块儿变化

 

4、python类中的new与init

__new__ 用来建立实例,在返回的实例上执行__init__,若是不返回实例那么__init__将不会执行,__init__ 用来初始化实例,设置类及实例属性,调用方法等操做。

 

5、python 星号

一个星(*):表示接收的参数做为元组来处理

两个星(**):表示接收的参数做为字典来处理

 

能够看到,这两个是python中的可变参数。*args表示任何多个无名参数,它是一个tuple;**kwargs表示关键字参数,它是一个dict。而且同时使用*args和**kwargs时,必须*args参数列要在**kwargs前,像foo(a=1, b='2', c=3, a', 1, None, )这样调用的话,会提示语法错误“SyntaxError: non-keyword arg after keyword arg”

 

6、进程和线程的区别

1. 进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元

2. 同一个进程中能够包括多个线程,而且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进程至少包括一个线程。

 

7、封装继承多态

from:https://blog.csdn.net/fanyun_01/article/details/50985333

面向对象的三个基本特征是:封装、继承、多态。

咱们知道,封装能够隐藏实现细节,使得代码模块化;继承能够扩展已存在的代码模块(类);它们的目的都是:代码重用。而多态则是为了实现另外一个目的——接口重用!多态的做用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

封装:

 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(咱们称之为类)。被封装的对象一般被称为抽象数据类型。 

 封装的意义:

  封装的意义在于保护或者防止代码(数据)被咱们无心中破坏。在面向对象程序设计中数据被看做是一个中心的元素而且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。

1. 保护数据成员,不让类之外的程序直接访问或修改,只能经过提供的公共的接口访问==>数据封装。

2. 方法的细节对用户是隐藏的,只要接口不变,内容的修改不会影响到外部的调用者==>方法封装。 

3. 当对象含有完整的属性和与之对应的方法时称为封装。

4. 从对象外面不能直接访问对象的属性,只能经过和该属性对应的方法访问。

5. 对象的方法能够接收对象外面的消息。

    类成员的访问修饰符:

即类的方法和成员变量的访问控制符,一个类做为总体对象不可见,并不表明他的全部域和方法也对程序其余部分不可见,须要有他们的访问修饰符判断。

权限以下: 

访问修饰符

同一个类

同包

不一样包,子类

不一样包,非子类

private

 

 

 

protected

 

public

默认

 

 

继承:

继承主要实现重用代码,节省开发时间。

1、C#中的继承符合下列规则:

    1. 继承是可传递的。若是C从B中派生,B又从A中派生,那么C不只继承了B中声明的成员,一样也继承了A中的成员。Object类做为全部类的基类。
    2. 派生类应当是对基类的扩展。派生类能够添加新的成员,但不能除去已经继承的成员的定义。
    3. 构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类可否访问它们。
    4. 派生类若是定义了与继承而来的成员同名的新成员,就能够覆盖已继承的成员。但这并不由于这派生类删除了这些成员,只是不能再访问这些成员。
    5. 类能够定义虚文法、虚属性以及虚索引指示器,它的派生类可以重载这些成员,从而实现类能够展现出多态性。

  2、new关键字

   若是父类中声明了一个没有friend修饰的protected或public方法,子类中也声明了同名的方法。则用new能够隐藏父类中的方法。(不建议使用)

  3、base关键字

   base 关键字用于从派生类中访问基类的成员:

    1. 调用基类上已被其余方法重写的方法。
    2. 指定建立派生类实例时应调用的基类构造函数。

多态:

一、“一个接口,多种方法”

同一操做做用于不一样的对象,能够有不一样的解释,产生不一样的执行结果。  多态的三个条件: a.    继承的存在(继承是多态的基础,没有继承就没有多态). b.    子类重写父类的方法(多态下调用子类重写的方法). c.    父类引用变量指向子类对象(子类到父类的类型转换).

重载(overload)和重写(override)是实现多态的两种主要方式。

2、实现多态:

    1. 接口多态性。
    2. 继承多态性。
    3. 经过抽象类实现的多态性。

 

8、几率题 利用X等几率生成1-n的数

X是一个以p的几率产生1,1-p的几率产生0的随机变量,利用X等几率生成1-n的数

生成两次,生成01,10的几率是相同的,生成00或者11则从新生成,构造一个等几率生成器,而后用二进制生成1-n

 

9、解决Hash冲突的方法

from:https://blog.csdn.net/zhangdaisylove/article/details/47862753

 1.开放定址法(再散列法):

     基本思想:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另外一个哈希地址p1,若是p1仍然冲突,再以p为基础,产生另外一个哈希地址p2,…,                            直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。

     这种方法有一个通用的再散列函数形式:

               Hi=(H(key)+di)% m   i=1,2,…,n

     其中H(key)为哈希函数,m 为表长,di称为增量序列。增量序列的取值方式不一样,相应的再散列方式也不一样。 

     1.线性探测再散列: 

  dii=1,2,3,…,m-1

  冲突发生时,顺序查看表中下一单元,直到找出一个空单元或查遍全表。

     2.二次探测再散列:

  di=12,-12,22,-22,…,k2,-k2    ( k<=m/2 )

  冲突发生时,在表的左右进行跳跃式探测,比较灵活。

     3.伪随机探测再散列:

  di=伪随机数序列。

  具体实现时,应创建一个伪随机数发生器,(如i=(i+p) % m),并给定一个随机数作起点。

     4.  示例:

          已知哈希表长度m=11,哈希函数为:H(key)= key  %  11,则H(47)=3,H(26)=4,H(60)=5,假设下一个关键字为69,则H(69)=3,与47冲突。

  a): 若是用线性探测再散列处理冲突,下一个哈希地址为H1=(3 + 1)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 + 2)% 11 = 5,仍是冲突,

               继续找下一个哈希地址为H3=(3 + 3)% 11 = 6,此时再也不冲突,将69填入5号单元。

    0     1     2     3     4     5     6     7     8     9     10

                        47   26   60   69

  b): 若是用二次探测再散列处理冲突,下一个哈希地址为H1=(3 + 12)% 11 = 4,仍然冲突,再找下一个哈希地址为H2=(3 - 12)% 11 = 2,此时再也不冲突,

               将69填入2号单元。

     0     1     2     3     4     5     6     7     8     9     10

                  69   47   26  60      

  c): 若是用伪随机探测再散列处理冲突,且伪随机数序列为:2,5,9,……..,则下一个哈希地址为H1=(3 + 2)% 11 = 5,仍然冲突,再找下一个哈希地址

               为H2=(3 + 5)% 11 = 8,此时再也不冲突,将69填入8号单元。

     0     1     2     3     4     5     6     7     8     9     10

                         47   26  60                  69

   2.再哈希法:

         这种方法是同时构造多个不一样的哈希函数:

                   Hi=RH1(key)  i=1,2,…,k

         当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突再也不产生。这种方法不易产生汇集,但增长了计算时间。

   3.拉链法(HashMap的冲突处理方式):

          基本思想: 将全部哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,于是查找、插入和删除主要

                            在同义词链中进行。链地址法适用于常常进行插入和删除的状况。

         例如:  已知一组关键字(32,40,36,53,16,46,71,27,42,24,49,64),哈希表长度为13,哈希函数为:H(key)= key % 13,

                     则用链地址法处理冲突的结果如图8.27所示:

       位置    Entry 

        0

        1  -->  40 --> 27 --> 53

        2

        3  -->  16 --> 42

        4

        5

        6  -->  32 --> 71

        7

        8

        9

        10 -->  36 --> 49

        11 -->  24

        12 -->  64

                本例的平均查找长度 ASL=(1*7+2*4+3*1)/13=1.38

   4.创建公共溢出区:

          这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一概填入溢出表

 

10、快速排序

from:http://www.javashuo.com/article/p-dczygbaf-dm.html

 

11、桶排序

from:https://blog.csdn.net/ii1245712564/article/details/45869623

桶排序_BUCKETSORT

假设你有五百万份试卷,每份试卷的满分都是100分,若是要你对这些试卷按照分数进行排序,天噜啦,五百万份试卷啊,快速排序?堆排序?归并排序?面对这么多的数据,平均下来上面的每一种一种算法至少都要花费nlogn=5000000log5000000=111267433单位时间啊,将近一亿多,太慢了。

要是咱们这样来作呢,首先买101只桶回来,分别为每一只桶编上0-100标号,咱们就只管遍历一边全部的试卷,将分数为n的试卷丢入到编号为n的桶里面,当全部的试卷都放入到相应的桶里面时,咱们就已经将全部的试卷按照分数进行排序了。遍历一遍全部数据的时间也就是五百万次,相比与一亿屡次,那但是省了很多时间。这里所用到的就是桶排序的思想。

桶排序的思想

桶排序,顾名思义就是运用桶的思想来将数据放到相应的桶内,再将每个桶内的数据进行排序,最后把全部桶内数据按照顺序取出来,获得的就是咱们须要的有序数据

好比咱们有下面的一些数据

49 43 11 61 31 71 53 51 71 84

下面咱们按照这些数的十位将他们放入桶内

将数据放入桶内

bucket#.0 +++ 

bucket#.1 +++ 11 

bucket#.2 +++ 

bucket#.3 +++ 31 

bucket#.4 +++ 49 43 

bucket#.5 +++ 53 51 

bucket#.6 +++ 61