剑指offer面试题(41-50)——java实现

面试题41:和为s的两个数字&和为s的连续整数序列

1.输入一个递增的数组和一个数字s,在数组中找到和为s的两个数字
  如果有多对数字的和,输出任意一对即可
public class TwoNumberSumIsS {
    public static void getTwoNumberSumIsS(int[] array, int s){
        if (array == null || array.length < 2) return;
        int low = 0, high = array.length - 1;
        while (low < high){
            int pre = array[low], after = array[high];
            if (pre + after == s){
                System.out.println(pre + "/" + after);
                return;
            }else if (pre + after < s) {
                low++;
            }else {
                high--;
            }
        }
        System.out.println("不存在和为s的");
    }

    public static void main(String[] args) {
        int[] array = {1,2,4,7,11,15};
        getTwoNumberSumIsS(array, 26);
    }
}

2.和为s的连续整数序列
public class SumIsS {
    public static void getSumIsS(int s){
        if (s < 3) return;
        int small = 1, big = 2, middle = (s + 1) >> 1, sum = 3;
        while (small < middle){
            if (sum == s){
                printResult(small, big);
            }
            while (sum > s && small < middle){
                sum -= small;
                small++;
                if (sum == s) printResult(small, big);
            }
            big++;
            sum += big;
        }
    }

    private static void printResult(int small, int big) {
        for (int i = small; i <= big; i++){
            System.out.printf("%d ", i);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        getSumIsS(100);
    }
}

面试题42:翻转单词顺序&左旋转字符串

1.输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变,为简单起见,标点符号和普通字符一样处理。

思路:首先翻转整个句子,然后再翻转每个单词的次序

public class Reverse {
    /**
     * 翻转字符串
     * @param str
     * @return
     */
    public static StringBuilder reverse(StringBuilder str){
        if (str == null || str.length() == 0) return null;
        int len = str.length();
        for (int i = 0, j = len - 1; i < j; i++, j--){
            char tempI = str.charAt(i);
            char tempJ = str.charAt(j);
            str.setCharAt(i , tempJ);
            str.setCharAt(j, tempI);
        }
        return str;
    }

    public static String reverse(String str){
        if (str == null || str.length() == 0) return null;
        StringBuilder temp = new StringBuilder(str);
        temp = reverse(temp);
//        记录要翻转的单词
        StringBuilder word = new StringBuilder();
//        i是单词的起始位置,j是结束为止
        for (int i = 0, j = 0; i < str.length() && j < str.length();){
            if (' ' == temp.charAt(j)){
                temp.replace(i, j, reverse(word).toString());
                i = j + 1;
                j++;
                word.delete(0,word.length());
            }else {
                word.append(temp.charAt(j));
                j++;
            }
        }
        return temp.toString();
    }

    public static void main(String[] args) {

        System.out.printf(reverse("i love you"));
    }
}

2.字符串的左旋操作就是把字符串前面的若干字符转移到字符串的尾部。

思路:前面若干字符pre取出,后面若干字符after取出,返回after+left
(其实是翻转前半部分字符,再翻转后半部分字符,然后翻转整个字符)

public class LeftRotateString {
    public static String getLeftRotateString(String str, int number){
        if (str == null || str.length() == 0) return null;
        StringBuilder temp = new StringBuilder(str);
        StringBuilder word = new StringBuilder();
        for (int i = 0; i < number; i++){
            word.append(temp.charAt(i));
        }
        StringBuilder after = new StringBuilder();
        for (int i = number; i < str.length(); i++){
            after.append(temp.charAt(i));
        }
        after.append(word);
        return after.toString();
    }

    public static void main(String[] args) {
        System.out.print(getLeftRotateString("abcdef", 2));
    }
}

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

 把n个骰子扔在地上,所有骰子朝上的一面的点数之和为s
 输入n,打印出s的所有可能的值出现的概率。

思路:用数组存放每种骰子点数和出现的次数。令数组中下标为n的元素存放点数和为n的次数。我们设置循环,每个循环多投掷一个骰子,假设某一轮循环中,我们已知了各种点数和出现的次数;在下一轮循环时,我们新投掷了一个骰子,那么此时点数和为n的情况出现的次数就等于上一轮点数和为n-1,n-2,n-3,n-4,n-5,n-6的情况出现次数的总和。从第一个骰子开始,循环n次,就可以求得第n个骰子时各种点数和出现的次数。

我们这里用两个数组来分别存放本轮循环与下一轮循环的各种点数和出现的次数,不断交替使用。

public class NTouZiDeDianShu {
    /**
     *
     * @param n 骰子个数
     * @param count 骰子的最大点数
     */
    public static void getPropotity(int n, int count){
        if (n < 1) return;
        int[][] arrays = new int[2][n * count + 1];
        //[2]代表用两个数组交替保存,[number*maxValue+1]是指点数为所在下标时,该点数出现的总次数。
        //arrays[*][0]是没用的,只是为了让下标对应点数
        for (int i = 0; i < n * count + 1; i++){
            arrays[0][i] = 0;
            arrays[1][i] = 0;
        }
        int flag = 0;
        for (int i = 1; i <= count; i++){
            arrays[flag][i] = 1;//第一个骰子出现的情况
        }
        for (int k = 2; k <= n; k++){//当前是第几个骰子
            for (int i = 0; i <= k; i++){
                arrays[1- flag][i] = 0;//前面的数据清零
            }
            for (int i = k; i <= count * k; i++){
                for (int j = 1; j <= i && j <= count; j++){
                    arrays[1 - flag][i] += arrays[flag][i - j];
                }
            }
            flag = 1 - flag;
        }
        double total = Math.pow((double)count, n);
        for (int i = n; i <= count * n; i++){
            double ratio = (double)arrays[flag][i] / total;
            System.out.println(i + ":" + String.format("%.5f", ratio));
        }
    }

    public static void main(String[] args) {
        getPropotity(2,6);
    }
}

面试题44:扑克牌的顺子

抽取5张牌,判断是不是一个顺子。
2-10为数字本身,A为1,J为11,Q为12,K为13,大小王可堪称任意数字。
import java.util.Arrays;

public class Straight {
    public static boolean isStraight(int[] cards){
        if (cards == null || cards.length < 5) return false;
        boolean flag = true;
        Arrays.sort(cards);
        int zeroCount = 0, skipCount = 0;
        for (int i = 0; i < cards.length; i++){
            if (cards[i] == 0){//0表示大小王
                zeroCount++;
            }
        }
        int small = zeroCount, big = small + 1;//统计间隔数
        while (big < cards.length){
            if (cards[small] == cards[big]){
                flag = false;
                break;
            }
            skipCount += cards[big] - cards[small] - 1;
            small = big;
            big++;
        }
        if (skipCount > zeroCount) flag = false;
        return flag;
    }

    public static void main(String[] args) {
        int[] cards = {2,0,4,3,0};
        System.out.println(isStraight(cards));
    }
}

面试题45:圆圈中剩下的最后一个数字

0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字
。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,
则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

思路1:使用环形链表

    /**
     * 使用环形链表
     * @param circle 数据
     * @param m 第m个数
     * @return
     */
    public static int getTheLastNumberInCircle(int[] circle, int m){
        if (circle == null || circle.length == 0) return -1;
        List<Integer> loop = new ArrayList<Integer>();
        for (int i = 0; i < circle.length; i++){
            loop.add(circle[i]);
        }
        int n = circle.length;
        int i = m % n - 1;
        if (i == -1) i = loop.size() - 1;
        loop.remove(i);
        while (loop.size() != 1){
            int count = 0;
            if (count < m) {
                i++;
                i = i % loop.size();
            }else break;
            loop.remove(i);
        }
        int res = loop.get(0);
        return res;
    }

思路2:使用数学公式

    /**
     * @param circle 数据
     * @param m 第m个数
     * @return
     */
    public static int getTheLastNumberInCircle(int[] circle, int m){
        if (circle == null || circle.length == 0) return -1;
        int n = circle.length;
        if (n < 1 || m < 1) return -1;
        int last = 0;
        for (int i = 2; i <= n; i++) {
            last = (last + m) % i;
        }
        return circle[last];
    }

面试题46:求1+2+3+…+n

要求不能使用乘除法,for、while、if、else、switch、case以及条件判断语句

思路:使用递归

public class PlusWithoutAnything {
    public static int getSum(int n) {
        int sum = n;
        boolean ans = (n > 0) && ((sum += getSum(n - 1)) > 0);
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(getSum(100));
    }
}

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

public class SumOfTwoNumberWithoutAnything {

    public static int getSum(int a, int b){
        int sum, carry;
        do{
            sum = a ^ b;
            carry = (a & b) << 1;
            a = sum;
            b = carry;
        }while ( (b != 0));
        return sum;
    }

    public static void main(String[] args) {
        System.out.println(getSum(1,2));
    }
}

面试题48:不能继承的类

//使用final关键字

面试题49:把字符串转换成数字

要考虑的东西很多,如:
1.字符串为空时
2.有符号+、-时
3.非法输入时
4.数字越界时
public class String2Number {

    public static int getNumber(String number){
        if (number == null || number.length() == 0) {
            System.out.println("字符串为空!");
            return 0;
        }
        boolean minus = false;//是否是负数
        int index = 0;//字符串开始的下标
        if (number.charAt(0) == '-') {
            index++;
            minus = true;
        }
        else if (number.charAt(0) == '+'){
            index++;
        }
        return getNumber(number, minus, index);
    }

    private static int getNumber(String number, boolean minus, int index) {
        int sign = 1;
        long result = 0;
        if (minus) sign = -1;
        for (int i = index; i < number.length(); i++){
            if (number.charAt(i) >= '0' && number.charAt(i) <= '9'){
                result = 10 * result + Integer.parseInt(String.valueOf(number.charAt(i)));
                if ((!minus && result > Integer.MAX_VALUE) || (!minus && result < Integer.MIN_VALUE)){
                    System.out.println("越界!");
                    return 0;
                }
            }else {
                System.out.println("非法输入!");
                return 0;
            }
        }
        return (int) (sign * result);
    }

    public static void main(String[] args) {
        System.out.println(getNumber("1230"));
    }
}

面试题50:树中两个节点的最低公共祖先

1.如果是二叉搜索树;

思路:最低公共祖先一定是小于一个数且大于另一个数的。如果同时小于,则在右子树中,如果同时大于则在左子树中

public class LowestCommonNode {
    public static Node getLowestCommonNode(Node tree, Node node1, Node node2){
        if (tree == null) return null;
        if (tree.value > node1.value && tree.value < node2.value) return tree;
        else if (tree.value < node1.value && tree.value < node2.value) return getLowestCommonNode(tree.right, node1, node2);
        else return getLowestCommonNode(tree.left, node1, node2);
    }

    public static void main(String[] args) {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        node2.left = node1;
        node2.right = node4;
        node4.left = node3;
        node4.right = node5;
        Node result = getLowestCommonNode(node2, node3, node5);
        if (result != null){
            System.out.println(result.value);
        }else {
            System.out.println("没找到!");
        }

    }
}
2.如果是普通的树但是有指向父节点的指针;

思路:跟着父指针构造两条链表,寻找两条链表的第一个公共节点

面试题37:两个链表的第一个公共节点

3.如果是普通的树且没有指向父节点的指针;

思路:使用辅助空间,来保存到达两个节点的链表,然后找到两个链表的公共节点

import java.util.LinkedList;
class TreeNode1{
    TreeNode1 node;
    Object value;
    TreeNode1[] childs;

    @Override
    public String toString() {
        return "value=" + value;
    }
}
public class LowestCommonNode2 {
        //使用两个链表来保存根节点到所求节点的路径
        static LinkedList list1 = new LinkedList();
        static LinkedList list2 = new LinkedList();

        public static void main(String args[]) {
            TreeNode1 A = new TreeNode1();
            A.value = 'A';
            TreeNode1 B = new TreeNode1();
            B.value = 'B';
            TreeNode1 C = new TreeNode1();
            C.value = 'C';
            TreeNode1 D = new TreeNode1();
            D.value = 'D';
            TreeNode1 E = new TreeNode1();
            E.value = 'E';
            TreeNode1 F = new TreeNode1();
            F.value = 'F';
            TreeNode1 G = new TreeNode1();
            G.value = 'G';
            TreeNode1 H = new TreeNode1();
            H.value = 'H';
            TreeNode1 I = new TreeNode1();
            I.value = 'I';
            TreeNode1 J = new TreeNode1();
            J.value = 'J';
            A.childs = new TreeNode1[] {B,C};
            B.childs = new TreeNode1[] {D,E};
            D.childs = new TreeNode1[] {F,G};
            E.childs = new TreeNode1[] {H,I,J};
        /*
                A
               / \
              B   C
             /  \
             D    E
           / \   / | \
          F   G  H I  J
         */

            //找F,H节点的LCA
            TreeNode1 lca = findLCA(F,H,A);
            System.out.println(lca);
        }
        
        private static TreeNode1 findLCA(TreeNode1 node1, TreeNode1 node2, TreeNode1 root) {
            getPathFromRootToNode(node1,root,list1);
            getPathFromRootToNode(node2,root,list2);
            //list1 : D -- B -- A
            //list2 : E -- B -- A

            //接下来遍历两个链表找到最近的公共节点
            int index = 0;
            int length1 = list1.size();
            int length2 = list2.size();
            int sub = length1 > length2 ? length1-length2 : length2-length1;
            if(length2 > length1) {
                LinkedList temp = list1;
                list1 = list2;
                list2 = temp;
            }
            while(index != length2 - 1) {
                if(((TreeNode1)list1.get(index + sub)).value == ((TreeNode1)list2.get(index)).value) {
                    return (TreeNode1)list2.get(index);
                }else {
                    index++;
                }
            }
            return null;
        }


        private static boolean getPathFromRootToNode(TreeNode1 node, TreeNode1 currentRoot, LinkedList list) {
            //找到就直接返回true
            if(node.value == currentRoot.value) {
                return true;
            }
            //找不到就将当前节点加入路径,push是在链表的头插入的,offer是尾部
            list.push(currentRoot);
            boolean found = false;
            TreeNode1[] childs = currentRoot.childs;
            if (childs != null && childs.length > 0) {
                //遍历当前节点的所有子节点,在子节点里边找
                for (int i = 0; i < childs.length; i++) {
                    if (found) {
                        break;
                    } else {
                        found = getPathFromRootToNode(node, childs[i], list);
                    }
                }
            }
            //找不到就将当前节点从路径中删除,因为是递归,当递归回来到这里的时候,当前节点一定是list的最后一个节点,即栈顶
            if(!found) {
                list.pop();
            }
            return found;
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值