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

}
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值