劍指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;
}
}