投了个内推,在南大旧的就业中心技术沙龙和面试
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\n",i);
}
main()
{
Queue *pq = InitQueue();
int i,item;
printf("0-9依次入队并输出如下:\n");
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