投了个内推,在南大旧的就业中心技术沙龙和面试
9.10号晚笔试题
1、正则表达式,邮件合法性检测,给出正则表达式规则,让写正确的正则表达式
2、统计英文文章单词个数,并按出现顺序打印出来,自己设计数据结构和算法
1、解答: 以下是一个不区分大小写的正则表达式:/^[a-z]([a-z0-9]*[-_]?[a-z0-9]+)*@([a-z0-9]*[-_]?[a-z0-9]+)+[.][a-z]{2,3}([.][a-z]{2})?$/i;
2、解答:
步骤1:从头开始读取每一个字符,用char *c来引用,遇到一个空格或者标点符号,一个单词结束;
步骤2:输出单词该单词,对这个单词进行hash处理:hash(*c)%1000,存入一个hashmap中,对应的<key,value>是单词和单词出现的次数;
步骤3:读取第2个单词,hash取模后判断是否在hashmap中,如果在,则对应的value加1,不在则输出,并存入到hashmap中;
步骤4;重复上述三个步骤,直到读取到文章末尾。
另,9.6的笔试题
1、设计一个分布式消息系统
2、求无向图的环的个数
9.12面试
1、面试官自己介绍,再让我自我介绍 2、说说自认为学的最好的一门课(楼主选了数据结构,面试官简历上标记) 3、说一下所有数据结构的特点;栈和队列的差别,数组和链表的差别
解答:
①从数据的逻辑结构分,数据结构分为线性结构和非线性结构。线性结构又分为一般的线性表、受限线性表和线性表推广。其中受限线性表包括栈、队列和串。线性表推广包括数组和广义表。非线性表包括集合、树形结构和图状结构。其中树形结构分为一般树和二叉树,图分为有向图和无向图。
线性表:线性表是具有相同类型的n(n>=0)个数据元素的有限序列。
线性表有两种表示形式:顺序表和链表。顺序表是用一组地址连续的存储单元,依次存储线性表中数据元素;链表不要求逻辑上相邻的两个元素逻辑上也相邻,它是通过“链”建立起数据元素之间的逻辑关系。
串: 串(或字符串),是由零个或多个字符组成的有穷序列。
树:树是N(N>=0)个结点的有限集合,N=0时,称为空树。在任意一棵非空树中应满足:1)有且仅有一个特定的称为根的结点;2)当N>1时,其余结点可分为m(m>0)个互不相交的有限集合T1、T2,···,Tm,其中每一个集合本身又是一棵树,并且称为根结点的子树。显然树的定义是递归的,是一种递归的数据结构。
图:由顶点集以及边集合组成的数据结构。
②栈:只允许在一端进行插入和删除的线性表,后进先出;队列:在一端进行插入,另一端删除的线性表,先进先出。
③数组是用一组地址连续的存储单元,依次存储线性表中数据元素,通过下标就能迅速访问数组中的任意元素,但同时插入、删除就比较麻烦;链表访问元素需要遍历,时间复杂度高,插入删除只要操作操作指针就行。
4、给10分钟写一个队列的出队入队操作
用C语言以及链式存储实现。
Queue.h文件:
//第一种结构定义方法 //typedef struct node //{ // int data; // struct node *next; //}Node,*PNode; //第二章结构定义方法 typedef struct node *PNode; typedef struct node { int data; PNode next; }Node; typedef struct{ PNode front; PNode rear; int size; }Queue; //构造一个空队列 Queue *InitQueue(); //销毁一个队列 void DestroyQueue(Queue *pqueue); //清空一个队列 void ClearQueue(Queue *pqueue); //判断队列是否为空 int IsEmpty(Queue *pqueue); //返回队列大小 int GetSize(Queue *pqueue); //返回队头元素 PNode GETFront(Queue *pqueue,int *item); //返回队尾元素 PNode GETRear(Queue *pqueue,int *item); //将新元素入队 PNode EnQueue(Queue *pqueue,int item); //将新元素出队 PNode DeQueue(Queue *pqueue,int *item);
Queue.c文件:
#include "Queue.h" #include <malloc.h> #include <stdio.h> //构造一个空队列 Queue *InitQueue(){ Queue *queue = (Queue *)malloc(sizeof(Queue)); if(queue != NULL){ queue->front = NULL; queue->rear = NULL; queue->size = 0; } return queue; } //判断队列是否为空 int IsEmpty(Queue *pqueue){ if(pqueue->front == NULL && pqueue->rear == NULL) return 1; else return 0; } //返回队列大小 int GetSize(Queue *pqueue){ return pqueue->size; } //返回队头元素 PNode GETFront(Queue *pqueue,int *item){ if(IsEmpty(pqueue)!=1&&item!=NULL) { *item = pqueue->front->data; } return pqueue->front; } //返回队尾元素 PNode GETRear(Queue *pqueue,int *item){ if(IsEmpty(pqueue)!=1&&item!=NULL) { *item = pqueue->rear->data; } return pqueue->rear; } //将新元素入队 PNode EnQueue(Queue *pqueue,int item){ PNode node = (PNode)malloc(sizeof(item)); if(node != NULL){ node->data = item; node->next = NULL; if(IsEmpty(pqueue)){ //如果队列为空 pqueue->front = node; }else{ pqueue->rear->next = node; } pqueue->rear = node; pqueue->size++; } return node; } //将新元素出队 PNode DeQueue(Queue *pqueue,int *item){ PNode node = pqueue->front; if(IsEmpty(pqueue)!=1 && node != NULL){ pqueue->front = node->next; free(node); pqueue->size--; if(pqueue->size == 0) pqueue->rear = NULL; } return pqueue->front; } /**测试**/ void print(int i) { printf("该节点元素为%d ",i); } main() { Queue *pq = InitQueue(); int i,item; printf("0-9依次入队并输出如下: "); for(i=0;i<10;i++) { EnQueue(pq,i); GETRear(pq,&item); printf("%d ",item); } }
5、多线程死锁写个示例
多线程死锁的四个必要条件:
4.1、互斥使用(资源独占)
一个资源每次只能给一个进程使用
4.2、不可强占(不可剥夺)
资源申请者不能强行的从资源占有者手中夺取资源,资源只能由占有者自愿释放
4.3、请求和保持(部分分配,占有申请)
一个进程在申请新的资源的同时保持对原有资源的占有(只有这样才是动态申请,动态分配)
4.4、循环等待
存在一个进程等待队列
{P1 , P2 , … , Pn},
其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路
实际例子:
package com.damlab.fz; public class DeadLock { public static void main(String[] args) { Resource r1= new Resource(); Resource r2= new Resource(); //每个线程都拥有r1,r2两个对象 Thread myTh1 = new MyThread1(r1,r2); Thread myTh2 = new MyThread2(r1,r2); myTh1.start(); myTh2.start(); } } class Resource{ private int i; } class MyThread1 extends Thread{ private Resource r1,r2; public MyThread1(Resource r1, Resource r2) { this.r1 = r1; this.r2 = r2; } @Override public void run() { while(true){ //先获得r1的锁,再获得r2的锁 synchronized (r1) { System.out.println("1号线程获取了r1的锁"); synchronized (r2) { System.out.println("1号线程获取了r2的锁"); } } } } } class MyThread2 extends Thread{ private Resource r1,r2; public MyThread2(Resource r1, Resource r2) { this.r1 = r1; this.r2 = r2; } @Override public void run() { while(true){ //先获得r2的锁,再获得r1的锁 synchronized (r2) { System.out.println("2号线程获取了r2的锁"); synchronized (r1) { System.out.println("2号线程获取了r1的锁"); } } } } }
6、TCPIP有哪几种状态
解答:其实就是TCP通过三次握手进行连接和通过四次握手释放连接的过程中,TCP有几种状态。
详见http://blog.sina.com.cn/s/blog_6a2787d40102uwte.html
7、词法分析和语法分析作用是什么
解答:编译过程包括词法分析,语法分析,语义检查,代码生成,代码优化。
词法分析:分析由字符组成的单词是否合法,如果没有问题的话,则产生一个单词流。
语法分析:分析由单词组成的句子是否合法,如果没有问题的话,则产生一个语法树。
8、一亿个数存在一个大文件里,现在给出一个数,怎么判断这数是否在文件里
解答:遍历这一亿个数,用bit-map存储这一亿个数,对应的bit位置为1,只需要约10^8/8byte=12.5Mb的内存存储空间。根据给出的数,通过位运算查找该数对应的位置上是否为1,如果是则表明该数在文件里,否则不在文件中。
9、最有成就感的项目
10、对比java和c++;c++的内存管理是怎么做的
-
Java不在所有类之外定义全局变量,而是在某个类中定义一种公用静态的变量来完成全局变量的功能.
-
Java不用goto语句,而是用try-catch-finally异常处理语句来代替goto语句处理出错的功能.
-
Java不支持头文件,面C和C++语言中都用头文件来定义类的原型,全局变量,库函数等,这种采用头文件的结构使得系统的运行维护相当繁杂.
-
Java不支持宏定义,而是使用关键字final来定义常量,在C++中则采用宏定义来实现常量定义,这不得于程序的可读性.
-
Java对每种数据类型都分配固定长度.比如,在Java中,int类型总是32位的,而在C和C++中,对于不同的平台,同一个数据类型分配不同的字节数,同样是int类型,在PC机中为二字节即16位,而在VAX-11中,则为32位.这使得C语言造成不可移植性,而Java则具有跨平台性(平台无关性).
-
类型转换不同.在C和C++中,可通过指针进行任意的类型转换,常常带来不安全性,而在Java中,运行时系统对对象的处理要进行类型相容性检查,以防止不安全的转换.
-
结构和联合的处理.在C和C++中,结构和联合的所有成员均为公有,这就带来了安全性问题,而在Java中根本就不包含结构和联合,所有的内容都封装在类里面
-
Java不再使用指针.指针是C和C++中最灵活,也最容易产生错误的数据类型.由指针所进行的内存地址操作常会造成不可预知的错误,同时通过指针对某个内存地址进行显式类型转换后,可以访问一个C++中的私有成员,从而破坏安全性.而Java对指针进行完全地控制,程序员不能直接进行任何指针操作.
C++内存管理:详见http://www.cnblogs.com/lancidie/archive/2011/08/05/2128318.html