常见IT企业面试题(1)-每日5题

来源:互联网 发布:淘宝开店起名 编辑:程序博客网 时间:2024/06/03 14:01

1.(常见问题,对于面试公司产品的特点是否了解)趋势科技产品特点:

1.占用资源较小;2.防毒能力强;3.可以在文件下载过程中就检测出是否感染了病毒(包括rar,zip中的文件);4.扫描速度超快。

2.冒泡排序,快速排序,堆排序的原理,应用场景和时间复杂度

#1.时间复杂度:“快些以nlog(2)n的速度归队。”其中,“快”指快速排序,“些”指希尔排序,“归”归并排序,“队”堆排序。
#2.空间复杂度:快速排序为O(nlog(2)n),归并排序为O(n),基数排序为O(rd),其他都是O(1)。
#3.算法稳定性总结:“心情不稳定,快些选一堆好友来聊天吧”。
“快”是快速排序,“些”是希尔排序,“选”是指简单选择排序,“堆”是堆排序。这4中是不稳定的,其他自然都是稳定的。
#4.其他细节(与排序原理相关)
1)经过一趟排序,能够保证一个元素到达最终位置,这样的排序是交换类的那两种(冒泡,快速)和选择类的两种(简单选择,堆)。
2)排序方法的元素比较次数和原始序列无关——简单选择排序和折半插入排序。
#5.一个非常有用的结论:借助于“比较”进行排序的算法,在最坏情况下能达到的最好时间复杂度为O(nlog2n)

3.冒泡排序法
代码如下:

 void BubbleSort(int x[],int n){   int i,j;   int k;   for(i=0;i<n;i++)   {      for(j=0;j<n-i-1;j++)      {        k=x[j];        x[j]=x[j+1];        x[j+1]=k;      }   }}

4.快速排序的推导和代码实现

快速排序采用了一种分治的策略,通常称其为分治法。分治法的基本思想:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
快速排序的基本思想:设当前待排序的无序区为A[low…high],利用分治可以描述为:
(1)分解:在A[low…high]中任选一个记录作为基准(pivot),以此基准将当前无序区划分为左右两个较小的子区间A[low…pivotpos-1]和A[pivotpos+1…high],并使左边子区间中所有记录的关键字均小于等于基准记录(pivot),右边的子区间中所有记录的关键字均大于等于pivot,而基准记录pivot则位于正确的位置上,它无需参加后序的排序。
注意:划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(pivot=A[pivotpos]):A[low…pivotpos-1]<=A[pivotpos]<=A[pivotpos+1…high],其中low<=pivotpos<=high。
(2)求解:通过递归调用快速排序对左,右子区间A[low…pivotpos-1]和A[pivotpos+1…high]快速排序。
(3)组合:当“求解”步骤中的两个递归调用结束时,其左右两个子区间已经有序。对快速排序而言,组合步骤无须做什么,可看作空操作。

代码如下:

void quick_sort(int a[],int low,int high){  int i,j,pivot;  if(low<high)  {     pivot=a[low];     i=low;     j=high;     while(i<j)     {        while(i<j && a[j]>=pivot)              j--;        if(i<j)            a[i++]=a[j];   //将比pivot小的元素移到低端        while(i<j && a[i]<=pivot)            i++;        if(i<j)            a[j--]=a[i];  //将比pivot大的元素移到高端     }     a[i]=pivot;          //pivot移到最终位置     quick_sort(a,low,i-1);//对左区间递归顺序     quick_sort(a,i+1,high);//对右区间递归排序  }}         

这里pivot代表基准值,它的初始值为a[low]。局部变量i和j分别代表low和high的位置。接着按照下面的步骤进行一趟交换。
(1)把比pivot小的元素移到低端(low)。
(2)把比pivot大的元素移到高端(high)。
(3)pivot移到最终位置,此时这个位置的左边元素的值都比pivot小,而其右边元素的值都比pivot大。
(4)对左,右区间分别进行递归排序。从而把前三步的粗排序逐渐细化,直至最终low和high交汇。

5.堆排序的推导和代码实现

堆排序:
堆排序定义:n个序列A1,A2,…An称为堆,有以下两种不同类型的堆。
小根堆:所有子结点都大于其父节点,即Ai<=A2i且Ai<=A2i+1。
大根堆:所有子结点都小于其父节点,即Ai>=A2i且Ai>=A2i+1。

若将此序列所存储的向量A[1…n]看为一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均不大于(或不小于)其左,右子结点(若存在)的关键字。

因此堆排序(heapSort)是树形选择排序。在排序过程中,将R[1…n]看成一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。

堆排序过程描述如下:
1)从无序序列所确定的完全二叉树的第一个非叶子结点开始,从右至左,从上至下,对每个结点进行调整,最终将得到一个大顶堆。
对结点的调整方法:
将当前结点(假设为a)的值与其孩子结点进行比较,如果存在大于a值的孩子结点,则从中选出最大的一个与a交换。当a来到下一层的时候重复上述过程,知道a的孩子结点值都小于a的值为止。
2)将当前无序序列中第一个元素,反映在树中是根结点(假设为a)与无序序列中最后一个元素交换(假设为b)。a进入有序序列,到达最终位置。无序序列中元素减少1个,有序序列中元素增加1个。此时只有结点b可能不满足堆定义,对其进行调整。
3)重复2)中过程,直到无序序列中的元素剩下1个时排序结束。
代码如下:

void Sift(int R[],int low,int high)//因R[]中是一棵完全二叉树,所以元素的存储必须从1开始{   int i=low,j=2*i;                //R[j]是R[i]的左孩子结点   int temp=R[i];   while(j<=high)   {      if(j<high&&R[j]<R[j+1])      //若右孩子较大,则把j指向右孩子         ++j;                      //j变为2*i+1      if(temp<R[j])      {         R[i]=R[j];              //将R[j]调整到双亲结点的位置上         i=j;                    //修改i和j的值,以便继续向下调整         j=2*i;      }      else          break;                //调整结束   }   R[i]=temp;                   //被调整结点的值放入最终位置}/*堆排序函数*/void heapSort(int R[],int n){    int i;    int temp;    for(i=n/2;i>=1;--i)        //建立初始堆        Sift(R,i,n);      for(i=n;i>=2;--i)          //进行n-1次循环完成堆排序    {    /*以下三句换出了根结点中元素,将其放入最终位置*/       temp=R[1];       R[1]=R[i];       R[i]=temp;       Sift(R,1,i-1);          //在减少了1个元素的无序序列中进行调整    }}

【用大根堆排序的基本思想:
(1)先将初始A[1…n]建成一个大根堆,此堆为初始的无序区。
(2)再将关键字最大的记录A[1](堆顶)和无序区的最后一个记录A[n]交换,由此得到新的无序区A[1…n-1]和有序区A[n],且满足A[1…n-1]<=A[n]。
(3)由于交换后新的根A[1]可能违反堆性质,故应将当前无序区A[1…n-1]调整为堆。然后再次将A[1…n-1]中关键字最大的记录A[1]和该区间的最后一个记录A[n-1]交换,由此得到新的无序区A[1…n-2]和有序区A[n-1…n],且仍满足关系A[1…n-2]<=A[n-1…n],同样要将A[1…n-2]调整为堆。
(4)对调整的堆重复进行上面的交换,直到无序区只有一个元素为止。

构造初始堆必须用到调整堆的操作,现在说明heapify函数思想方法。
每趟排序开始前,A[1…i]是以A[1]为根的堆,在A[1]与A[i]交换后,新的无序区A[1…i-1]中只有A[1]的值发生了变化,故除A[1]可能违反堆性质外,其余任何结点为根的子树均是堆。因此,当被调整区间是A[low….high]时,只需调整以A[low]为根的树即可。
可以使用“刷选法”进行堆的调整。A[low]的左,右子树(若存在)均已是堆,这两棵子树的根A[2low]和A[2low++]分别是各自子树中关键字最大的结点。若A[low]不小于这两个孩子节点的关键字,则A[low]未违反堆性质,以A[low]为根的树已是堆,无序调整;否则必须将A[low]和它的两个孩子节点中关键字较大者进行交换,即A[low]与A[large](A[large]=max(A[2low],A[2low+1])交换。交换后又可能使节点A[large]违反堆性质。同样,由于该节点的两棵树(若存在)仍然是堆,故可重复上述调整过程,对以A[large]为根的树进行调整。此过程直至当前被调整的节点已满足堆性质,或者该节点已是叶子为止。上述过程就像过涮子一样,把较小的关键字逐层涮下去,而把较大的关键字逐层涮上来。】

1 0