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;
   

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