无题

来源:互联网 发布:mac怎么卸载itunes 编辑:程序博客网 时间:2024/05/21 22:38

点击打开链接


------------------------------------------------------------------------------------ 

后面是9月份以前默默的自己瞎写的内容,那时候还不知道找工作会遇到什么,哎呀,不舍得删 
------------------------------------------------------------------------------------ 
   几个系统设计考题:微博系统设计 网页爬虫设计 广告排序设计 
------------------------------------------------------------------------------------ 
  
这三个题目都可以写成长篇的论文了,估计面试官只会挑其中的一些点来问你的解决方案吧, 
搜了些资料,发现很杂乱…… 
  
------------------------------------------------------------------------------------ 
   raid 冗余 磁盘阵列 
------------------------------------------------------------------------------------ 
  
Redundant array of independent disk  为了提高硬盘吞吐率及数据可靠性而采用的多磁盘组织形式。 
  
把多块磁盘按照一定规则排成阵列,同时存取,大大提高硬盘性能。 
  
raid0  无数据冗余,存取速度高,但是可靠性差,N块磁盘组成阵列,则可靠性下降到1/N。 
  
raid1  通过镜像磁盘,保证数据可靠性,利用率为50%。 
  
raid1+0 前面两个的折中,提供数据可靠性,并且利用率有所提高。 
  
raid2 raid3 等等…… 了解即可。 
  
  
------------------------------------------------------------------------------------ 
tcp粘包、socket套接字使用流程、socket阻塞与非阻塞的区别,qq消息通信过程,网页通信过程等 
------------------------------------------------------------------------------------ 
  
1.tcp粘包、半包的产生原因。 
UDP丢包是因为数据包在传送过程中丢失了 而TCP是基于流式的发送 并且存在丢包重发机制 TCP是可靠连接而UDP是不可靠的这个我就不多说了 
关于TCP的粘包 正是由于TCP是流式传送的 也就是连接建立后可以一直不停的发送 并没有明确的边界定义 而你用UDP发送的时候 是可以按照一个一个 
  
数据包去发送的 一个数据包就是一个明确的边界 
而TCP并没有数据包的概念 是完全流式的 他会开辟一个缓冲区 发送端往其中写入数据 每过一段时间就发送出去 然后接收端接收到这些数据 但是并不 
  
是说我发送了一次数据就肯定发送出去了 数据会在缓冲区中 有可能后续发送的数据和之前发送的数据同时存在缓冲区中随后一起发送 这就是粘包的一 
  
种形式 接收端也有产生粘包的情况 如果应用程序没有及时处理缓冲区中的数据 那么后续到达的数据会继续存放到缓冲区中 也就是2次接收的数据同时 
  
存在缓冲区中 下次取缓冲区的时候就会取出2次粘包后的数据 这是粘包的另外一种形式 还有其他许多形式 比如填充缓冲区到一半缓冲区满了直接发送 
  
了 但是其实那个包还没填充完全 这个就是不完整的粘包了 剩余数据会在下次发送的时候补上 
  
关于解决方法 如果你是连续的整个数据流 比如发送文件 那么完全不考虑粘包也无所谓 因为可以建立连接后发送 发送完毕后断开连接 整个数据流就 
  
是整个一个文件 无论数据从那里切开都无所谓 整个拼接后依旧是整个一个文件的数据 
如果你发送的数据是多次通信 比如把一个目录下所有的文件名都发送过去 那么就不能当作一个整体发送了 必须对他们划分边界 有一个很简单的处理 
  
方法 就是采用"数据长度+实际数据"的格式来发送数据 这个"数据长度"的格式是固定宽度的 比如4字节 可以表示0~4GB的宽度了 足够用了 这个宽度 
  
说明了后续实际数据的宽度 这样你就可以把粘包后的数据按照正确的宽度取出来了 
每次都是取出4字节 随后按照正确的宽度取出后续部分的就OK了 
如果你的所有数据都是固定宽度的 比如不停的发送温度数据 每个都是1字节 那么宽度已知了 每次你都取出一个1字节就OK了 所以就不用发送宽度数据 
  
了 
当然你也可以按照建立连接断开连接来划分边界 每次发送数据都打开关闭一次连接 不过对于频繁的小数据量是不可取的做法 因为开销太大 建立连接 
  
和关闭连接也是需要耗费网络流量的 
总而言之 粘包的情况是无法绝对避免的 因为网络环境是很复杂的 依赖发送和接收缓冲区的控制是不能保证100%的 只要在发送的数据中说明数据的宽 
  
度随后在接收部分按照这个宽度拆开就OK了 宽度全都是统一的已知宽度的情况下拆开更加容易 连在发送端填入宽度数据都可以省去了 
  
2.socket套接字使用流程,socket阻塞与非阻塞的区别。 
  
1).新建一个socket对象,调用socket(int,int,int),设置通信协议族(如AF_INET 互联网),通信协议号(一般0),udp/tcp/raw对应参数  
  
SOCKET_DGRAM/SOCKET_STREAM/SOCKET_RAW. 
2).设置socket的选项,setsocketopt(……),可以设定缓冲区,可以设定是阻塞模式还是非阻塞模式。 
3).调用bind函数,绑定到特定的ip地址和端口号。 
4).调用connect函数,建立两个主机socket之间的链接,链接之后可以调用recv函数接受数据,或者send函数发送数据。 
5).如果不确定跟谁链接,可以调用listen监听函数,来监听某一个端口,这个函数维持一个连接请求队列, 再调用accept函数可以从队列中选择一个 
  
socket建立连接,函数返回一个新的socket(用于新的链接)。 
6).调用closesocket函数释放资源。 
  
socket 阻塞模式下,在发送包成功接收前,一直处于等待状态,成功发送后再发送下一个数据包。非阻塞模式下,不会等待返回确认,直接发送。 
通常情况下要用非阻塞模式。 
socket套接字参考资料: 
http://blog.csdn.net/cxh342968816/article/details/6336938   
http://wenku.baidu.com/view/78ad7b563c1ec5da50e270a2.html 
  
3.浏览器输入url,之后显示页面内容的整个通信过程。 
  
  
A) 通过电话网拨号与访问路由器建立物理连接,与访问路由器建立数据链路 
  
B) 获得PC机的IP地址 
  
C) 解析Web页面的URL,得到Web服务器的域名 
  
D)通过DNS服务器获得Web服务器的IP地址 
  
E) 与Web服务器建立TCP连接 
  
F)与Web服务器建立HTTP连接 
  
G) 从Web服务器获得URL指定的文档 
  
H)浏览器解释页面文档,并显示在屏幕 
  
qq通信过程类似,先与服务器通信,然后服务器与对方建立连接,然后发送给对方信息。中间可能有很多特殊情况,qq tcp udp两个协议同时用,不同情况下协议不同。 
  
------------------------------------------------------------------------------------ 
   数据库索引的原理 以及 数据库存储引擎的分类 
------------------------------------------------------------------------------------ 
  
1.索引的种类。 
包含 唯一索引,聚集索引,非聚集索引,表在设定主键后会自动生成一个主键索引,主键索引是唯一索引的一种特例。索引会占据很大的存贮空间,也会降低数据库在增删改时的性能。 
2.聚集索引与非聚集索引的区别。 
  聚集索引与数据的物理存贮地址是对应的,每个表只能有一个聚集索引。 
  非聚集索引使用指针或数组指向物理地址,并不影响实际数据的存储地址。 
  每个表最多可以建立250个索引,设置上限是为了保证性能,因为索引虽然会提高数据查询速度,但是在增删改时也需要维护索引,大大降低数据库效率。 
  通常建立索引的默认类型是非聚集索引。聚集索引可以提高范围查找的性能,而非聚集不能。一般在orderby groupby 等常用的列上可以加索引。 
3.数据库的ACID 
   a原子性,一个事物要么全做,也么全不做。 
   c一致性,当表中数据改动时,对应的一系列约束也要改动。 
   i隔离性  一个用户修改数据时,只有在完成修改操作后,才能被其他用户查看,分四种隔离级别,通过不同的锁来实现。 
   d持久性 当事物提交后,将持久有效,不可更改。 
4.数据库引擎分类 
    ISAM 
   
  ISAM是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM的两个主要不足之处在于,它不支持事务处理,也不能够容错:如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把ISAM用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MYSQL能够支持这样的备份应用程序。 
   
     MYISAM 
   
  MYISAM是MYSQL的ISAM扩展格式和缺省的数据库引擎。除了提供ISAM里所没有的索引和字段管理的大量功能,MYISAM还使用一种表格锁定的机制,来优化多个并发的读写操作。其代价是你需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的空间。MYISAM还有一些有用的扩展,例如用来修复数据库文件的MYISAMCHK工具和用来恢复浪费空间的MYISAMPACK工具。 
   
       MYISAM强调了快速读取操作,这可能就是为什么MYSQL受到了WEB开发如此青睐的主要原因:在WEB开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和INTERNET平台提供商只允许使用MYISAM格式。 
   
   
   
      HEAP 
   
  HEAP允许只驻留在内存里的临时表格。驻留在内存使得HEAP比ISAM和MYISAM的速度都快,但是它所管理的数据是不稳定的,而且如果在关机之前没有进行保存,那么所有的数据都会丢失。在数据行被删除的时候,HEAP也不会浪费大量的空间,HEAP表格在你需要使用SELECT表达式来选择和操控数据的时候非常有用。要记住,用完表格后要删除表格。 
   
   
   
     INNODB和BERKLEYDB 
   
  INNODB和BERKLEYDB(BDB)数据库引擎都是造就MYSQL灵活性的技术的直接产品,这项技术就是MySql++ API。在使用MySql的时候,你所面对的每一个挑战几乎都源于ISAM和MYIASM数据库引擎不支持事务处理也不支持外来键。尽管要比ISAM和MYISAM引擎慢很多,但是INNODB和BDB包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。如前所述,如果你的设计需要这些特性中的一者或者两者,那你就要被迫使用后两个引擎中的一个了。 
------------------------------------------------------------------------------------ 
   Tcp/Ip 协议包头解析以及三次握手协议过程。 
------------------------------------------------------------------------------------ 
  
开始吧,先介绍IP协议。   
  
  IP协议(Internet Protocol)是网络层协议,用在因特网上,TCP,UDP,ICMP,IGMP数据都是按照IP数据格式发送得。IP协议提供的是不可靠无连接得服务。IP数据包由一个头部和一个正文部分构成。正文主要是传输的数据,我们主要来理解头部数据,可以从其理解到IP协议。   
  
  IP数据包头部格式(RFC791) 
  
  Example Internet Datagram Header  
   
  上面的就是IP数据的头部格式,这里大概地介绍一下。   
  
  IP头部由20字节的固定长度和一个可选任意长度部分构成,以大段点机次序传送,从左到 右。   
  
  TCP协议   
  
  TCP协议(TRANSMISSION CONTROL PROTOCOL)是传输层协议,为应用层提供服务,和UDP不同的是,TCP协议提供的可靠的面向连接的服务。在RFC793中是基本的TCP描述。关于TCP协议的头部格式内容的说明:   
  
  TCP Header FORMat  
  
  TCP Header FORMat   
  
  跟IP头部差不多,基本的长度也是20字节。TCP数据包是包含在一个IP数据报文中的。   
  
  好了,简单介绍到此为止。来看看我捕获的例子吧。这是一次FTP的连接,呵呵,是cuteftp默认的cuteftp的FTP站点,IP地址是:216.3.226.21。我的IP地址假设为:192.168.1.1。下面的数据就是TCO/IP连接过程中的数据传输。我们可以分析TCP/IP协议数据格式以及TCP/IP连接的三次握手(ThreeWay-Handshake)情况。下面的这些十六进制数据只是TCP/IP协议的数据,不是完整的网络通讯数据。   
  
  第一次,我向FTP站点发送连接请求(我把TCP数据的可选部分去掉了)  
   
  192.168.1.1->216.3.226.21   
  
  IP头部: 45 00 00 30 52 52 40 00 80 06 2c 23 c0 a8 01 01 d8 03 e2 15   
  
  TCP头部:0d 28 00 15 50 5f a9 06 00 00 00 00 70 02 40 00 c0 29 00 00   
  
  来看看IP头部的数据是些什么。   
  
  第一字节,“45”,其中“4”是IP协议的版本(Version),说明是IP4。“5”是IHL位,表示IP头部的长度,是一个4bit字段,最大就是1111了,值为12,IP头部的最大长度就是60字节。而这里为“5”,说明是20字节,这是标准的IP头部长度,头部报文中没有发送可选部分数据。   
  
  接下来的一个字节“00”是服务类型(Type of Service)。这个8bit字段由3bit的优先权子字段(现在已经被忽略),4 bit的TOS子字段以及1 bit的未用字段(现在为0)构成.4 bit的TOS子字段包含:最小延时、最大吞吐量、最高可靠性以及最小费用构成,这四个1bit位最多只能有一个为1,本例中都为0,表示是一般服务。   
  
  接着的两个字节“00 30”是IP数据报文总长,包含头部以及数据,这里表示48字节。这48字节由20字节的IP头部以及28字节的TCP头构成(本来截取的TCP头应该是28字节的,其中8字节为可选部分,被我省去了)。因此目前最大的IP数据包长度是65535字节。   
  
  再是两个字节的标志位(Identification):“5252”,转换为十进制就是21074。这个是让目的主机来判断新来的分段属于哪个分组。   
  
  下一个字节“40”,转换为二进制就是“0100 0000”,其中第一位是IP协议目前没有用上的,为0。接着的是两个标志DF和MF。DF为1表示不要分段,MF为1表示还有进一步的分段(本例为0)。然后的“0 0000”是分段便移(Fragment Offset)。   
  
  “80”这个字节就是TTL(Time To Live)了,表示一个IP数据流的生命周期,用Ping显示的结果,能得到TTL的值,很多文章就说通过TTL位来判别主机类型。因为一般主机都有默认的TTL值,不同系统的默认值不一样。比如WINDOWS为128。不过,一般Ping得到的都不是默认值,这是因为每次IP数据包经过一个路由器的时候TTL就减一,当减到0时,这个数据包就消亡了。这也时Tracert的原理。本例中为“80”,转换为十进制就是128了,我用的WIN2000。   
  
  继续下来的是“06”,这个字节表示传输层的协议类型(Protocol)。在RFC790中有定义,6表示传输层是TCP协议。   
  
  “2c 23”这个16bit是头校验和(Header Checksum)。   
  
  接下来“c0 a8 01 01”,这个就是源地址(Source Address)了,也就是我的IP地址。  
  
  转换为十进制的IP地址就是:192.168.1.1,同样,继续下来的32位“d8 03 e2 15”是目标地址,216.3.226.21   
  
  好了,真累啊,终于看完基本的20字节的IP数据报头了。继续看TCP的头部吧,这个是作为IP数据包的数据部分传输的。   
  
  TCP头部:0d 28 00 15 50 5f a9 06 00 00 00 00 70 02 40 00 c0 29 00 00   
  
  一来就是一个两字节段“0d 28”,表示本地端口号,转换为十进制就是3368。第二个两字节段“00 15”表示目标端口,因为我是连接FTP站点,所以,这个就是21啦,十六进制当然就是“00 15”。   
  
  接下来的四个字节“50 5f a9 06”是顺序号(Sequence Number),简写为SEQ,SEQ=1348446470下面的四个字节“00 00 00 00”是确认号(Acknowledgment Number),简写为ACKNUM。   
  
  继续两个字节,“70 02”,转换为二进制吧,“0111 0000 0000 0010”。这两个字节,总共16bit,有好多东西呢。第一个4bit“0111”,是TCP头长,十进制为7,表示28个字节(刚才说了,我省略了8字节的option数据,所以你只看见了20字节)。接着的6bit现在TCP协议没有用上,都为0。最后的6bit“00 0010”是六个重要的标志。这是两个计算机数据交流的信息标志。接收和发送断根据这些标志来确定信息流的种类。下面是一些介绍:   
  
  URG:(Urgent Pointer field significant)紧急指针。用到的时候值为1,用来处理避免TCP数据流中断   
  
  ACK:(Acknowledgment fieldsignificant)置1时表示确认号(AcknowledgmentNumber)为合法,为0的时候表示数据段不包含确认信息,确认号被忽略。   
  
  PSH:(Push Function),PUSH标志的数据,置1时请求的数据段在接收方得到后就可直接送到应用程序,而不必等到缓冲区满时才传送。   
  
  RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误。   
  
  SYN:(Synchronize sequence numbers)用来建立连接,在连接请求中,SYN=1,ACK=0,连接响应时,SYN=1,ACK=1。即,SYN和ACK来区分Connection Request和Connection Accepted。   
  
  FIN:(No more data from sender)用来释放连接,表明发送方已经没有数据发送了。   
  
  这6个标志位,你们自己对号入座吧。本例中SYN=1,ACK=0,当然就是表示连接请求了。我们可以注意下面两个过程的这两位的变换。   
  
  后面的“40 00 c0 29 00 00”不讲了,呵呵,偷懒了。后面两次通讯的数据,自己分开看吧。我们看看连接的过程,一些重要地方的变化。   
  
  第二次,FTP站点返回一个可以连接的信号。   
  
  216.3.226.21->192.168.1.1   
  
  IP头部: 45 00 00 2c c6 be 40 00 6a 06 cd ba d8 03 e2 15 c0 a8 01 01   
  
  TCP头部:00 15 0d 28 4b 4f 45 c1 50 5f a9 07 60 12 20 58 64 07 00 00   
  
  第三次,我确认连接。TCP连接建立起来。   
  
  192.168.1.1->216.3.226.21   
  
  IP头部: 45 00 00 28 52 53 40 00 80 06 2c 2a c0 a8 01 01 d8 03 e2 15   
  
  TCP头部:0d 28 00 15 50 5f a9 07 4b 4f 45 c2 50 10 40 b0 5b 1c 00 00   
  
  好,我们看看整个Threeway_handshake过程。   
  
  第一步,我发出连接请求,TCP数据为:SEQ=50 5f a9 06,ACKNUM=00 00 00 00,SYN=1,ACK=0。   
  
  第二步,对方确认可以连接,TCP数据为:SEQ=4b 4f 45 c1,ACKNUM=50 5f a9 07,SYN=1,ACK=1。   
  
  第三步,我确认建立连接。SEQ=50 5f a9 07, ACKNUM=4b 4f45c2,SYN=0,ACK=1。   
  
  可以看出什么变化么?正式建立连接了呢,这些东西是什么值?   
  
  我接收从216.3.226.21->192.168.1.1的下一个数据包中:   
  
  SEQ=4b 4f 45 c2,ACKNUM=50 5f a9 07,SYN=0,ACK=1这些都是很基础的东西,对于编写sniffer这样的东西是必须非常熟悉的。这里只讲解了TCP/IP协议的一点点东西,主要是头部数据的格式。(T002) 
------------------------------------------------------------------------------------ 
   求组合数的一个递归程序模板 
------------------------------------------------------------------------------------ 
#include<iostream> 
#include <vector> 
using namespace std; 
/////////////////////////// 
// 
//给出一个 元素数组  一个 目标数 
//要求输出 元素之和等于目标数的所有的组合 元素可以重复使用 
//例如: 输入元素数组{1,2,3} 目标数 3,则3=1+1+1 3=1+2 3=3 
/////////////////////////// 
#define N 7 
int Elements[N]={1,2,3,4,5,6,7};//这个可以输入别的元素数组 
int targetNum=7;//不宜过大 否则组合数太多 
vector<int> path; 
int Search(int target,int low)//low 是为了保证递归出的组合 不重复,保证递增序列搜索 

if(target==0) 

for(int i=0;i!=path.size();++i) 
cout<<path[i]<<" "; 
cout<<endl; 
return 1; 

int count=0; 
for(int i=low;i<=N-1;++i) 

if(Elements[i]>target) 
break; 
path.push_back(Elements[i]); 
target-=Elements[i]; 
count+=Search(target,i); 
path.pop_back(); 
target+=Elements[i]; 

return count; 

void main() 

cout<<"输入目标数:\n";cin>>targetNum; 
int count= Search(targetNum,0); 
cout<<"There are "<<count<<" ways.\n"; 

::system("pause"); 

  
------------------------------------------------------------------------------------ 
   这个题目连续两次被我遇到了,主要考察细节,值得注意  ps:ms面试问过我这个题目 
------------------------------------------------------------------------------------ 
#include<iostream> 
#include <vector> 
#include <locale> 
#include <string> 
using namespace std; 
/////////////////////////// 
// 
//给出一个一亿以内的整数,输出它的中文表达 
// 
// 
/////////////////////////// 
string aux[10]={"零","一","二","三","四","五","六","七","八","九"}; 
void f1(int); 
void f(int a) 

if(a<0||a>=100000000) 
throw "illegal argument"; 
if(a==0) 
{cout<<"零"; 
return;} 
if(a<10000) 
f1(a); 
else 

f1(a/10000); 
cout<<"万"; 
int tmp=a%10000; 
if(tmp<1000&&tmp>0||((a/10000)%10==0)&&tmp>0) 
cout<<"零"; 
f1(tmp); 


void f1(int a) 

if(a<0||a>10000) 
throw "illegal argument!"; 
if(a==0) 
return; 
int t1=a/1000,t2=a/100%10,t3=a/10%10,t4=a%10; 
if(t1) cout<<aux[t1]<<"千"; 
if(t2) cout<<aux[t2]<<"百"; 
if(t3) 

if(!t1&&!t2||t2) 
cout<<aux[t3]<<"十"; 
else 
cout<<"零"<<aux[t3]<<"十"; 

if(t4) 

if(!t1&&!t2&&!t3||t3) 
cout<<aux[t4]; 
else 
cout<<"零"<<aux[t4]; 

  

void main() 

while(1) 

int a; 
cin>>a; 
f(a); 
cout<<endl; 


::system("pause"); 

------------------------------------------------------------------------------------ 
   模拟栈操作的一个递归程序,可以输出所有合法操作序列 
------------------------------------------------------------------------------------ 
#include<iostream> 
#include <vector> 
using namespace std; 
/////////////////////////// 
//程序说明 
//给出n个元素的排序序列,每个元素进出栈一次, 
//给出所有的合法操作序列 
//输出序列中 1代表入栈 0代表出栈 
/////////////////////////// 
int RemainNum=6;//这个代表要入栈的元素个数,可以适当设置 
int stackSize=0; 
int count=0;//所有合法操作序列的个数  6个元素的时候 结果为132 是不是想起小矮人的题目了? 
vector<int> path; 
void Search() 

if(RemainNum==0) 

for(int i=0;i<path.size();++i) 
cout<<path[i]<<" "; 
for(int i=0;i<stackSize;++i) 
cout<<"0"<<" "; 
cout<<endl; 
count++; 
return; 

vector<int> tmp=path; 
for(int i=1;i<=stackSize;++i) 

path=tmp; 
for(int j=0;j<i;++j) 
path.push_back(0); 
path.push_back(1); 
stackSize-=i-1; 
RemainNum-=1; 
Search(); 
RemainNum+=1; 
stackSize+=i-1; 

for(int i=1;i<=RemainNum;++i) 

path=tmp; 
for(int j=0;j<i;++j) 
path.push_back(1); 
path.push_back(0); 
stackSize+=i-1; 
RemainNum-=i; 
Search(); 
RemainNum+=i; 
stackSize-=i-1; 


void main() 

Search(); 
cout<<"There are "<<count<<" ways.\n"; 

::system("pause"); 

------------------------------------------------------------------------------------ 
   是不是总是听别人说写个背包,自己却不知所云?给一个背包算法的完整代码,理解后,记住吧! 
------------------------------------------------------------------------------------ 
#include<iostream> 
#include <vector> 
using namespace std; 
///////////////////////////// 
//输入是 物品重量数组  背包容量 
//输出是 最大可取物品的组合 
// 
////////////////////////////// 
void PackSearch(int * things,int * result,int size,int capacity) 

int minValueRemain=INT_MAX; 
int * stack=new int[size]; int top=-1; int index=0; 
while(1) 

while(index<size&&things[index]<=capacity) 

stack[++top]=index; 
capacity-=things[index]; 
index++; 

if(capacity<minValueRemain) 

minValueRemain=capacity; 
memset(result,0,size*4); 
for(int j=0;j<=top;++j) 
result[j]=things[stack[j]]; 

if(top<0) break; 
index=stack[top]+1; 
top--; 
capacity+=things[index-1]; 

         delete[] stack; 

  
  
  
  
void main() 

int arr[10]={1,4,7,4,6,9,4,2,11,7};//物品重量数组 
int result[10];//保存背包结果 
int capacity=24;//背包容量 
PackSearch(arr,result,10,capacity); 
for(int i=0;i!=10;++i) 
cout<<result[i]<<"\t"; 
cout<<endl; 

::system("pause"); 

  
------------------------------------------------------------------------------------ 
                                 推荐两个题目 
------------------------------------------------------------------------------------ 
  
1、 拆分数组算法。编程之美上给的答案很不好。题目:给出一个2n长度的数组,拆分成两个,使他们的元素和之差最小。 
算法:数学分析后,可以每次交换两个元素,使两个数组的差变小,直到找不到这样的两个元素为止,是多项式复杂度。代码: 
#include<iostream> 
using namespace std; 
  
void f(int * a,int * b,int size,int diff) 
{//diff 是数组a所有元素之和 减去 数组b所有元素之和 
bool flag=true; 
while(flag) 

flag=false; 
for(int i=0;i!=size;++i) 
for(int j=0;j!=size;++j) 

if(a[i]-b[j]>0&&a[i]-b[j]<diff) 

diff=diff+2*(b[j]-a[i]); 
int temp=a[i];a[i]=b[j];b[j]=temp; 
flag=true; 
break; 


if(diff<0) 

int * t=a; a=b; b=t; 
diff= -diff; 



void main() 

int Length; 
cout<<"请输入数组长度:"; 
cin>>Length; 
int * Array=new int[Length]; 
for(int i=0;i!=Length;++i) 
cin>>Array[i]; 
int diff=0; 
for(int i=0;i!=Length;++i) 
if(i<Length/2) diff+=Array[i]; 
else diff-=Array[i]; 
if(diff>0) 
f(Array,Array+Length/2,Length/2,diff); 
else 
f(Array+Length/2,Array,Length/2,-diff); 
for(int i=0;i!=Length;++i) 
{cout<<Array[i]<<" "; 
if(i==Length/2-1) cout<<endl; 

::system("pause"); 

2、 输入n,m ,从1至n中取若干个数,使这些元素的和为m,找出所有的组合。 
类似有个题目,输入n,求n的所有拆分数目,如4=1+1+2,4=2+2,等等,这个可以用DP,子问题的分类标准是拆分后组合中的最小值,但是前面那个题目是升级版,因为元素不能重复且有范围限制。 
借助上面那个算法的分类思想写出的递归算法代码很简单,但是证明时间复杂度有点麻烦,输入1000后就不能很快出结果了,猜测是弱指数形式的复杂度。 
#include<iostream> 
#include <vector> 
using namespace std; 
vector<int> v; 
void show(){  
int s=v.size(); 
for(int i=0;i!=s;++i) 
cout<<v[i]<<" "; 
cout<<endl; 

void f(int start,int end,int m) 

if(m<=end&&start<=m) 
{v.push_back(m);show();v.pop_back();} 
int middle=m/2; 
if(middle<start) return; 
middle=end-1<middle?end-1:middle; 
for(int i=start;i<=middle;++i) 

v.push_back(i); 
f(i+1,end,m-i); 
v.pop_back(); 

  

  
void main() 

while(1) 

int n,m; 
cout<<"输入n m :"; 
cin>>n>>m; 
if( (1+n)*n/2<m) cout<<"NO Answer!"; 
if(n>m) n=m; 
f(1,n,m); 

::system("pause"); 

------------------------------------------------------------------------------------ 
                                 日期分割线 
------------------------------------------------------------------------------------ 
1.    根据上排给出的十个数,在其下排填出对应的十个数,要求下排每个数都是先前上排那十个数在下排出现的次数。(腾讯面试题)  
提示:上排大于10的下排一定为零,下排可以填的数字从9→1递推。  
2.    如何判断两个链表是否相交?如果相交,如何求第一个公共点?(微软面试题)  
提示:注意链表可能是有环的,首先判断两个链表是否有环,如果都没有环则可以通过尾部节点判断,如果有环可以通过快慢指针分别求出两个环中的一个节点,然后把这两个节点作为快慢指针的一个变形来判断是否是公共环(也可以简单遍历环判断另一个节点在不在环上)。求第一个公共点是类似的,如果都有环的情况下,先分别求出两个链表环的入口节点,如果不一样,返回其中一个即可,如果一样则转化成无环链表求第一个公共节点。求入口点的时候,可以先通过快慢指针把链表拆分成两个无环链表,再求公共点,即是入口点。  
3.    C库函数int strstr(char* str,char * ptn)的实现,在不同的时间复杂度、空间复杂度限制下设计算法。  
辅助空间为常数的算法:BM朴素算法,时间复杂度O(N*M)。Rabin-Carp算法,将字符串匹配转化成基数为127的大整数匹配,时间复杂度为O(N+M)。  
辅助空间不为常数的算法:KMP算法,需要O(M)的辅助空间来储存Next数组,时间复杂度为O(N)。  
折中的算法:Sunday算法,辅助数组为每个字符在substring中最后出现的位置,如果是ASSICII字符,则只需128的长度Assist[128],时间复杂度O(k*N)。  
4.    不用循环控制结构求1+2+……+N。  
提示:关键求N^2,如果是int,因为只有32位,可以通过32个位操作语句求得结果。  
(1<<k&N)&&(SUM+=N<<k);k∈(0,31)。  
5.    请实现C库函数int atoi(char * p).  
int  atoi(char * p)  
{  
      If(!p) throw  “NULL Pointer Error!”;  
      Bool negativeFlag=false; int numLength=0;  
      If(*p==’-‘) {negativeFlag=true;p++;}  
      Else if(*p==’+’){p++;}  
      Int result=0;  
      If(*p=’\0’) thorw “Ilegal Argument Error!”;  
      While(*p!=’\0’)  
      {  
      If(*p<=’9’&&*p>=’0’)  
      {result=result*10+(*p-‘0’);numlength++;}  
      Else throw “Ilegal Argument!”;  
      If(numlength>10) throw “OverFlow Error!”;  
}  
Return negativeFlag?-result:result;  
}  
6.    有n个长为m+1的字符串,如果某个字符串的最后m个字符与某个字符串的前m个字符匹配,则两个字符串可以连接。问这n个字符串最多可以连成一个多长的字符串,如果出现循环,则返回错误。  
提示:建模题目。把n个字符串做顶点,能否连接做有向边,把每个边的权值赋值为-1,转化为求一个图中任意两点的最短路径问题。因为可能有负数回路,可以用Bellman算法。如果提前判断图为拓扑图(通过拓扑排序或者DFS是否有反向边来判断),可以采用其他经典最短路径算法。  
7.    一个大小为N的数组,里面是N个整数,怎样去除重复,要求时间复杂度为O(n),空间复杂度为O(1).  
提示:因为严格的时间、空间复杂度限制,只能对基数排序进行改进,可以把int分为32位,从高位到低位进行partition。  
8.    求一个有向图的割点,割点的定义是去掉这个点以及与其相关的边,有向图不再连通。  
提示:在DFS树中,如果一个节点A的后代节点所连接的边不可能指向A的祖先,则去掉A后不再连通。  
      Struct Node{ string name;int visitedTime;int finishedTime;int lowest;Bool  isCutPoint};  
      Node Nodes[N]; int Arc[N][N]={0};  
      Int visited[N]={0};int Time=0;  
      Init();//初始化图 邻接矩阵表示  
      Void DFS(int root)// 调用完后 节点中的isCutPoint 表示是否为割点  
{  
      ++Time;  
      Nodes[root].visitedTime=Time;  
Nodes[root].Lowest=Time;  
      Visited[root]=1;  
      For(int i=0;i!=N;++i)  
          If(visited[i]==0&&Arc[root][i])  
              { DFS(i);  
               If(Node[i].Lowest<Node[root].Lowest)  
                  Node[root].Lowest=Node[i].Lowest;  
               If(Node[i].Lowest==Node[i].visitedTime)  
                  Node[root].isCutPoint=true;  
              }  
      Node[root].finishedTime=++Time;  
}  
Void Fix(int root) //修正根结点  因为根节点是否为割点 由它是否有多棵子树决定  
{ for(int i=0;i!=N;++i)  
      If(Arc[root][i]&&Node[i].visitedTime==2)  
          If(Node[i].finishedTime!=Node[root].finishedTime-1)  
              {Node[root].isCutPoint=true;return;}  
          Else { Node[root].isCutPoint=false;return;}  
}  
9.    一个整数数组,长度为N,将其分为M个非空子集,使各子集的元素和相等。求M的最大值。  
提示:M∈(1,N)&&  N%M==0 ,选定M后,用回溯法判断是否可以拆分为M。  
Int A[N]={……};int aux[N]={0};  
Int maxSplit(A,int size)  
{ int Sum=0;  
   For(int i=0;i!=N;i++)  
      Sum+=A[i];  
      For(int i=N;i>=2;i--)  
{  
      If(N%i==0)  
{  
      Memset(aux,0,sizeof(aux)*4);  
      If(Test( i,Sum , Sum, 1))  
          Return i ;  
}  
} return 1;  
}  
Int Test(int splitCount, int  Sum, int  RemainSum, int level)//划分子数组数,原数组元素//值之和,原数组未分配元素的和,第几个子数组  
{  
      if (RemainSum<0) return 0;  
      if (RemainSum==0) return 1;  
      for(int i=0;i!=N;++i)  
          if (aux[i]==0)  
              { aux[i]=level; int temp=0;  
              for (int j=0;j!=N;j++)  
                  if (aux[j]==level)  
                      temp+=A[j];  
              if ( temp>Sum/splitCount);  
              else if(temp<Sum/splitCount)  
                  if(Test(splitCount,Sum,RemainSum-temp,level))  
                      return 1;  
              else   
                  if(Test(splitCount,Sum,RemainSum-temp,level+1 ))  
                      return 1;  
              aux[i]=0;  
              }  
Return 0;  
}  
10.    设计一个只能生成一个实例的类,再设计一个不能被继承但可以生成实例的类。  
只能生成一个实例的类:  
Class A{  
Public: A(){ if(count==0) {count++; } else throw “Too Much Error!”;}  
~A(){}  
static int count; };  int A::count=0;  
    
不能被继承,但是可以生成实例的类:  
Template< typename T> class A{  
Private: A() {}  ~A(){}  
Friend T;};  
Class B: virtual public A{  
Public: B(){} ~B(){} };   //B 可以生成实例,但是不能被继承   
  
------------------------------------------------------------------------------------ 
                                 日期分割线 
------------------------------------------------------------------------------------ 
1. 有一个随机数生成器,p概率生成1,1-p概率生成0,问怎样以等概率生成1到N的一个数。  
提示:构造等概率事件,如01 与 10 就是两个等概率事件。  
2. 栈翻转的非递归算法。  
提示:有很多方法,这儿给一个比较漂亮的供参考。  
Void Reverse(Stack<int> & s)  
{ if ( s.size()==0||s.size()==1) return;  
    Object  a=s.pop();  
Reverse(s);  
    Object  b=s.pop();  
Reverse(s); s.push(a);  
     Reverse(s);s.push(b); }  
3. 已知字符串A,B,求A、B的最长公共子字符串,要求时间复杂度尽可能小。  
提示:A的长度为n,B的长度为m,如果直接求解,需要O(n*m*min(n,m)),采用DP可以降低复杂度到O(n*m),代码如下。  
string LCS(string A,string B)  
{ int n=A.length();  
   Int * aux=new int [n]; memset(aux,0,n*4);  
   Int maxLength=0,index=0;  
   For(int i=0;i<B.length();++i)  
For(int j=A.length()-1;j>=0;j--)  
{if (A[j]==B[i])  
{ if (j==0) aux[j]=1;  
   Else aux[j]=aux[j-1]+1;  
   if (aux[j]>maxLength) {maxLength=aux[j];index=j;}  
}  
Else aux[j]=0;  
}  
Return A.substring(index-maxLength+1,maxLength);  
}  
4. 果冻有红黄绿三种颜色,每次随机取一种,问取齐所有颜色的次数期望。  
提示:采用指示器变量,代入期望公式即可,答案5.5。  
5. 著名毒酒问题,1000桶酒有一桶有毒,老鼠喝了毒酒一周后发作,在一周内得出毒酒是哪桶,最少需要多少老鼠?怎么试验?  
提示:这个题目今年MS实习笔试还考了,我脑残的写了1000,当时觉得2^10=1024,每一周缩小一半,共十周用掉10只老鼠,可是题目写一周内得出结果,所以写了1000…… 答案应该是10,一周后10个老鼠有2^10种状态,一周内就可得出毒酒是哪个。  
6. 9点花十条直线,每条直线过三点。  
提示:花个锐角三角形,3条边上取3个点,内部加一个点。  
7. 12个球,用天平在3次内找出唯一的一个不同球,并判断是轻了还是重了。  
提示:8个球,3次找不同球的升级版本。  
第一步分444,后面要利用好不同集合间的元素交换即可。  
    
  
------------------------------------------------------------------------------------ 
                                 日期分割线 
------------------------------------------------------------------------------------ 
  
-- 
原创粉丝点击