有趣的程序

Java程序

一百亿计算器

首先要明白这道题目的考查点是什么,一是大家首先要对计算机原理的底层细节要清楚、要知道加减法的位运算原理和知道计算机中的算术运算会发生越界的情况,二是要具备一定的面向对象的设计思想。

首先,计算机中用固定数量的几个字节来存储的数值,所以计算机中能够表示的数值是有一定的范围的,为了便于讲解和理解,我们先以byte类型的整数为例,它用1个字节进行存储,表示的最大数值范围为-128+127-1在内存中对应的二进制数据为11111111,如果两个-1相加,不考虑Java运算时的类型提升,运算后会产生进位,二进制结果为1,11111110,由于进位后超过了byte类型的存储空间,所以进位部分被舍弃,即最终的结果为11111110,也就是-2,这正好利用溢位的方式实现了负数的运算。-128在内存中对应的二进制数据为10000000,如果两个-128相加,不考虑Java运算时的类型提升,运算后会产生进位,二进制结果为1,00000000,由于进位后超过了byte类型的存储空间,所以进位部分被舍弃,即最终的结果为00000000,也就是0,这样的结果显然不是我们期望的,这说明计算机中的算术运算是会发生越界情况的,两个数值的运算结果不能超过计算机中的该类型的数值范围。由于Java中涉及表达式运算时的类型自动提升,我们无法用byte类型来做演示这种问题和现象的实验,大家可以用下面一个使用整数做实验的例子程序体验一下

 

Socket编程实例

5Java 的通信编程,编程题(或问答),用JAVA SOCKET编程,读服务器几个字符,再写入本地显示? 

:Server端程序:

 package test; import java.net.*; import java.io.*; public class Server { private ServerSocket ss; private Socket socket; 

private BufferedReader in; private PrintWriter out; public Server() { try { ss=new ServerSocket(10000); while(true) { socket = ss.accept(); 

String RemoteIP = socket.getInetAddress().getHostAddress(); String RemotePort = ":"+socket.getLocalPort(); System.out.println("A client come in!IP:"+RemoteIP+RemotePort); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = in.readLine(); System.out.println("Cleint send is :" + line); out = new PrintWriter(socket.getOutputStream(),true); out.println("Your Message Received!"); out.close(); in.close(); socket.close(); } }catch (IOException e) { out.println("wrong"); } } public static void main(String[] args) { new Server(); } } 

Client端程序:

 package test; import java.io.*; import java.net.*; public class Client { Socket socket; BufferedReader in; PrintWriter out; public Client() { try { System.out.println("Try to Connect to 127.0.0.1:10000"); socket = new Socket("127.0.0.1",10000); System.out.println("The Server Connected!"); System.out.println("Please enter some Character:"); BufferedReader line = new BufferedReader(new InputStreamReader(System.in)); out = new PrintWriter(socket.getOutputStream(),true); 

class的动态载入和更新

       只要我们能够动态改变类搜索路径和清除classloader的cache中已经载入的Class就行了,有两个方案,一是我们继承一个classloader,覆盖loadclass方法,动态的寻找Class文件并使用defineClass方法来;另一个则非常简单实用,只要重新使用一个新的类搜索路径来new一个classloader就行了,这样即更新了类搜索路径以便来载入新的Class,也重新生成了一个空白的cache(当然,类搜索路径不一定必须更改)。噢,太好了,我们几乎不用做什么工作,java.netURLClassLoader正是一个符合我们要求的classloader!我们可以直接使用或者继承它就可以了!

       将接口或抽象类放到classpath中,自定义classloader加载实现类

线程j的加减

设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。

以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题。

public class ThreadTest1{private int j;  public static void main(String args[]){  ThreadTest1 tt=new ThreadTest1();  Inc inc=tt.new Inc(); Dec dec=tt.new Dec();  for(int i=0;i<2;i++){  Thread t=new Thread(inc);  t.start();  t=new Thread(dec);  t.start();  } }   private synchronized void inc(){  j++;  System.out.println(Thread.currentThread().getName()+"-inc:"+j);  }

  private synchronized void dec(){ j--;  System.out.println(Thread.currentThread().getName()+"-dec:"+j);  }

class Inc implements Runnable{  public void run(){  for(int i=0;i<100;i++){  inc();  } }  } class Dec implements Runnable{public void run(){  for(int i=0;i<100;i++){ dec(); } }  } }

汉字字符截取

7、编程:编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个”。 
答:代码如下: package test; class SplitString { String SplitStr; int SplitByte; public SplitString(String str,int bytes) { SplitStr=str; SplitByte=bytes; System.out.println("The String is:′"+SplitStr+"′;SplitBytes="+SplitByte); } public void SplitIt() { int loopCount; loopCount=(SplitStr.length()%SplitByte==0)?(SplitStr.length()/SplitByte):(SplitStr.length()/Split Byte+1);System.out.println("Will Split into "+loopCount); for (int i=1;i<=loopCount ;i++ ) { if (i==loopCount){ 
System.out.println(SplitStr.substring((i-1)*SplitByte,SplitStr.length())); } else { 
System.out.println(SplitStr.substring((i-1)*SplitByte,(i*SplitByte))); } } } public static void main(String[] args) { SplitString ss = new SplitString("test中dd文dsaf中男大3443n中国43中国人 0ewldfls=103",4); ss.SplitIt(); } } 

数字、字符串互转

数字变字符:取模+‘0’,倒置

字符串变数字思路:先对a[0]判断,在对a[1  n-1]求:num=num*10+a[i]-‘0’ ;

进制转换程序的写法(MàN

   While(m!=0) {

      I++;

      sum=sum+pow(m%N,i);

      m=m/N;

}

数字排列

1.   1,2,2,3,4,5  六个数字,打印出所有的排列,其中 35不能相连,4不能在第三位

     六个数字作为联通无向图,从每一个顶点出发的所有的遍历路径

    3 5 不能相连 在图上显示

    不能有重复,如两个2 ,可以使用 treeset 过滤重复

    4  不能在第三位,在结果集中过滤

  实现:

    1. 使用二维矩阵表示边, 一维数组表示结点

算法

英文拼写纠错

在用户输入英文单词时,经常发生错误,我们需要对其进行纠错。假设已经有一个包
含了正确英文单词的词典,请你设计一个拼写纠错的程序。
1)请描述你解决这个问题的思路;
2)请给出主要的处理流程,算法,以及算法的复杂度;
3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。

 

思路 :

字典以字母键树组织,在用户输入同时匹配

流程:

每输入一个字母:
沿字典树向下一层,
a)若可以顺利下行,则继续至结束,给出结果;
b)若该处不能匹配,纠错处理,给出拼写建议,继续至a);

算法:

1.在字典中查找单词

字典采用27叉树组织,每个节点对应一个字母,查找就是一个字母

一个字母匹配.算法时间就是单词的长度k.

2.纠错算法

情况:当输入的最后一个字母不能匹配时就提示出错,简化出错处理,动态提示可能处理方法:
(a)
当前字母前缺少了一个字母:搜索树上两层到当前的匹配作为建议;
(b)当前字母拼写错误:当前字母的键盘相邻作为提示;(只是简单的描述,可以有更多的)
根据分析字典特征和用户单词已输入部分选择(a),(b)处理

复杂性分析:影响算法的效率主要是字典的实现与纠错处理
(a) 字典的实现已有成熟的算法,改进不大,也不会成为瓶颈;
(b) 纠错策略要简单有效 ,如前述情况,是线性复杂度;
改进:

策略选择最是重要,可以采用统计学习的方法改进。

寻找热门查询

搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串
的长度为1-255字节。假设目前有一千万个记录,

这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个
。一个查询串的重复度越高,说明查询它的用户越多,
也就是越热门。请你统计最热门的10个查询串,要求使用的内存不能超过1G
1)请描述你解决这个问题的思路;
2)请给出主要的处理流程,算法,以及算法的复杂度。

(1)思路:用哈希做
(2)
首先逐次读入查询串,算哈希值,保存在内存数组中,同时统计频度
(注意值与日志项对应关系)
选出前十的频度,取出对应的日志串,简单不过了。
哈希的设计是关键。

 

集合合并

给定一个字符串的集合,格式如:
{aaa bbb ccc} {bbb ddd}{eee fff}{ggg}{ddd hhh}
要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出

{aaa bbb ccc ddd hhh}{eee fff} {ggg}
1)请描述你解决这个问题的思路;
2)请给出主要的处理流程,算法,以及算法的复杂度
3)请描述可能的改进(改进的方向如效果,性能等等,这是一个开放问题)。

 

1)思路:先将集合按照大小排列后,优先考虑小的集合是否与大的集合有交集。有
就合并,如果小集合与所有其他集合都没有交集,则独立。独立的集合在下一轮的比较中不用考虑。这样就可以尽量减少字符串的比较次数。当所有集合都独立的时候,
就终止。
2)处理流程:
1.将集合按照大小排序,组成集合合并待处理列表
2.选择最小的集合,找出与之有交集的集合,
如果有,合并之;
如果无,则与其它集合是独立集合,从待处理列表中删除。
3.重复直到待处理列表为空

算法:
1。将集合按照大小从小到大排序,组成待处理的集合列表。
2。取出待处理集合列表中最小的集合,对于集合的每个元素,依次在其他集合中搜索是否有此元素存在:
1>若存在,则将此小集合与大集合合并,并根据大小插入对应的位置。转3
2>若不存在,则在该集合中取下一个元素。如果无下一个元素,即所有元素
都不存在于其他集合。则表明此集合独立,从待处理集合列表中删除。并加入结果集
合列表。3。如果待处理集合列表不为空,转2
如果待处理集合列表为空,成功退出,则结果集合列表就是最终的输出。
算法复杂度分析:
假设集合的个数为n,最大的集合元素为m
排序的时间复杂度可以达到n*log(n)
然后对于元素在其他集合中查找,最坏情况下为(n-1*m
查找一个集合是否与其他集合有交集的最坏情况是m*m*(n-1)
合并的时间复杂度不会超过查找集合有交集的最坏情况。
所以最终最坏时间复杂度为O(m*m*n*n)
需要说明的是:此算法的平均时间复杂度会很低,因为无论是查找还是合并,都是处
于最坏情况的概率很小,而且排序后优先用最小集合作为判断是否独立的对象,优先
与最大的集合进行比较,这些都最大的回避了最坏情况。
(3)可能的改进:

首先可以实现将每个集合里面的字符串按照字典序进行排列,这样就可以将查找以及
合并的效率增高。

另外,可能采取恰当的数据结构也可以将查找以及合并等操作的效率得到提高。

八皇后问题:回溯法

思路:假设i-1已经处理完成,如果i>树的高度,输出序列。否则求出i层满足条件的布局,插入B中,递归处理下面,再将B弹出(代表回溯,这步很重要)

void queen(int i,vector<int> B,int n) {

 

     if (i==n) {

               display(B);

     } else {

               for(int h=0;h<n;h++) {

                      for(int m=0;m<B.size();m++) {

                             int w=B.at(m);

                             if(w==h || abs(h-w)==(i-m)) {

                     break;

                             }

                      }

                      if(m>=B.size() ) {

                             B.push_back(h);

                             

                             queen(i+1,B,n);

                             B.pop_back();

                      }

                      

               }

        

     }

}

N[0,k]之间随机数

for i =[0, n)

    x[i]= n%k

for i =[0,n)

    swap(i, randint(i, n-1))

    print x[i]

 

快速排序、 图的深度和广度遍历, 拓扑排序和强联通, 动态规划矩阵炼乘法

编程珠玑

整数排序

         1千万个数放在文件中,每个整数为7位正整数,最多出现一次,内存只有1M,运行时间10几秒

思考:

1.  如果使用32Integer存储则需要40M存储空间,远超于内存,则只能使用外部排序。

2.  1M /4B=250K,为一次可在内存中排序的整数个数,则创建 9999999 / 250k 个临时文件,每个文件存储相应范围的整数,如[k*250KB, (k+1)*250KB ]

3.  扫描原始文件,将每个整数保存到相应文件中

4.  按顺序对每个文件在内存中做快速排序并写到对应结果文件

5.  按顺序合并结果文件

分析:

         按以上思路需要40读取和写入文件,原因是需要40MB的存储,是否可采用一种合适的表达方式,将10,000,000个数字使用更少的bit来表示,如使用位图或位向量(即n位的二进制序列表示最大值为n的数,当值为i的数存在时,第i位为1)来表示集合,则表示1千万需要1.25MB 存储。这种思路利用了排序数据中不常见的属性:输入数据在较小范围内;数据没有重复;对每条记录无关联数据

解法:

1.       将所有位置零,扫描文件对相应位置1

2.       按顺序校验每一位,如果该位为1则输出相应的整数

总结:

1.       位图数据结构表示有限定义域中的稠密集合,其中的每一个元素最多出现一次,且没有其他任何数据与该数据关联,即使有关联,也可将该值作为更复杂表格的索引。

2.       当需要同时减少空间和时间时,有两个方法:减少处理的数据;保存在内存中。当然了只有原始设计不是最佳方案时才能实现

3.       简单设计:设计不能再减少东西了

4.       如果限定在1M中,则2次遍历,第一次只处理0-4999999,第二次处理5000000-9999999.

5.       每个数字最多出现10次时?   对每个数字使用4bit存放

缺失的1个整数

         1000,000 个整数,每个整数在 0,3000,000之间,每个数只出现一次

解法:

         首先要遍历一遍数据,找出最大数M和最小数N,计算出中位数的大小,然后统计中位数以上和以下的数字个数,选择个数不到(M-N+1/2的一段重复上述的过程。

给定一个n元实数集合,一个实数t和一个整数k,如何快速确定是否存在一个k元子集,使其元素之和不超过t

解法:

构建一个最小堆,取其前k个最小的数。如果用topk的思路,那么应该更加快速,构建一个k个元素的最大堆来获得k个最小的元素。

430000000032位整数的顺序文件,如何找出一个至少出现两次的整数?

解法:

根据鸽巢定理,这写32位的整数中肯定有重复的。那么我每次统计中位数上和下两部分各自的个数,选取多于n/2的部分重复此过程即可。如果空间足够大,可以用bitmap的方法,已经有的数bit位设置为1,如果有数字的bit位置1时该位已经为1了,那么这个数就出现了2次。

根据电话号码查找名字,有可能重复。

与第一题类似,名字映射到电话号码索引,冲突的名字存在链表里。

书中采用和第一题类似的思路,最后提到现实中一般采用散列和数据库系统。

向量旋转

         N元字符串向左旋转i个位置,如 abcdefgh向左旋转 3得到 defghabc。要求几十字节的额外空间,O(n)时间内。类似问题:交换相邻的不同大小的内存块,拖动交换两块内存中的文字。

解法1

         For j=0 to i  {

                   t=x[j];

                   for(h=0;i*h+j<n;h++) {

                            X[(h-1)*i+j]=x[i*h+j];  // a[3]->a[0]  a[6]->a[3]

                   }  

                   X[i*(h-1)+j]=t;  // t->a[6]

         }

解法2

         将字符串分成 ab1b2三部分,其中a为前i个字符,b2a长度相同。先将ab2交换得到 b2b1a, a在正确位置,继续对b2b1进行递归调用。

快速转置4000*4000的矩阵。

对于每个点,考虑该点被置换到正确位置时,原来位置去哪了?比如,N*N的矩阵来说,[0,0]->[0,N-1]->[N-1,N-1]->[N-1,0]->[0,0],箭头后面的数表明要去的坐标,可以看出是一个循环,于是我们可以根据如下公式进行转置:

t = m[i,j];

m[i,j]= m[N-j-1,i];

m[N-j-1,i]= m[N-i-1,N-j-1];

m[N-i-1,N-j-1]= m[j,N-i-1];

m[j,N-i-1]= t;

对于每一种可能的环进行一次上述操作即可。

书中的答案思路很赞,而且由于采用的是磁带存储,执行起来有可能更好:

对每个元素,插入行号和列号,先按列进行升序排序,然后在对每4000个进行按行降序排序,最后删除行号和列号即可。

子向量的和

问题

         N个浮点数的向量,找出连续子向量中的最大和

思考:

1.       最简单三重循环,计算Aij的值,并记录最大值  ,为O(n^3)

2.       发现Aij的计算中有重复子结构,Aij=Ai(j-1)+aj  O(n^2)

3.       Aij=A0j-A0i,为O(n^2)

4.       n^2考虑了所有的子向量,因为存在O(n^2)的子向量,所以至少需要平方和的实现,通过想办法避免监测所有的子向量,获得更短时间的算法,使用分治法,将n分为大小类似的ab两段,则最大向量为 AaAb、起点在a,终点在b 三种情况的最值,前两种是递归,后一种对于a length[a]向前的最大值+b[0]向后的最大值,则F(n)=2F(n/2)+O(n) O(nlgn)

5.       扫描算法:I 0 to n-1 计算从0i的最大值,该值有两种可能(0, i-1)间和以i结尾, A0(i-1)已经存在,以i结尾的最值 max( maxending+a[i] ) O(n)

技术:

         保存状态,避免重复计算

         将信息预处理到数据结构,如算法3

         分治算法:有可能达到 nlgn

         扫描算法:与数组相关的算法一般可通过思考如何将 a[0,i-1]扩展到a[0,i]来实现。

         累积:范围问题,如 310月销售额,可通过至10月销售额-2月销售额

         下界:证明本问题的算法下界

 

字符串

最长重复字串

    问题:查找一个文本文件中,出现的最长重复子字符串,要求O(nlgn)时间内

    解法: 使用“后缀数组”的数据结构(字符指针数组 a,a[0]指向整个,a[1]指向第二位开始的,a[2]为第三位到结束),如果某个字符串出现两次,则a[i]和a[j]的前面几位都是该字符串。步骤如下:1. 构造后缀数组;2. 使用qsort进行排序;3. for i 0 to n 从开始比较 a[i] 和 a[i+1] 得到重复子字符串,求得其最长值

 

生成文本

生成看起来更像英文的文本

    先读取样本,统计每个字母之后出现的次数;在写随机文本时,使用当前字母的一个随机函数生成下一个字母,这是一节文本,如果使用前面四个字母生成下一个字母就非常类似于英文单词了

在单词级别生成随机文本

    读取样本,对每个单词计数,根据概率选择下一个输出的单词,如果生成下一个单词时考虑前面几个单词的马尔科夫链,则会等到更感兴趣的文本

走阶梯

     N介楼梯,一个人每次走1 或2个, 请问有多少种走法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值