1. 程式人生 > >測試開發工程師面試總結(二)——演算法篇

測試開發工程師面試總結(二)——演算法篇

演算法也屬於常見面試內容之一,但基本不會超過《劍指offer》的範圍,在此附上一篇簡書上整理的內容:
第二版java解法
常見的面試題包括以下幾類:字串操作,檔案輸入輸出流及統計,矩陣操作,單例模式等。

1.針對字串的操作:如字串反轉、字串去重、含有左右括號的字串匹配。

含有左右括號的字串匹配的題目及程式碼如下:

給定一個字串,其中的字元只包含三種括號:花括號{ }、中括號[ ]、圓括號( ),即它僅由 “( ) [ ] { }” 這六個字元組成。設計演算法,判斷該字串是否有效,即字串中括號是否匹配。括號匹配要求括號必須以正確的順序配對,如 “{ [ ] ( ) }” 或 “[ ( { } [ ] ) ]” 等為正確的格式,而 “[ ( ] )” 或 “{ [ ( ) }” 或 “( { } ] )” 均為不正確的格式。

/**
 * 棧的先入後出,非常適合作為括號匹配的物件 思路:將所有的左括號型別入棧,出現一個匹配的右括號即出棧
 * 如果最後棧為空則完全匹配;如果最後棧非空則不匹配
 */
public class MatchString {

       public static boolean isMatchStr(String s){
              if(s==null || s.length()<1) return false;
              Stack<Character> stack=new Stack<>();
              char
c; boolean flag=true; int cnt=0; for(int i=0;i<s.length();i++){ c=s.charAt(i); // 入棧規則 if(c=='(' || c=='[' || c=='{'){ stack.push(c); cnt++; continue
; } // 過濾非括號的無關字串 if(c!='(' && c!='[' && c!='{' && c!=')' && c!=']' && c!='}') continue; // 出棧規則 if(stack.isEmpty()||!isMatch(c,stack.peek())) return false; if (!stack.isEmpty() && isMatch(c, stack.peek())) { stack.pop(); cnt++; continue; } } if(!stack.isEmpty()) return false; return flag; } private static boolean isMatch(char right, char left) { if(right==')') return left=='('; if(right==']') return left=='['; if(right=='}') return left=='{'; return false; } public static void main(String[] args) { String s1="({{123}})"; String s2="}}}}}{{{{"; String s3="((111111({{}}})))"; System.out.println(isMatchStr(s1)); System.out.println(isMatchStr(s2)); System.out.println(isMatchStr(s3)); } }

字串反轉在劍指offer上也能找到類似的題目,反轉單詞順序:

輸入一個英文句子,翻轉單詞順序,單詞內字元不翻轉,標點符號和普通字母一樣處理。例如輸入輸入“I am a student.”,則輸出“student. a am I”。

/**
 * 先對所有字串進行反轉,之後按照空格分隔開,再對單個單詞進行反轉。
 */
public class ReverseWordsInSentence {

       public static String reverseStr(String str){
              if(str==null || str.length()<=1) return str;
              Stack<Character> stack=new Stack<>();

              StringBuffer stringBuffer=new StringBuffer();
              for(int i=0;i<str.length();i++){
                      stack.push(str.charAt(i));

              }
              for(int i=0;i<str.length();i++){
                      stringBuffer.append(stack.pop());
              }
              System.out.println(stringBuffer.toString());
              String[] strings=stringBuffer.toString().split(" ");
              stringBuffer.setLength(0); 
              for(int i=0;i<strings.length;i++){
                     stringBuffer.append(reverseWord(strings[i]));
                     stringBuffer.append(" ");
              }
              return stringBuffer.toString();

       }
       public static String reverseWord(String s){
              StringBuffer stringBuffer=new StringBuffer();
              if(s==null || s.length()<=1) return s;
              Stack<Character> stack=new Stack<>();
              for(int i=0;i<s.length();i++){
                     stack.push(s.charAt(i));
              }
              while(!stack.isEmpty()){
                     stringBuffer.append(stack.pop());
              }
              return stringBuffer.toString();
       }

       public static void main(String[] args) {
              String str="I am a student.";
              System.out.println(reverseStr(str));
       }
}

查詢字串重複問題因為會用到雜湊也常被放在面試中。

找出給定字串中第一個不重複的字母。例如:abcccaddd,第一個不重複的字母是b。

public class FirstNotRepeatingChar {
       public static char firstNotRepeatChar(String str){
//              256個字母
              char temp=str.charAt(0);
              int[] hashTimes=new int[256];
              for(int i=0;i<str.length();i++){
                     hashTimes[str.charAt(i)]++;
              }
              for (int i=0;i<str.length();i++){
                     if(hashTimes[str.charAt(i)]==1)
                            return str.charAt(i);
              }
              return '\77';
       }

       public static void main(String[] args) {
              System.out.println(firstNotRepeatChar("aaaaaaaaaaaaaa"));

       }
}
2. 檔案輸入流考察:檔案讀入並統計某個字元的值。
3. 矩陣(二維陣列)考察:矩陣的的順時針列印、順時針90度等。
4. 遞迴考察:其實遞迴是很容易考察到的一個點,我因為這塊薄弱掛掉了某廠。把兩個典型的遞迴的方法在這裡寫一下,希望大家也能有所啟發。遞迴重要的點一定是去找結束條件以及對應的返回值,多做幾個題目就熟練了。

1.求1的階乘到n的階乘的和。

public class GetSumFactorial {
       public static long getAddFactorial(long n){
              if(n<=0) return -1;
              long sum=0,start=1,end=n;
              long mid;
              while (start<=end){
                     sum+=getSingleFactorial(end);
                     end--;

              }
              return sum;
       }

       private static long getSingleFactorial(long i) {
              if(i<1) return -1;
              if(i==1) return 1;
              return getSingleFactorial(i-1)*i;
       }


       public static void main(String[] args) {
              System.out.println(getAddFactorial(4));
              // 異常值 如果n比較大的時候,遞迴消耗記憶體就會導致程式啟動報棧溢位異常,這個時候可以通過設定jvm的引數-Xss512k來解決這個問題 (如果主動考慮到這個 可以作為面試中的加分項哦)       System.out.println(getAddFactorial(100000));

       }
}

2.給一個有序排列的整數陣列a[n],整數陣列有且僅有一個值缺失,求當前缺失的值。例子 a[10]={0,1,2,3,5,6,7,8,9,10},返回缺失的值為4.
其中,方法一和方法二都是遞迴的思路,只不過方法一是用了二分法求和的遞迴思路,方法二是利用陣列的特點進行遞迴,遞迴仍然使用二分的思想。

public class QueryLackedInt {
       //       方法一:根據陣列特性即可求出缺失的值
       public static int getNum1(int[] data) {
              if (data == null || data.length == 0) return -1;
              if (data[0] != 0) return 0;
              int len = data.length;
              int sum1 = getSum(data, 0, len - 1);
              int sum2 = (len + 1) * (data[0] + data[len - 1]) / 2;
              return sum2 - sum1;
       }

       public static int getSum(int[] data, int low, int high) {
              if (data == null || data.length == 0) return -1;
              if (low == high) {
                     return data[low];
              } else {
                     int mi = (low + high) >> 1;
                     return getSum(data, low, mi) + getSum(data, mi + 1, high);
              }
       }

       //       方法二:使用兩個指標進行遞迴
       public static int getNum2(int[] data) {
              if (data == null || data.length == 0) return -1;
              if (data.length >= 1 && data[0] != 0) return 0;
              return getRecursion(data, 0, data.length-1);
       }

       private static int getRecursion(int[] data, int left, int right) {
              int mid = (left + right) / 2;
              if (right == left && data[left] != left) return left;
              if (data[mid] == mid) {
                    return getRecursion(data, mid+1, right);
              } else {
                  return   getRecursion(data, left, mid);
              }
       }

       public static void main(String[] args) {
              int[] a = {0, 1, 2, 3, 4, 5, 6, 7,  9, 10, 11, 12,13, 14, 15};
              int[] b = null;
              int[] c = {1, 2};
              System.out.println(getNum1(a));
              System.out.println(getNum1(b));
              System.out.println(getNum1(c));
              System.out.println(getNum2(a));
              System.out.println(getNum2(b));
              System.out.println(getNum2(c));


       }
}
5. 常見的排序演算法,冒泡、選擇、快速、歸併、堆排序等演算法要會,而且時間複雜度和空間複雜度的分析要會。
6. 單例模式:但凡要考察併發的使用,一般會要求寫單例模式,注意懶漢模式、餓漢模式;執行緒安全、執行緒不安全的單例模式;以及雙重校驗,單例模式最好的實現是列舉實現,舉出列舉實現的例子並且講出原理。
7. 考察二叉樹、紅黑樹等。