Java 笔试

一著名软件公司的java笔试算法题!

(2008-11-06 12:49:19)

原题如下:用1、2、2、3、4、5这六个数字,用java写一个main函数,打印出所有不同的排列,如:512234、412345等,要求:"4 "不能在第三位, "3 "与 "5 "不能相连.
我看了回贴都没有很好解决,主要是没有排除重复。
解决思路:强化题目,用1、2、2、3、4、5这六个数字排列“递增”序列。其他要求不变。
算法思路:显然是递归,初始序列122345,先从末两位(45)变化(45,54),然后末三位(345)  ...  直到最后六位.怎样解决重复问题?很简单,由于是递增序列,每生成新序列可与前一生成序列比较,如<放弃当前序列。当然有更好效率,如预先预测。代码如下:
class   test
{  
    //  当前固定部分
    private  String   CurFixPart;
    private  String   PreGenNum;
   
public   static   void  main(String[]   args)
{
  test   t=new  test();
  t.GenControll( "122345 ");
}

//   调整字符串s位置pos字符到最前
private   String   shift(String  s,   int  pos)
{
String   newStr;
if   (s.length()> pos+1)
    newStr=s.substring(pos,  pos+1)
              +s.substring(0,  pos)
              +s.substring(pos+1);
else
    newStr=s.substring(pos)
              +s.substring(0,  pos);
return   newStr;
}

protected   int   Validate(String  newNum)
{
    String  newGenNum=CurFixPart+newNum;
    if  (Integer.valueOf(newGenNum)<=Integer.valueOf(PreGenNum))
       return   0;
    if  (newGenNum.substring(2,3).equals( "4 ")   || 
            (newGenNum.indexOf( "35 ")!=-1)  ||   (newGenNum.indexOf( "53")!=-1))  
       return   0;
          
    PreGenNum=newGenNum;
System.out.println(newGenNum);
return   0;
}
 
public   void  GenControll(String   Base)
{
    PreGenNum= "0 ";
CurFixPart= " ";
GenNext(Base,   0);
}

void   GenNext(String   varPart,  int   curPos)
{
if   (varPart.length()==2)
{
    Validate(varPart);
    Validate(shift(varPart,  1));
    return;
  }
//   Next   Layer
String   newGen=shift(varPart,  curPos);
String   SavedFixPart=CurFixPart;
CurFixPart=CurFixPart+newGen.substring(0,1);
GenNext(newGen.substring(1),   0);
CurFixPart=SavedFixPart;
//   同层递增
if   (curPos==varPart.length()-1)   
    return;
GenNext(varPart,   curPos+1);
}
}
序列122345测试通过。

1  把问题归结为图结构的遍历问题。实际上6个数字就是六个结点,把六个结点连接成无向连通图,对于每一个结点求这个图形的遍历路径,所有结点的遍历路径就是最后对这6个数字的排列组合结果集。
2   显然这个结果集还未达到题目的要求。从以下几个方面考虑:
    1.  3,5不能相连:实际要求这个连通图的结点3,5之间不能连通,  可在构造图结构时就满足改条件,然后再遍历图。
    2.   不能有重复:  考虑到有两个2,明显会存在重复结果,可以把结果集放在TreeSet中过滤重复结果
    3.  4不能在第三位:   仍旧在结果集中去除满足此条件的结果。

采用二维数组定义图结构,最后的代码是:

import   java.util.Iterator;
import   java.util.TreeSet;

public   class   TestQuestion  {

private   String[]   b  =   new  String[]{ "1 ",   "2 ",   "2 ",  "3 ",   "4 ",  "5 "};
private   int   n  =   b.length;
private   boolean[]   visited  =   new  boolean[n];
private   int[][]   a  =   new  int[n][n];
private   String   result  =   " ";
private   TreeSet   set  =   new  TreeSet();

public   static   void  main(String[]   args)  {
new   TestQuestion().start();
}

private   void   start()  {

//   Initial   the  map   a[][]
for   (int   i  =   0;   i  <   n;   i++)  {
for   (int   j  =   0;   j  <   n;   j++)  {
if   (i   ==  j)   {
a[i][j]   =   0;
}   else   {
       a[i][j]   =  1;
}
}
}

//   3   and   5  can   not   be  the   neighbor.
a[3][5]   =   0;
a[5][3]   =   0;

//   Begin   to  depth search.
for   (int   i  =   0;   i  <   n;   i++)  {
       this.depthFirstSearch(i);
}

//   Print   result  treeset.
Iterator   it   =  set.iterator();
while   (it.hasNext())   {
String   string   =  (String)   it.next();
//   "4 "   can  not   be   the  third   position.
if   (string.indexOf( "4 ")   !=  2)   {
System.out.println(string);
}
}
}

private   void  depthFirstSearch(int   startIndex)  {
visited[startIndex]   =  true;
result   =   result  +   b[startIndex];
if   (result.length()   ==  n)   {
//   Filt   the  duplicate   value.
set.add(result);
}
for(int   j   =  0;   j  <   n;   j++)  {
if   (a[startIndex][j]   ==  1  &&   visited[j]  ==   false)  {
depthFirstSearch(j);
}   else   {
continue;
}
}

//   restore   the  result   value  and   visited  value   after  listing   a  node.
       result   =  result.substring(0,   result.length()  -1);
       visited[startIndex]   =  false;
}
}
3,5不能相连:实际要求这个连通图的结点3,5之间不能连通,  可在构造图结构时就满足改条件,然后再遍历图。
  代码中请注意这几行:
  //   3   and  5   can   not  be   the  neighbor.
  a[3][5]   =  0;
  a[5][3]   =  0;

只要这样定义图,根本不用在代码中写IF   ELSE语句。
实际上基于图的算法好处在于,只要你能定义好满足题目要求的图结构,遍历的结果就是你要的结果,不用任何对遍历结果做任何处理。包括本题中的:4不能在第三位置,3,5不能相连,唯一
性要求,其实都可以在体现在构造的图形结构里,然后直接遍历图取得自己要的结果。而不用再次处理结果集。

只是说这里实际上对其它要求要体现在图结构里有困难(理论上是可以的),但起码3,5不能相接是很好构造的,就是上面的代码段来解释的。

关于图形数据结构建议先看看数据结构的书,主要是将如何利用二维数组描述图结构,再看看图的深度遍历实现原理。最后再应用到这个问题上来,自然就不难明白了。

补充:由于时间的关系,上面给的代码没有多做考虑。
其实有两方面可以优化一下:
1。Validate  可以学习Struts,利用抽象Validate集去掉If/Else语句。在此不多说。
2。在同层递增前增加“重复预测“减少不必要的重复排序,提高效率。
改进代码如下,其中“//********”处为修改地方
class   test
{  
    //  当前固定部分
    private  String   CurFixPart;
    private  String   PreGenNum;
   
public   static   void  main(String[]   args)
{
  test   t=new  test();
  t.GenControll( "1111111111 ");
}

//   调整字符串s位置pos字符到最前
private   String   shift(String  s,   int  pos)
{
String   newStr;
if   (s.length()> pos+1)
    newStr=s.substring(pos,  pos+1)
              +s.substring(0,  pos)
              +s.substring(pos+1);
else
    newStr=s.substring(pos)
              +s.substring(0,  pos);
return   newStr;
}

protected   int   Validate(String  newNum)
{
    String  newGenNum=CurFixPart+newNum;
 
    if  (Integer.valueOf(newGenNum)<=Integer.valueOf(PreGenNum))
       return   0;
 
    if  (newGenNum.substring(2,3).equals( "4 ")   || 
            (newGenNum.indexOf( "35 ")!=-1)  ||   (newGenNum.indexOf( "53")!=-1))  
       return   0;
          
    PreGenNum=newGenNum;
System.out.println(newGenNum);
return   0;
}
 
public   void  GenControll(String   Base)
{
    PreGenNum= "0 ";
CurFixPart= " ";
GenNext(Base,   0);
}

void   GenNext(String   varPart,  int   curPos)
{
if   (varPart.length()==2)
{
    Validate(varPart);
    Validate(shift(varPart,  1));
    return;
  }
//   Next   Layer
String   newGen=shift(varPart,  curPos);
String   SavedFixPart=CurFixPart;
CurFixPart=CurFixPart+newGen.substring(0,1);
GenNext(newGen.substring(1),   0);
CurFixPart=SavedFixPart;
//*************
//   同层递增
while   (true)
{
if   (curPos==varPart.length()-1)   
    break;
    //  预测是否重复
    curPos++;
if   (Integer.valueOf(CurFixPart+shift(varPart,  curPos))<=Integer.valueOf(PreGenNum))
       continue;
      
GenNext(varPart,   curPos);
break;
}
//*************
}
}

 

本来以为很简单,用递归一下就搞定的问题,结果按照递归的常规思路,在打印的时候碰到了很麻烦的问题,费了我不少脑筋,结果答案还是很简单,弄得我做出来了过后都准备把结果放到博客商保存了。其实我做的是字符串排列显示的问题,其中打印时的条件判断语句是根据楼主的特殊要求添加的,诸位参考:
import   java.util.*;
public   class   test  {
public   static   void  main(String[]   arg)  {
Scanner   r=new  Scanner(System.in);
String   s=r.nextLine();
Pailie(s, " ");
}
static   void   Pailie(String  s,   String  p)   {
if(s.length() <1)   {
String   t=p+s;
if(t.charAt(2)!= '4 '  &&   t.contains("35 ")==false)
System.out.println(t);
}
else   {
for(int   i=0;   i<s.length();   i++)  {
Pailie(s.substring(1),p+s.substring(0,1));
s=s.substring(1)+s.substring(0,1);
}
}
}
}

提醒一下,大家都没理解,其实这题难点是如何有效排除重复的排列。
JianZhiZG(健之)   你可以试一下1223,你的算法没有排除重复

对算法的最终改进,这次应该是比较完整的版本了吧。
思路是这样的,对于任意一个串利用递归进行排列时,我们是循环串中的每个字符到第一个字符进行递归。如果串中字符出现重复的话,则重复的字符只可以利用递归算法一次,即只要与前面相同的字符循环到第一个字符时不调用递归就可以避免重复,为此,我们只需要按如下方式修改算法:
import   java.util.*;
public   class   test  {
static   int   count=0;
       public   static  void   main(String[]  arg)   {
              Scanner  r=new   Scanner(System.in);
              String  s=r.nextLine();
              Pailie(s, " ");
              System.out.println( "Total:"+count);
       }
       static   void  Pailie(String   s,String  p)   {
              if(s.length()<1)   {
             System.out.println(p);//字符串长度小于1,换行
              count++;
              }
              else  {
              int  index[]=new   int[s.length()];
              for(int  i=0;   i <s.length();  i++)//该循环将所有字符的第一次出现的位置记录在数组index中
             index[i]=s.indexOf(s.charAt(i));
              for(int  i=0;   i <s.length();  i++)   {
             if(i==index[i])//只有当循环数与第一次记录数相等时才递归,保证相同字符中的第一个调用
             Pailie(s.substring(1),p+s.substring(0,1));//递归,打印其它字符
             s=s.substring(1)+s.substring(0,1);//循环移位
                     }
              }
       }
}
这样,由于相同的字符只递归调用了一次,则避免了重复串的排列。下面是几个典型的运算结果:
2222222(输入。当串中的所有字符相同时,应该递归调用1次)
2222222
Total:1

122222(输入。当串中只有一个字符不同时,该字符应该循环所有不同位置)
122222
222221
222212
222122
221222
212222
Total:6

1223(输入。szlhj的例子)
1223
1232
1322
2231
2213
2312
2321
2123
2132
3122
3221
3212
Total:12

122345(输入。本题的要求,最后结果为360个,其它的我就不列出来了,大家可以自己测试)
122345
122354
122453
122435
122534
……
543221
543212
Total:360

 

贴个C语言版的:

#include  

<stdio.h>

#define   MAXN   6

int   a[MAXN],   o[MAXN],  used[256]   =  {   0   },  count   =  0;

void   print()
{
       int   i;
       for   (i   =  0;   i  <   MAXN;  ++i)
              printf( "%d ",  o[i]);
       printf( "\n ");
}

void   solve(const   int  depth)
{
       int   i;

       for   (i   =  0;   i  <   MAXN;  ++i)
       {
              if  (used[i]   >  0)
              {
                    --used[i];

                     if  (
                           !(i   +   1  ==   4  &&   depth  ==   2)  &&   //  4   at  position   3
                           !(i   +   1  ==   3  &&   depth  >   0  &&  o[depth   -  1]   ==   5)  &&   //  53
                           !(i   +   1  ==   5  &&   depth  >   0  &&  o[depth   -  1]   ==   3)        //  35
                     )
                     {
                           o[depth]   =  i   +   1;
   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章