1. 程式人生 > >劍指Offer演算法題及答案Java完整版(一)

劍指Offer演算法題及答案Java完整版(一)

1、輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有的奇數位於陣列的前半部分,所有的偶數位於位於陣列的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

package cn.ctgu.offer;
/*
 * 輸入一個整數陣列,實現一個函式來調整該陣列中數字的順序,使得所有的奇數位於陣列的前半部分
 * 所有的偶數位於位於陣列的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。
 * 
 * 思路:
 * 1、遍歷陣列將所有的奇數找出來放在一個新陣列的前面
 * 2、再次遍歷陣列將所有的偶數找出來放在新陣列的後面
 * 
 * */
public class AdjustArray {
    public int[] reOrderArray(int [] array) {
        int[]new_array=new int[array.length];
        int j=0;
        //遍歷陣列先將其中所有的奇數找出來放在新陣列的前面
        for(int i=0;i<array.length;i++) {
            if(array[i]%2!=0) {
                new_array[j]=array[i];
                j++;//
            }
        }
        //再次遍歷陣列將其中的所有偶數找出來放在新陣列的後面
        for(int i=0;i<array.length;i++) {
            if(array[i]%2==0) {
                new_array[j]=array[i];//先賦值給j,因為前面的j+1但是沒有賦值,所以這個位置不需要先加1
                j++;
            }
        }
        return new_array;
    }
    /*
     * 
     * 類似冒泡演算法,前偶後奇數就交換:
     * 
     * 
     * */
     public void reOrderArray1(int [] array) {
           for(int i= 0;i<array.length-1;i++){
                for(int j=0;j<array.length-1-i;j++){
                    if(array[j]%2==0&&array[j+1]%2==1){
                        int t = array[j];
                        array[j]=array[j+1];
                        array[j+1]=t;
                    }
                }
            }
        }
    public static void main(String[]args) {
        int[]array= {1,2,3,4,5,6,7,8,9};
        int[]new_array=new int[array.length];
        AdjustArray adjust=new AdjustArray();
        new_array=adjust.reOrderArray(array);
        for(int i=0;i<new_array.length;i++) {
            System.out.println(new_array[i]);
        }
    }
}



2、求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

package cn.ctgu.offer;
/*
 * 題目:
 * 求1+2+3+...+n,
 * 要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。
 * 
 * 
 * 思路:
 * 1、利用邏輯與的短路特性實現遞迴終止
 * 2、當n==0時,(n>0)&&((sum+=Sum_Solution(n-1))>0)只執行前面的判斷,為false,然後直接返回0;
 * 3、當n>0時,執行sum+=Sum_Solution(n-1),實現遞迴計算Sum_Solution(n)。
 * 
 * */
public class CalculateSum {
    public int Sum_Solution(int n) {
        int sum=n;
        boolean flag=(sum>0)&&((sum=sum+Sum_Solution(--n))>0);
        return sum;
    }
    public static void main(String[]args) {
        CalculateSum solution=new CalculateSum();
        int s=solution.Sum_Solution(3);
        System.out.println(s);
    }
}



3、求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

package cn.ctgu.offer;
/*
 * 題目:
 * 求出1~13的整數中1出現的次數,並算出100~1300的整數中1出現的次數?
 * 為此他特別數了一下1~13中包含1的數字有1、10、11、12、13因此共出現6次,但是對於後面問題他就沒轍了。
 * ACMer希望你們幫幫他,並把問題更加普遍化,可以很快的求出任意非負整數區間中1出現的次數
 * (從1 到 n 中1出現的次數)。
 * 
 * 
 * 思路:
 * 1、將數字轉成字串陣列(因為不知道n多大,只能這樣做)
 * 2、從1遍歷到n,將所有的數字儲存到一個字元緩衝區中
 * 3、將該數字轉換成字串
 * 4、查詢其中1的個數
 *
 * */
public class ContainsOne {
    public int NumberOf1Between1AndN_Solution(int n) {
        //1、將數字轉成字元陣列
        int count=0;
        StringBuffer s=new StringBuffer();
        for(int i=1;i<n+1;i++) {
            s.append(i);
        }
        String str=s.toString();
        for(int i=0;i<str.length();i++) {
            if(str.charAt(i)=='1') {
                count++;
            }
        }
        return count;
    }
    public static void main(String[]args) {
        ContainsOne solution=new ContainsOne();
        int num=solution.NumberOf1Between1AndN_Solution(13);
        System.out.println(num);
    }
}



4、{6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。給一個數組,返回它的最大連續子序列的和

package cn.ctgu.offer;
/*
 * 題目:
 * {6,-3,-2,7,-15,1,2,2},連續子向量的最大和為8(從第0個開始,到第3個為止)。
 * 給一個數組,返回它的最大連續子序列的和
 * 
 * 思路:
 * 使用動態規劃
 * 
 * */
public class ContinuousSubArray {
    public int FindGreatestSumOfSubArray(int[] array) {
         int sum=array[0];//記錄當前所有子陣列的和的最大值
         int max=array[0]; //包含array[i]的連續陣列最大值
         for(int i=1;i<array.length;i++) {
             max=Math.max(array[i], max+array[i]);
             sum=Math.max(max, sum);
         }
         return sum;
     }
    public static void main(String[]args) {
        ContinuousSubArray solution=new ContinuousSubArray();
        int[] number= {6,-3,-2,7,-15,1,2,2};
        int sum=solution.FindGreatestSumOfSubArray(number);
        System.out.println(sum);

    }
}


5、大家都知道斐波那契數列,現在要求輸入一個整數n,請你輸出斐波那契數列的第n項。n<=39

package cn.ctgu.offer;
/*
 * 大家都知道斐波那契數列,現在要求輸入一個整數n
 * 請你輸出斐波那契數列的第n項。n<=39
 * 
 * 
 * */
public class FibonacciSequence {
    public int fib(int n) {
        if(n==1||n==2) {
            return 1;
        }else {
            return fib(n-1)+fib(n-2);
        }
    }
    public static void main(String[]args) {
        FibonacciSequence fibonac=new FibonacciSequence();
        int i=fibonac.fib(28);
        System.out.println(i);
    }
}



6、小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和為100(至少包括兩個數)。沒多久,他就得到另一組連續正數和為100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和為S的連續正數序列?

package cn.ctgu.offer;

import java.util.ArrayList;
/*
 * 題目:
 * 小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。
 * 但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和為100(至少包括兩個數)。
 * 沒多久,他就得到另一組連續正數和為100的序列:18,19,20,21,22。
 * 現在把問題交給你,你能不能也很快的找出所有和為S的連續正數序列?
 * 
 * 思路:
 * 雙指標解決該問題
 * 
 * 
 * */
public class FindContinuousSeq {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        //存放結果
        ArrayList<ArrayList<Integer>>result=new ArrayList<ArrayList<Integer>>();
        //兩個起點,相當於動態視窗的兩邊,根據其視窗內的值的和來確定視窗的位置和大小
        int start=1;
        int end=2;
        while(start<end) {
            //由於是連續的,差為1的一個序列,那麼求和公式是(a0+an)*n/2
            int cur=(start+end)*(end-start+1)/2;
            //相等,那麼就將視窗範圍所有的數新增進結果集
            if(cur==sum) {
                ArrayList<Integer>list=new ArrayList<Integer>();
                for(int i=start;i<=end;i++) {
                    list.add(i);
                }
                result.add(list);
                start++;//end++  都可以,它主要是為了讓視窗右移
            }else if(cur<sum) {//如果視窗內的值之和小於sum,那麼右邊視窗右移一下
                end++;
            }else {
                //如果當前視窗內的值之和大於sum,那麼左邊視窗右移一下
                start++;
            }
        }
        return result;
    }
}


7、輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4。

package cn.ctgu.offer;

import java.util.ArrayList;
/*
 * 題目:
 * 輸入n個整數,找出其中最小的K個數。
 * 例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
 * 
 * 思路:
 * 1、通過快速排序演算法將陣列排好序存入陣列
 * 2、將最小的K個數遍歷出來
 * 
 * */
public class FindKMinNumber {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        quickSort(input,0,input.length-1);
        ArrayList<Integer>target=new ArrayList<Integer>();
        if(input.length==0||k<=0||k>input.length) {
            return target;
        }
        for(int i=0;i<k;i++) {
            target.add(input[i]);
        }
        return target;  
    }
    private static void quickSort(int[] array,int l,int r) {
        if(l<r) {
            int i=l;
            int j=r;
            int x=array[l];//基準數
            while(i<j) {
                //從右向左找第一個小於x的數
                while(i<j && array[j]>=x) {
                    j--;
                }
                if(i<j) {
                    array[i]=array[j];
                    i++;
                }

                //從左向右找第一個大於x的數
                while(i<j && array[i]<x) {
                    i++;
                }
                if(i<j) {
                    array[j]=array[i];
                    j--;
                }
                array[i]=x;//i和j相等的時候結束
                //遞迴呼叫
                quickSort(array,l,i-1);
                quickSort(array,i+1,r);
            }
        }
    }
    public static void main(String[]args) {
        FindKMinNumber solution=new FindKMinNumber();
        int[] number= {4,5,1,6,2,7,3,8};
        ArrayList<Integer>target=new ArrayList<Integer>();
        target=solution.GetLeastNumbers_Solution(number, 4);
        for(int i=0;i<target.size();i++) {
            System.out.println(target.get(i));
        }
    }
}


8、陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。如果不存在則輸出0。

package cn.ctgu.offer;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/*
 * 題目:
 * 陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。
 * 例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。
 * 由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。如果不存在則輸出0。
 * 
 * 
 * 思路:
 * 1、計算陣列長度
 * 2、統計每個數字出現的次數
 * 3、將每個數字出現的次數與陣列長度比較,如果大於等於陣列長度一半,則輸出該數,否則輸出0
 * */
public class FindNumber {
    public int MoreThanHalfNum_Solution(int [] array) {
        Map<Integer, Integer> number=new HashMap<Integer, Integer>();
        int target=0;
        for(int i=0;i<array.length;i++) {
            int key=array[i];
            int count=0;
            for(int j=0;j<array.length;j++) {
                if(key==array[j]) {
                    count=count+1;
                }
            }
            number.put(key, count);
        }
        Iterator<Map.Entry<Integer, Integer>> iter=number.entrySet().iterator();
        while(iter.hasNext()) {
            Map.Entry<Integer, Integer>entry=iter.next();
            if(entry.getValue()>(int)(array.length/2)) {
                target=entry.getKey();
            }
        }
        return target;
    }
    public static void main(String[]args) {
        FindNumber solution=new FindNumber();
        int[] number= {1,2,3,2,2,2,5,4,2};
        int target=solution.MoreThanHalfNum_Solution(number);
        System.out.println(target);
    }

}


9、輸入一個遞增排序的陣列和一個數字S,在陣列中查詢兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

package cn.ctgu.offer;

import java.util.ArrayList;
/*
 * 題目:
 * 輸入一個遞增排序的陣列和一個數字S,在陣列中查詢兩個數,
 * 使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。
 * 
 * 思路:
 *  1、
 * 假設:找到兩組滿足條件的陣列對(x,y)、(x+a,y-a),其中(x+y=S, 0<a<y-x)
 * x*y-[(x+a)(y-a)]
 *  =x*y-x*y-(y-x)a+a2
 *  =a[a-(y-x)]
 *  因為0<a<y-x,所以a-(y-x)<0,所以a[a-(y-x)]<0
 *  因此(x,y)乘積一定比(x+a,y-a)小
 *  
 *  所以兩頭的乘積一定比中間的小
 * 
 * 2、左右夾逼
 * 
 * 
 * */
public class FindNumbersAndSum {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer>list=new ArrayList<Integer>();
        if(array==null||array.length<2) {
            return list;
        }
        int i=0;
        int j=array.length-1;
        while(i<j) {
            if(array[i]+array[j]==sum) {
                list.add(array[i]);
                list.add(array[j]);
                return list;
            }else if(array[i]+array[j]>sum){
                j--;
            }else {
                i++;
            }
        }
        return list;
    }
    public static void main(String[]args) {
        FindNumbersAndSum solution=new FindNumbersAndSum();
        int[]num= {1,2,4,7,11,15};
        ArrayList<Integer>result=new ArrayList<Integer>();
        result=solution.FindNumbersWithSum(num, 15);
        System.out.println(result.get(0));
        System.out.println(result.get(1));
    }
}


10、一個整型數組裡除了兩個數字之外,其他的數字都出現了偶數次。請寫程式找出這兩個只出現一次的數字。

package cn.ctgu.offer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/*
 * 題目:
 * 一個整型數組裡除了兩個數字之外,其他的數字都出現了偶數次。
 * 請寫程式找出這兩個只出現一次的數字。
 * 
 * 思路:
 * 1、遍歷陣列,統計每個數字出現的次數
 * 2、將兩個只出現一次的數字分別儲存到num1和num2
 * 
 * */
public class FindTwoOneAppear {
     public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
         ArrayList<Integer> list=new ArrayList<Integer>();
         Map<Integer,Integer>map=new HashMap<Integer,Integer>();   
         for(int i=0;i<array.length;i++) {
                int data=array[i];
                int count=0;
                for(int j=0;j<array.length;j++) {
                    if(data==array[j]) {
                        count=count+1;
                    }
                }
                map.put(data, count);

            }
         Iterator<Map.Entry<Integer,Integer>>iter=map.entrySet().iterator();
         while(iter.hasNext()) {
             Map.Entry<Integer, Integer>entry=iter.next();
             if(entry.getValue()==1) {
                 list.add(entry.getKey());
             }
         }
         num1[0]=list.get(0);
         num2[0]=list.get(1);
      }
}



11、請實現一個函式用來找出字元流中第一個只出現一次的字元。例如,當從字元流中只讀出前兩個字元”go”時,第一個只出現一次的字元是”g”。當從該字元流中讀出前六個字元“google”時,第一個只出現一次的字元是”l”。

package cn.ctgu.offer;

import java.util.LinkedHashMap;
import java.util.Map;

/*
 * 題目:
 * 請實現一個函式用來找出字元流中第一個只出現一次的字元。
 * 例如,當從字元流中只讀出前兩個字元"go"時,第一個只出現一次的字元是"g"。
 * 當從該字元流中讀出前六個字元“google"時,第一個只出現一次的字元是"l"。
 * 
 * 思路:
 * 1、利用LinkedHashMap的有序性
 * 2、字元為key,出現的次數為value
 * 
 * */
public class FirstAppearOnce {
    Map<Character,Integer>map=new LinkedHashMap<Character,Integer>();
    //Insert one char from stringstream
    public void Insert(char ch)
    {
        if(map.containsKey(ch)) {
            map.put(ch,map.get(ch)+1);
        }else {
            map.put(ch, 1);
        }

    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(Map.Entry<Character, Integer>set:map.entrySet()) {
            if(set.getValue()==1) {
                return set.getKey();
            }
        }
        return '#';
    }
}



12、在一個字串(0<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置,如果沒有則返回 -1(需要區分大小寫).

package cn.ctgu.offer;
/*
 * 題目:
 * 在一個字串(0<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置
 * 如果沒有則返回 -1(需要區分大小寫).
 * 
 * 思路:
 * 1、將字串轉成字串陣列
 * 2、從前到後遍歷每個字元並統計出現的次數,若某個字元只出現一次則返回它的位置
 * 
 * 
 * */
public class FirstNotRepeat {
    public int FirstNotRepeatingChar(String str) {
        char[]strArray=str.toCharArray();
        int index=-1;
        for(int i=0;i<strArray.length;i++) {
            char s=strArray[i];
            int count=0;
            for(int j=0;j<strArray.length;j++) {
                if(s==strArray[j]) {
                    count=count+1;
                }
        }
        if(count==1) {
            index=i;
            return index;
        }

    }
        return index;
    }
    public static void main(String[]args) {
        FirstNotRepeat solution=new FirstNotRepeat();
        int index=solution.FirstNotRepeatingChar("abcdaaddcbvhhhj");
        System.out.println(index);
    }
}


13、我們可以用2*1的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

package cn.ctgu.offer;
/*
 * 我們可以用2*1的小矩形橫著或者豎著去覆蓋更大的矩形。
 * 請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?
 * 
 * 思路:
 * 1、每次填充都有兩種方法:橫著(兩個)、豎著一個
 * 2、由於高已經限制死了,所以橫著的時候兩個必須是同時出現且都橫著,橫著的寬度佔2
 * 3、豎著寬度佔1,高度恰好
 * 4、當target=n時,它始終是在n-1時上面拓展來的,分兩種情況拓展:一是在n-1的基礎上直接加一塊豎著的(1*2)即f(n)=f(n-1)+?
 * ?代表第二種新增方式,即當遇到1*2的時候它將會有兩種變形即|| =兩種,通過歸納可得出為f(n-2)
 * 
 *  
 * */
public class FixMartix {
    public int RectCover(int target) {

        //如果長度等於1則只有一種排法,即豎著排
        if(target==1) {

            return 1;
        }
        //如果長度等於2,則有兩種排法,兩個橫著或者兩個豎著
        if(target==2) {

            return 2;
        }
        else {
            return  RectCover(target-1)+RectCover(target-2);
        }

    }
    public static void main(String[]args) {
        FixMartix fix=new FixMartix();
        System.out.println(fix.RectCover(5));
    }
}


14、如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取資料流,使用GetMedian()方法獲取當前讀取資料的中位數。

package cn.ctgu.offer;

import java.util.ArrayList;
import java.util.Collections;

/*
 * 題目:
 * 如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值
 * 那麼中位數就是所有數值排序之後位於中間的數值。
 * 如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。
 * 我們使用Insert()方法讀取資料流,使用GetMedian()方法獲取當前讀取資料的中位數。
 * 
 * 思路:
 * 1、將所有的數新增到一個列表中
 * 2、將列表進行排序
 * 3、如果列表長度是奇數則直接用取中間值,如果是偶數,則取中間那個數和前面那個數的平均值
 * 
 * */
public class GetMedium {
    ArrayList<Integer>list=new ArrayList<Integer>();
    public void Insert(Integer num) {
        list.add(num);
        Collections.sort(list);
    }

    public Double GetMedian() {
        int mid=list.size()/2;
        if((list.size()%2)==1) {
            return list.get(mid)/1.0;
        }else {
            return (list.get(mid-1)+list.get(mid))/2.0;
        }
    }
}


15、統計一個數字在排序陣列中出現的次數。

package cn.ctgu.offer;
/*
 * 題目:
 * 統計一個數字在排序陣列中出現的次數。
 * 
 * 思路:
 * 由於陣列有序,所以可以使用二分查詢方法定位K第一次出現的位置和最後一次出現的位置
 * 
 * 
 * */
public class GetNumberK {
    public int GetNumberOfK(int [] array , int k) {
           int first=getLower(array,k);
           int second=getUpper(array,k);
           return second-first+1;
    }
    //獲取k第一次出現的下標
    int getLower(int[] array,int k) {
        int start=0;
        int end=array.length-1;
        int mid=(start+end)/2;
        while(start<=end) {
            if(array[mid]<k) {
                start=mid+1;
            }else {
                end=mid-1;
            }
            mid=(start+end)/2;
        }
        return start;
    }
    //獲取k最後一次出現的下標
    int getUpper(int[] array,int k) {
        int start=0;
        int end=array.length-1;
        int mid=(start+end)/2;
        while(start<=end) {
            if(array[mid]<=k) {
                start=mid+1;
            }else {
                end=mid-1;
            }
            mid=(start+end)/2;
        }
        return end;
    }

}