JAVA实现 剑指offer第二版 5

面试题53_1:在排序数组中查找数字

因数组有序,可以考虑二分查找,但二分查找后还需顺序查找第一个k和最后一个k,可以在二分查找的过程中对是否为第一个或最后一个k进行判断。最终的复杂度控制在o(logn);
public static int getApperanceNumber(int[] nums,int k){
        if (nums == null || nums.length == 0){
            return 0;
        }
        if (nums.length == 1){
            if (k == nums[0]){
                return 1;
            }else {
                return 0;
            }
        }
        int firstindex = getFirstIndex(nums,k);
        int lastindex = getLastIndex(nums,k);


        if (firstindex != -1 && lastindex!=-1){
            int reback = lastindex-firstindex+1;
            if (reback>= 0){
                return reback;
            }else {
                return 0;
            }
        }
        return 0;
    }
    public static int getFirstIndex(int[] nums,int k){
        int low = 0;
        int high = nums.length -1;
        while (low <= high){
            int mid = (low+high)/2;
            if (k == nums[mid]){
                for (int x = mid;x>=0;x--){
                    if (nums[x] != k){
                        return x+1;
                    }
                    if (x == 0){
                        return 0;
                    }
                }
            }
            if (k < nums[mid]){
                high = mid-1;
            }
            if (k > nums[mid]){
                low = mid+1;
            }
        }
        return -1;
    }
    public static int getLastIndex(int[] nums,int k){
        int low = 0;
        int high = nums.length -1;
        while (low <= high){
            int mid = (low+high)/2;
            if (k == nums[mid]){
                for (int x = mid;x<=nums.length-1;x++){
                    if (nums[x] != k){
                        return x-1;
                    }
                    if (x == nums.length-1){
                        return x;
                    }
                }
            }
            if (k < nums[mid]){
                high = mid-1;
            }
            if (k > nums[mid]){
                low = mid+1;
            }
        }
        return -1;
    }

53_2:0-(n-1)中的数字

求出0-(n-1)原本的和,再求数组的和,相减过后得到差得那个数,o(n)但没有利用数组按顺序排列的特点
缺失的那个数下标与原数不同,且之前相同,之后不同;利用二分来找到第一个下标不同的数,o(logn)
public static int getMissingNum(int[] nums){
        if (nums == null || nums.length  == 0){
            return -1;
        }
        int low = 0;
        int high = nums.length-1;
        while (low <= high){
            int mid = (low+high)/2;
            if (nums[mid] != mid){
                if (mid == 0){
                    return mid;
                }
                if (nums[mid-1] != mid-1){
                    high = mid-1;
                }else {
                    return mid;
                }
            }else {
                low = mid +1;
            }
        }
        return nums.length;
    }

面试题53_3:数组中数值和下标相等的元素

数组有序,则可以用二分查找来优化。o(logn)
public static Integer getIndexSameNum(int[] nums){
        if (nums == null || nums.length == 0){
            return null;
        }
        if (nums.length == 1){
            if (nums[0] == 0){
                return 0;
            }else {
                return null;
            }
        }

        int low = 0;
        int high = nums.length-1;
        while (low <= high){
            int mid = (low+high)/2;
            if (nums[mid] == mid){
                return mid;
            }else if (nums[mid] > mid){
                high = mid-1;
            }else {
                low = mid+1;
            }
        }
        return null;
    }

面试题54:二叉搜索树的第k大节点

中序遍历
//递归中传递参数:当返回类型,函数参数都不好用时,直接设置一个大小为1的数组进行值得传递;     
public static Node getKmaxNum(Node root,int k){
        if (root == null || k <= 0){
            return null;
        }
        int[] temp = {k};
        return getKmaxNum_core(root,temp);
    }
    public static Node getKmaxNum_core(Node root,int[] temp){
        Node reback = null;
        if (root.left != null){
            reback = getKmaxNum_core(root.left,temp);
        }
        if (reback == null){
            if (temp[0] == 1){
                reback = root;
            }
            temp[0]--;

        }
        if (reback == null && root.right!= null){
            reback = getKmaxNum_core(root.right,temp);
        }
        return reback;

    }

面试题55_1:二叉树的深度

二叉树的深度可以依靠左右子树的深度递归求解

    public static int getTreeDeep(Node root){
        if (root == null){
            return 0;
        }
        int left  = getTreeDeep(root.left);
        int right = getTreeDeep(root.right);

        return left > right ? left+1:right+1;
    }

面试题55_2:平衡二叉树

求出左右子树的深度,判断,再递归判断左右子树是否为平衡的,存在重复判断的情况
后续遍历,先检查左右子树,并记录深度,从叶子节点开始检查
    public static boolean judgeBlanceTree(Node root){
        if (root == null) {
            return false;
        }
        judgeBlanceTree_core(root);
        return isBlanced;
    }
    public static int judgeBlanceTree_core(Node root){
        if (root == null){
            isBlanced = true;
            return 0;
        }
        int left = judgeBlanceTree_core(root.left);
        int right = judgeBlanceTree_core(root.right);
        int depth =( left > right?left:right)+1;
        if (Math.abs(left-right)<=1){
            isBlanced = true;
        }else {
            isBlanced = false;
        }
        return depth;
    }

面试题56_1:数组中数字出现的次数

任何一个数字异或它自己 = 0;遍历数组,若只有一个数出现一次,通过异或的结果可以得到此数

存在两个只出现一次的数时需要将数组分为两个,使每个只包含一个;

分数组的方式为:遍历异或数组后的值为两个孤儿的异或值,因不相同,则至少有1位为1,取最低的1位,在此位两个数相反,故按此位是否为0,将原数组划分为2组,再对两组分别求孤儿;

//无法判断数组中是否有重复的,或有多于2个的重复;如果需要判断此步,在第一次遍历过程中需要用hashmap将出现次数存起来。但也就得到出现次数为1的了   
public static int[] notDuplicateNums(int[] nums){
        int[] reback = new int[2];
        if (nums == null || nums.length <= 1){
            return reback;
        }
        if (nums.length == 2){
            if (nums[0] == nums[1]){
                return reback;
            }else {
                reback[0] = nums[0];
                reback[1] = nums[1];
                return reback;
            }
        }
        int exclusive = countYH(nums);
        int firstdigt = getFirstDigt(exclusive);

        for (int x :nums){
            if (isBit1(x,firstdigt) == 1){
                reback[0] = reback[0]^x;
            }else {
                reback[1] = reback[1]^x;
            }
        }
        return reback;
    }
    public static int countYH(int[] nums){
        int exclusive = 0;
        for (int x : nums){
            exclusive = exclusive^x;
        }
        return exclusive;
    }
    public static int getFirstDigt(int num){
        int indexBit = 0;
        while (((num&1) == 0 )&& (indexBit < 32)){
            num = num>>1;
            indexBit++;
        }
        return indexBit;
    }
    public static int isBit1(int num,int firstdigt){
        num = num>>firstdigt;
        return num&1;
    }

面试题56_2:数组中唯一只出现一次的数字

hashmap存储出现次数
位运算:每位的和加起来应是3的倍数,若不是,则只出现1次的数此位为1;正负无影响
public static int getNoDuplicated(int[] nums){
        if (nums == null || nums.length == 0){
            error = true;
        }
        int[] bitsum = new int[32];
        for (int x = 0; x<=nums.length-1;x++){
            int bitmash = 1;
            for (int j = 31;j>=0;j--){
                int bit = nums[x] & bitmash;
                if (bit != 0){
                    bitsum[j] += 1;
                }
                bitmash = bitmash<<1;
            }
        }
        int result = 0;
        for (int x =0 ;x<32;x++){
            result = result<<1;
            result += bitsum[x]%3;
        }
        return result;
    }

面试题57_1:和为s的数字

从头开始遍历数组,将当前数与剩下的数进行比较,寻找和相等的 o(n[2])
由于数组有序,设置两个指针,一个指向第一个元素,一个指向最后一个元素,根据和调整这两个指针 o(n)
public static int[] judgeSum(int[] nums,int sum){
        if (nums == null || nums.length<2 ){
            return null;
        }
        int[] reback = new int[2];
        int start = 0;
        int end = nums.length - 1;
        while (start < end){
            int temp = nums[start]+nums[end];
            if (temp == sum){
                reback[0] = nums[start];
                reback[1] = nums[end];
                return reback;
            }else if (temp < sum){
                start++;
            }else {
                end -- ;
            }
        }
        return null;
    }

面试题57_2:和为s的连续正数序列

由于是等差数列,可以化简为找到头尾两个数,第一个指针指向第一个元素,第二个指针指向第二个元素,因为必须含两个元素,故直到较小元素到达(1+s)/2为止
    public static void FindContinusSequence(int sum){
        if (sum <= 0){
            return;
        }
        int start = 1;
        int end = 2;
        while (start < (1+sum)/2){
            float temp = ((float)(start + end))*(end-start+1)/2;
            if (temp == sum){
                for (int x = start;x<=end;x++){
                    System.out.print(x);
                }
                System.out.print("\n");
                end++;
                start++;
            }else if (temp < sum){
                end++;
            }else {
                start++;
            }
        }
        return;
    }

面试题58_1:翻转字符串

先将字符串全部翻转,再依据空格将每个单词翻转回来。也可以使用java中的split函数来先进行分割,再在词的基础上进行翻转。额外需要用空间;
    public static String ReverseString(String st){
        if (st == null || st.length() == 0){
            return "";
        }
        if (st.length() == 1){
            return st;
        }
        String[] st_li = st.split(" ");
        int start =  0;
        int end = st_li.length-1;
        StringBuilder stb = new StringBuilder();
        while (start < end) {
            stb.append(st_li[end]);
            st_li[end] = st_li[start];
            st_li[start] = stb.substring(0,stb.length());
            stb.delete(0,stb.length());
            start++;
            end--;
        }
        return String.join(" ",st_li);
    }

面试题58_2:左旋转字符串

StringBuilder的思路更简洁
public static String LeftRotate(String st ,int index){
        if (st == null||st.length() == 0||index>st.length()-1||index<0){
            return "";
        }
        if (index == 0 || index == st.length()){
            return st;
        }
        StringBuilder stb = new StringBuilder();
        stb.append(st);
        stb.append(stb.subSequence(0,index));
        stb.delete(0,index);
        return stb.substring(0,stb.length());

    }

面试题59_1:滑动窗口的最大值

顺序遍历及比较 o(nk)
用双端开口的队列进行辅助 o(n)
    public static ArrayList<Integer> MaxOfWindow(int[] nums ,int k){
        if (nums == null || nums.length < k || k<=0 ){
            return null;
        }
        ArrayDeque<Integer> arrayDeque = new ArrayDeque<>();
        ArrayList<Integer> reback = new ArrayList<>();
        for (int x = 0;x<k;x++){
            while (!arrayDeque.isEmpty() && nums[x] >= nums[arrayDeque.peekFirst()]){
                arrayDeque.pollFirst();
            }
            arrayDeque.addFirst(x);
        }
        for (int x=k;x<nums.length;x++){
            reback.add(nums[arrayDeque.peekLast()]);
            while (!arrayDeque.isEmpty() && nums[x]>=nums[arrayDeque.peekFirst()]){
                arrayDeque.pollFirst();
            }
            if (!arrayDeque.isEmpty() && arrayDeque.peekLast() <= (x-k)){
                arrayDeque.pollLast();
            }
            arrayDeque.addFirst(x);
        }
        reback.add(nums[arrayDeque.peekLast()]);
        return reback;
    }

59_2:队列的最大值

相当于将整个队列设置为滑动窗口
class Deque_max{
    class Data{
        int value;
        int index;
        Data(int value,int index){
            this.value = value;
            this.index = index;
        }
    }
    ArrayDeque<Data> arrayDeque_data = new ArrayDeque<>();
    ArrayDeque<Data> arrayDeque_max = new ArrayDeque<>();
    public static int currentIndex;

    public void push_back(int num){
        while (!arrayDeque_max.isEmpty() && num >= arrayDeque_max.peekLast().value){
            arrayDeque_max.pollLast();
        }
        Data newdata = new Data(num,currentIndex);
        arrayDeque_data.addLast(newdata);
        arrayDeque_max.addLast(newdata);
        currentIndex++;
    }
    public int pop_front() throws Exception{
        if (arrayDeque_data.isEmpty()){
            throw new Exception("no numbers");
        }
        if (arrayDeque_data.peekFirst().index == arrayDeque_max.peekFirst().index){
            arrayDeque_max.pollFirst();
        }
        return arrayDeque_data.pollFirst().value;
    }
    public int max() throws Exception{
        if (arrayDeque_max.isEmpty()){
            throw new Exception("no max");
        }
        return arrayDeque_max.peekFirst().value;
    }
}

面试题60:n个骰子的点数

求出n个骰子的点数和,用递归进行求解
用两个数组存储,第一个数组存储和为n出现的次数,第二个数组n为前一个数组n-1,n-2,n-3到n-6之和
    public static void Prosibility(int numOfGm){
        if (numOfGm<1){
            return;
        }
        int maxSum = 6*numOfGm;
        int[] prosibilitys = new int[maxSum-numOfGm+1];
        Probability(numOfGm,prosibilitys);
        int total = (int)Math.pow(6,numOfGm);
        for (int i = numOfGm;i<= maxSum;i++){
            System.out.println(prosibilitys[i-numOfGm]/(float)(total));
        }
    }
    public static void Probability(int numOfGm,int[] prosibilitys){
        for (int x = 1;x<=6;x++){
            Prosibility_core(numOfGm,numOfGm,x,prosibilitys);
        }
    }
    public static void Prosibility_core(int original,int current,int sum,int[] prosibilitys){
        if (current == 1){
            prosibilitys[sum-original]++;
        }else {
            for (int i = 1;i<=6;i++){
                Prosibility_core(original,current-1,i+sum,prosibilitys);
            }
        }
    }
    public static void Prosibility(int numOfGm){
        if (numOfGm<1){
            return;
        }
        int[][] pProbabilities = new int[2][];
        pProbabilities[0] = new int[6*numOfGm+1];
        pProbabilities[1] = new int[6*numOfGm+1];

        int flag = 0;
        for (int i = 1;i<=6;i++){
            pProbabilities[flag][i] = 1;
        }
        for (int k = 2;k<=numOfGm;k++){
            for (int x = 0;x<k;x++){
                pProbabilities[1-flag][x] = 0;
            }
            for (int x = k;x<=6*k;x++){
                pProbabilities[1-flag][x] = 0;
                for (int j = 1;j<=x &&j<=6;j++){
                    pProbabilities[1-flag][x] += pProbabilities[flag][x-j];
                }
            }
            flag = 1-flag;
        }
        int total = (int)Math.pow(6,numOfGm);
        for (int x = numOfGm;x<=6*numOfGm;x++){
            System.out.println(pProbabilities[flag][x]/(float)(total));
        }
    }

面试题61:扑克牌中的顺子

将扑克看做数字,大小王视作0 。需要将数组排序,统计数组中0的个数,判断空缺数,若空缺数<=0的个数也是有序的。非0数字连续出现则数组无序。
    public static boolean isContinues(int[] nums){
        if (nums == null || nums.length != 5){
            return false;
        }
        Arrays.sort(nums);
        int numOfzero = 0;
        for (int x:nums){
            if (x == 0){
                numOfzero++;
            }
        }
        if (numOfzero>2){
            return false;
        }

        int gap = 0;
        for (int x = 0;x<nums.length-1;x++){
            if ( nums[x] != 0){
                if (nums[x+1] == nums[x]){
                    return false;
                }else {
                    gap += nums[x+1]-nums[x]-1;
                }
            }
        }
        return numOfzero >= gap ? true:false;
    }

面试题62:圆圈中最后剩下的数字(约瑟夫环问题)

用头尾相连的链表来模拟约瑟夫环,每次删除第m个
数学推导得到递推公式:f(n,m) = (f(n-1,m)+m)%n
class Circle {
    class Node{
        int value;
        Node next;
        Node(int value){
            this.value = value;
        }
    }
    Node head;
    Node tail;
    public void addNode(int value){
        Node now = new Node(value);
        if (head == null){
            head = now;
            tail = now;
        }else {
            tail.next = now;
            tail = tail.next;
            tail.next = head;
        }
    }
    public Node delNode(int index,Node start){
        Node temp = start;
        for (int x = 1;x<index-1;x++){
            temp = temp.next;
        }
        int reback = temp.next.value;
        temp.next = temp.next.next;
        return temp.next;
    }
    public int getRemain(int[] nums,int index) throws Exception{
        if (nums == null || nums.length == 0 || index<=0){
            throw new Exception("error");
        }
        for (int x:nums){
            addNode(x);
        }
        Node temp = head;
        while (temp != temp.next){
            temp = delNode(index,temp);
        }
        return temp.value;
    }
}

面试题63:股票的最大利润

直接求出所有数对,取差值最大的一个 o(n[2])
扫描数字,记录前面出现过的最小值及差值的最大值 o(n)
public static int MaxD(int[] nums) throws Exception{
        if (nums == null || nums.length <= 1){
            throw new Exception("error");
        }
        int minNum = nums[0];
        int MaxD = 0;
        for (int x = 1;x<nums.length;x++){
            if (nums[x] - minNum > MaxD){
                MaxD = nums[x]-minNum;
            }
            if (nums[x]<minNum){
                minNum = nums[x];
            }
        }
        return MaxD;

    }

面试题64:求1-n的和

利用&&的短路效果结束递归
利用反射的技术
public static int getSum(int num){
        int sum  = num;
        boolean ans = (num>0) && ((sum += getSum(num-1))>0);
        return sum;
    }
class count{
    public int terminator(int n){
        return 0;
    }
    public int sum(int num) throws IllegalAccessException,InvocationTargetException {
        List<Boolean> li = new ArrayList<>();
        li.add(false);
        li.add(true);
        Method methods[] = this.getClass().getMethods();
        int index = li.indexOf( num == 0);
        return num+(int)methods[index].invoke(this,(--num));
    }
}

面试题65_1:不用加减乘除做加法

用位运算代替:异或,求进位,再异或直至不产生进位
    public static int Sum(int num1,int num2){
        int sum = num1 ^ num2;
        int carry = (num1 & num2)<<1;
        while (carry != 0){
            int temp = sum;
            sum = sum ^ carry;
            carry = (temp & carry)<<1;

        }
        return sum;
    }

面试题65_2:不使用心得变量,交换两个变量的值

 
 

//基于加法

public static void Turn(int num1,int num2){

num1 = num1+num2; num2 = num1-num2; num1 = num1 - num2; System.out.println(num1); System.out.println(num2); }
public static void Turn(int num1,int num2){
        num1 = num1^num2;
        num2 = num1^num2;
        num1 = num1^num2;
        System.out.println(num1);
        System.out.println(num2);
    }

面试题66:构建乘积数组

直接遍历相乘 o(n[2])
下一个要求的元素的两部分与上一个有关联,故可以在前一个的基础上求得下一个 o(n)
    public static int[] getSecondArray(int[] nums,int[] target){
        if (nums == null||target == null||nums.length != target.length || nums.length == 0){
            return null;
        }
        //每次计算出的要进入数组。
        target[0] = 1;
        for (int x = 1;x<nums.length;x++){
            target[x] = target[x-1]*nums[x-1];
        }
        int temp = 1;
        for (int x = nums.length-2;x>=0;x--){
            temp *= nums[x+1];
            target[x] *= temp;
        }
        return target;
    }





猜你喜欢

转载自blog.csdn.net/u011010851/article/details/80291120
今日推荐