1. 单词a中任意字母交换位置变为单词b,我们就称单词a,b为兄弟单词,如 army 与 mary为兄弟单词。现给一个单词字典,用户输入一个单词,找出字典中所有的兄弟单词,请写出你的解题思路和算法。
答案:
(思路一)计算ASCII码,如果值与输入词的ASCII码相等,再判断;
(思路二)是对输入的单词进行全排列,对每一种排列在字典里查询,统计查到的兄弟单词个数。(但是这个思路有个问题,就是单词的字母太多的时候,排列情况非常多,查询时间复杂度很大)
(思路三)将字典里的所有单词按字母顺序重新组成单词,如army和mary都变成amry,那么所有兄弟单词都变成同样的单词。再对这些变化过后的单词做hash映射,再对输入的单词做同样的映射,就可以很快查询出所有兄弟单词,查询的时间复杂度为O(1)
(思路四) 给每个字母设置权值,所有权值均为素数,a=2,b=3,c=5,d=7,e=11,……,然后求出单词中所有字母的乘积。如果乘积相等,则说明是兄弟单词。
前三个思路是我转的,第四个思路是我在其它地方看到的,所以把它加入了进来。
2.线程和进程有什么区别,“线程安全”怎么理解?
答案:
线程是指进程内的一个执行单元,也是进程内可调度的实体。
与进程的区别:
1)地址空间:进程内的一个执行单元;
进程至少有一个线程;
它们共享进程的地址空间;
而进程有自己独立的地址空间;
2)线程是处理器调度的基本单位,但进程不是;
3)线程是处理器调度的基本单位,但进程不是;
4)二者均可并发执行。
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。
3. c与c++分别是怎样动态分配和释放内存的,有什么区别?
答案:
c语言提供内存动态分配的函数有:malloc、calloc、realloc,在使用这些函数时必须包含其头文件,分别为:<malloc.h>、<stdlib.h>、<alloc.h>
1) malloc 函数: void *malloc(unsigned int size)
在内存的动态分配区域中分配一个长度为size的连续空间,如果分配成功,则返回所分配内存空间的首地址,否则返回NULL,申请的内存不会进行初始化。
2)calloc 函数: void *calloc(unsigned int num, unsigned int size)
按照所给的数据个数和数据类型所占字节数,分配一个 num * size 连续的空间。
calloc申请内存空间后,会自动初始化内存空间为 0,但是malloc不会进行初始化,其内存空间存储的是一些随机数据。
3)realloc 函数: void *realloc(void *ptr, unsigned int size)
动态分配一个长度为size的内存空间,并把内存空间的首地址赋值给ptr,把ptr内存空间调整为size。
申请的内存空间不会进行初始化。
用c语言提供的这些动态内存分配函数后,对于这些已经申请的内存空间需要你自己进行释放。如果你没有释放,并且你只是随便运行一下自己的一个很小的程序,可能不会产生什么很大的影响。但是,如果这样一个大型程序或软件运行中调用了这些语句,而没有对申请的内存进行释放,那么后果是很严重的。
因此,在我们平时写程序的过程中,应该养成好的变成习惯。在使用了这些函数动态分配了一段内存后,要记得对其进行释放。
释放的函数为free函数:
free函数原型为:void free(void *ptr)
作用:释放由上面3种函数所申请的内存空间。
参数:ptr:指向需要释放的内存空间的首地址。
在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。
申请和释放堆中分配的存储空间,分别使用new 和 delete 的两个运算符来完成:
指针变量名 = new 类型名(初始化式);
delete 指针名;
例如:
int *pi = new int(0)
4、网页爬虫,即从一个网页开始,查找出该页的所有url网址,并进入这些url,如此循环,直到某个时候连接回来或者到某个空白页为止。将这些连接url一一连接起来。为了简单起见,假设每个网页里都只有一个url,从两个网页入口开始,做上述操作,那么将形成两个单向链表。请判断这两个爬虫里有没有相同的url。(大概是这样的)
答案:
其实这就是变相的问,两个单向链表有没有相交。我有两种思路
思路一:如果两个单身链表相交,那么从相交节点开始,后面的节点完全重合,直到链表末尾(这个可以自己画一下,因为是单向链表)。所以用两个指针分别指向两个链表头节点,从头节点开始依次往后直到指向链表末尾,然后判断两个指针是不是指向同一个节点,如果是,那么就有相同的url。
这里给出我的实现:
struct ListNode
{
T url;
bool visit;
ListNode *link;
};
bool Judge(ListNode *p1,ListNode* p2)
{
//开始条件:未访问时链接结点的visit为false
while(p1->link!=NULL&&p1->link->visit==false)
{
p1=p1->link;
p1->visit=true;
}
while(p2!=NULL&&p2->visit==false)
{
if(p1->url == p2->url) //这里假设url是string类型,如果是char *还需要使用strcmp,但这不是问题的关键
return true;
p2->visit=true;
p2=p2->link;
}
return false;
}
思路二:将其中一个链表(L1)的末尾指向另一个链表(L2)的头节点,然后从L1的头节点开始往后遍历每个节点,并留下访问过的标记。如果有相交,那么肯定会形成一个环,所以如果检测到有环的话,说明有相同的url。(不明白什么意思)
5. 数组al[0...num-1]可以分为两部分,al[0...mid-1]和al[mid...num-1],并且这两部分都各自有序。请将数组两部分merge(合并),形成一个总体有序的数组,并且要求空间复杂度为O(1)。
答案:
这个题如果没有要求空间复杂度,那么很容易想到用归并排序,但是是要另外开辟一个同样大小的数组,所以不行。其实这个题要求空间复杂度为O(1),就是说不能另外开辟空间,我的方法是将al[mid...num-1]的每个元素插入到al[0...mid-1]里,先在前部分,找到待插入元素的位置,然后将要插入的元素保存到一个变量里,然后从待插入的位置开始把后面的元素依次往后移动一格,腾出位置,最后将保存下来的元素插入到该位置,最终形成一个整体有序的数组。
这里给出我的程序:
template<typename T>
void mergesort(T a[],int mid,int num)
{
int i,j;
T tmp;
for(i=0,j=mid;i<num-1&&j<num;)
{
if(a[i]<a[j])
++i;
else
{
tmp=a[j];
for(int k=j;k>i;--k)
a[k] = a[k-1];
a[i]=tmp;
++i;
++j;
}
}
}
6. 是一个关于百度搜索中的suggestion的东东,也就是你在搜索框里输入某个字,会有提示相关的语句的下拉列表。比如说输入“北京”,那么会提示“北京地铁”、“北京天安门”... 。请问这是怎么实现的,写出相应的数据结构和思路,要求效率尽可能高,有没有新的方法提高效率。
下面2题我的试卷上没有。
7、一个长方形框,里面嵌套n个长方形框,这些长方形框只能是全部水平排列或是全部垂直排列,同样里面的长方形框还能嵌套其他长方形框,规则同上,最里面的长方形框内有n个数据(n>=0),垂直化一条线,能穿过的数字序列
(1)求两个数列是否能在一条直线上
(2)求出所有能在一条线上的字符序列(不知道这样解释大家清楚不,有拍照的同学上传张照片,我本想拍叫那监考MM看见了,就一直呆在我旁边,没机会拍)
思路:
(1)判断两个数所属的同一层次的相同矩形框的下一层次矩形框是水平排列的还是垂直排列的,垂直排列在能在一条线上,水平排列则不能。
(2)用回溯算法求出所有在一条直线上的字符串,用两字符串是否在同一直线上进行剪枝操作
8、就是词义相似度