Java实现 剑指offer(第二版)1

题目2:实现Singleton模式

构造函数设为私有函数;饿汉式;懒汉式+同步锁;静态内部类;
//饿汉式
class Singleton1{
    private static Singleton1 s1= new Singleton1();
    private Singleton1(){};
    public static Singleton1 get(){
        return s1;
    }
}
//懒汉式_将get方法设为同步;
class Singleton1{
    private static Singleton1 s1= null;
    private Singleton1(){};
    public static synchronized Singleton1 get(){
        if (s1 == null){
            s1 = new Singleton1();
        }
        return s1;
    }
}
//懒汉式_同步代码块
class Singleton1{
    volatile private static Singleton1 s1= null;
    private Singleton1(){};
    public static Singleton1 get(){
        if (s1 == null){
            synchronized (Singleton1.class){
                if (s1 == null){
                    s1 = new Singleton1();
                }
            }
        }
        
        return s1;
    }
}
//静态内部类
class Singleton1{
    private Singleton1(){};
    private static class Inner{
        private static Singleton1 s1 = new Singleton1();
    }
    public static Singleton1 get(){
        return Inner.s1;
    }
}

题目3_1:数组中重复的数字

解法1 对数组排序,再扫描数组;o(nlogn)
解法2 额外增加一个o(n)的哈希表,遇到数字则存入哈希表,若已存在则遇到重复;时间复杂度o(n)
解法3 因为数字限定在0-n-1的范围内,则可以将数字与下标对应,若遇到乱序则进行还原;时间复杂度o(n),空间复杂度o(1)
 public static int duplicate(int[] a){
        //空数组
        if (a.length == 0){
            System.out.println("null array");
            return -1;
        }
        for (int x = 0;x<a.length;x++){
            int temp = a[x];
            //包含0-(n-1)范围外的数字
            if (temp>a.length-1){
                System.out.print("error input");
                return -1;
            }
            //当角标不等时
            if (temp != x){
                //已有
                if (a[temp] == temp){
                    return temp;
                }else {
                    a[x] = a[temp];
                    a[temp] = temp;
                    System.out.println(Arrays.toString(a));
                    x -- ;
                }
            }
        }
        return -1;
    }

题目 3-2

额外o(n)的数组,依次将原数组复制至新数组对应下标,出现已有元素则找到重复 o(n);o(n)
将n个数二分,计算每部分在数组中出现的次数,必然有一边多于n/2,则重复出现在那边 o(nlogn);o(1)
public static int getDuplication(int[] a){
        if (a == null || a.length == 0){
            return -1;
        }
        int start = 1;
        int end = a.length - 1;
        while (start<=end){
            int mid = (end-start)/2+start;
            int count = count(a,start,mid);
            if (start == end){
                if (count>1){
                    return start;
                }else {
                    break;
                }
            }
            if ( count > mid - start + 1){
                end = mid;
            }else {
                start = mid+1;
            }
        }
        return -1;
    }
    public static int count(int[] a ,int start ,int end){
        int count = 0;
        if (a == null || a.length == 0){
            return count;
        }
        for (int x :a){
            if (x>=start && x<=end){
                count++;
            }
        }
        return count;
    }

题目 4

利用左下角或右上角减小需要判断的区域;
public static boolean find(int[][] a,int num){
        int hang = 0;
        int lie = a.length-1;
        if (a == null || a.length == 0){
            return false;

        }
        while (hang <= a.length-1 && lie >= 0){
            if (a[hang][lie] == num){
                return true;
            }
            else if (a[hang][lie] < num){
                hang++;
            }
            else if (a[hang][lie] > num){
                lie--;
            }
            if (hang > a.length || lie < 0){
                break;
            }
        }
        return false;
    }

题目 5_1

从头扫描字符串,遇到空格则后移后面的字符插入替换的字符 o(n[2])
预算替换后的长度,用两个指针,一个指向字符串尾,一个指向预算长度尾,向前替换 o(n)
//java中String类型是不可变的,数组是不可直接扩容的,故这题无法直接在原字符串上进行替换 
public static String replaceBlack(String a){
        if (a ==null ||a.length() == 0){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int x = 0;x<a.length();x++){
            if (a.charAt(x) == ' '){
                sb.append("%20");
            }else {
                sb.append(a.charAt(x));
            }
        }
        return sb.toString();
    }

题目5_2 合并两个有序数组

public static int[] combineArray(int[] a, int[] b){
        //数组为空的情况
        if (a == null || b == null){
            return null;
        }
        //数组长度为0的情况
        if (a.length == 0 || b.length == 0){
            return a.length == 0 ? b:a;
        }

        ArrayList<Integer> c = new ArrayList<>();

        int i = 0;
        int j = 0;
        while (i<a.length || j< b.length){
            if (i == a.length && j<b.length){
                c.add(b[j]);
                j++;
            }
            else if (i<a.length && j == b.length){
                c.add(a[i]);
                i++;
            }
            else {
                if (a[i]>=b[j]){
                    c.add(b[j]);
                    j++;
                }else {
                    c.add(a[i]);
                    i++;
                }
            }
        }
        int[] d = new int[c.size()];
        for(int x = 0;x<d.length;x++){
            d[x] = c.get(x);
        }
        return d;
    }

题目 6

用栈来进行暂存; o(n);o(n)
递归,每当访问一个节点首先输出后一个节点,链表过长会导致函数调用栈溢出; o(1);o(n)
//用的是自己写的链表,用的时候才发现当时漏掉了好多函数; 
public static void PrintListReversingly_Iteratively(MyLinkList my){
        Stack<MyLinkList.Node> st = new Stack<>();
        MyLinkList.Node no = MyLinkList.getRoot();
        while (MyLinkList.getNext(no)!= null){
            MyLinkList.Node next = MyLinkList.getNext(no);
            st.push(next);
            no = next;
        }
        while (st.isEmpty() != true){
            System.out.print(st.pop().data);
        }
    }
 public static void PrintListReversingly_Recursively(MyLinkList.Node e){
        if (e != null){
            if ( MyLinkList.getNext(e) != null){
                PrintListReversingly_Recursively(MyLinkList.getNext(e));
            }
            System.out.print(e.data);
        }
    }

题目7

递归的思想构建,每次返回子树根节点
 public static Tree.Node Construct(int[] preorder ,int[] inorder){
        if (preorder == null || inorder == null || preorder.length == 0 || inorder.length == 0|| preorder.length != inorder.length){
            return null;
        }

        Tree.Node temp = Tree.newNode(preorder[0]);
        for (int i = 0;i<inorder.length;i++){
            if (preorder[0] == inorder[i]){
                temp.leftchild = Construct(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
                temp.rightchild = Construct(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
            }
        }
        return temp;
    }

题目8 二叉树的下一个节点

分类讨论;若存在右子树,则下一个节点为右子树的最左节点;若无且当前节点为父节点的左节点,则为父节点;若为右节点,则向上找一个为左子节点的父亲的父亲;
public class test {

    public static void main(String[] args) {
        Node[] nodes = new Node[9];
        for(int i = 0;i<9;i++){
            nodes[i] = new Node(i);
        }
        nodes[0].leftchild = nodes[1];
        nodes[0].rightchild = nodes[2];
        nodes[1].father = nodes[0];
        nodes[1].leftchild = nodes[3];
        nodes[1].rightchild = nodes[4];
        nodes[2].father = nodes[0];
        nodes[2].leftchild = nodes[5];
        nodes[2].rightchild = nodes[6];

        nodes[3].father = nodes[1];
        nodes[4].father = nodes[1];
        nodes[4].leftchild = nodes[7];
        nodes[4].rightchild = nodes[8];
        nodes[5].father = nodes[2];
        nodes[6].father = nodes[2];

        nodes[7].father = nodes[4];
        nodes[8].father = nodes[4];

        print(nodes[0]);
        System.out.print("\n");
        System.out.println(getNext(nodes[6]).data);

    }
    public static void print(Node e){
        if (e == null){
            return;
        }
        if (e.leftchild != null){
            print(e.leftchild);
        }
        System.out.print(e.data);
        print(e.rightchild);
    }

    public static Node getNext(Node e){
        if (e == null){
            return null;
        }
        if (e.rightchild != null){
            Node temp = e.rightchild;
            while (temp.leftchild != null){
                temp = temp.leftchild;
            }
            return temp;
        }else {
            if (e.father.leftchild == e){
                return e.father;
            }else {
                while (e.father != null && e.father.father.leftchild != e.father){
                    e = e.father;
                }
                return e.father;
            }
        }
    }
}

class Node{
    int data;
    Node(int num){
        this.data = num;
    }
    Node leftchild;
    Node rightchild;
    Node father;
}

面试题9_1

stack2为空时,stack1弹出所有元素并压入stack2;stack2不为空时,直接弹出stack2的栈顶;
class CQueue{
    private static Stack<Integer> e1 = new Stack();
    private static Stack<Integer> e2 = new Stack();

    public static void appendTail(int num){
        e1.push(num);
    }

    public static int deleteHead() throws Exception{
        if (e2.isEmpty() == true){
            while (e1.isEmpty() != true){
                e2.push(e1.pop());
            }
        }
        if (e2.isEmpty() == true){
            throw new Exception("queue is empty");
        }
        return e2.pop();
    }
}

面试题9_2

维持一个队列为空,每次弹出是将1-(n-1)的元素移动至另一队列中;
class Cstack{
    private static ArrayDeque<Integer> q1 = new ArrayDeque<>();
    private static ArrayDeque<Integer> q2 = new ArrayDeque<>();

    public static void push(int num){
        if (q1.isEmpty() != true){
            q1.add(num);
        }else {
            q2.add(num);
        }
    }

    public static int pop() throws Exception{
        if (q1.isEmpty() != true){
            int temp = 0;
            while (temp<q1.size()-1){
                q2.add(q1.poll());
            }
            return q1.poll();
        }else {
            if (q2.isEmpty() == true){
                throw new Exception("empty");
            }
            int temp = 0;
            while (temp<q2.size()-1){
                q1.add(q2.poll());
            }
            return q2.poll();
        }
    }

}

面试题10_1

自底向上计算,避免重复计算 o(n)
利用公式化解计算 o(log[n])
public static int Fibonacci(int num){
        int f0 = 0;
        int f1 = 1;
        int temp = f0+f1;
        for (int x = 0;x<num-1;x++){
            temp = f0+f1;
            f0 = f1;
            f1 = temp;
        }
        return num == 0?f0:temp;
    }

面试题10_2

n = 1时只有一种跳法;n = 2时2钟;n时,可以选择前一步为只走1步,也可以选择前一步只走2步,f(n) = f(n-1)+f(n-2);

面试题10_3

f(8) = 竖着放一个小矩形:f(7)+横着放两个小矩形+f(6)

面试题11

最小值刚好位于两个有序数组的分界处,可以用二分查找的思想进行范围的缩小;当尾指针=头指针=中间指针时,只能进行顺序查找;o(log[n])
public static int Min(int[] a){
        if ( a == null || a.length == 0){
            return -1;
        }
        int i = 0;
        int j = a.length-1;
        int mid = 0;
        while (a[i]>=a[j]){
            if (j-i == 1){
                mid =j;
                break;
            }
            mid = (i+j)/2;
            if (a[i] == a[j] && a[i] == a[mid]){
                return MinInOrder(a,i,j);
            }
            if (a[i]<=a[mid]){
                i = mid;
            }
            else if (a[j]>=a[mid]){
                j = mid;
            }
        }
        return a[mid];
    }

    public static int MinInOrder(int[] a, int i,int j){
        int re = a[i];
        for(int x = i;x<=j;x++){
            if ( re > a[x] ){
                re = a[x];
            }
        }
        return re;
    }

面试题12:矩阵中的路径

遍历矩阵选择点;判断该点是否可作为字符串开头;若可作为开头,递归的查看其4个方向的点是否在字符串中;
    public static boolean hasPath(char[][] matrix,char[] str){
        if (matrix == null || str == null){
            return false;
        }
        int rows = matrix.length;
        if (rows == 0){
            return false;
        }
        int cols = matrix[0].length;
        if (cols == 0){
            return false;
        }
        
        boolean[][] visited = new boolean[rows][cols];
        int pathLenth = 0;
        for (int x = 0;x<rows;x++){
            for (int y = 0; y<cols;y++){
                if (hasPathCore(matrix,rows,cols,x,y,str,pathLenth,visited) == true){
                    return true;
                }
            }
        }
        return false;
    }
    public static boolean hasPathCore(char[][] matrix,int rows,int cols,int x,int y,char[] str,int pathlenth,boolean[][] visited){
        if (str.length == pathlenth){
            return true;
        }
        boolean hasPath = false;
        if (x>=0 && x<rows && y>=0 && y<cols && matrix[x][y] == str[pathlenth] && visited[x][y] == false){
            pathlenth++;
            visited[x][y] = true;
            
            hasPath = hasPathCore(matrix,rows,cols,x,y+1,str,pathlenth,visited) ||
                    hasPathCore(matrix,rows,cols,x,y-1,str,pathlenth,visited) ||
                    hasPathCore(matrix,rows,cols,x-1,y,str,pathlenth,visited) ||
                    hasPathCore(matrix,rows,cols,x+1,y,str,pathlenth,visited);
            
            if (hasPath == false){
                pathlenth --;
                visited[x][y] = false;
            }
        }
        return hasPath;
    }

面试题13:机器人的运动范围

可达点的周围也必然有可达点;从(0,0)开始,进行递归的扫描;
 public static int getDigitSum(int number){
        int sum = 0;
        while (number > 0){
            sum += number%10;
            number /= 10;
        }
        return sum;
    }
    public static boolean check(int threshold,int rows,int cols,int row,int col,boolean[][] visited){
        if (row>=0 && row<rows && col>=0 && col<cols && (getDigitSum(row)+getDigitSum(col))>threshold && visited[row][col] == false){
            return true;
        }
        return false;
    }
    public static int movingCount(int threshold,int rows,int cols){
        if (threshold<0 || rows<0 || cols<0){
            return 0;
        }
        boolean[][] visited = new boolean[rows][cols];
        
        return movingCountCore(visited,threshold,rows,cols,0,0);
    }
    public static int movingCountCore(boolean[][] visited,int threshold,int rows,int cols,int row,int col){
        int count = 0;
        if (check(threshold,rows,cols,row,col,visited) == true){
            visited[row][col] = true;
            count = 1+ movingCountCore(visited,threshold,rows,cols,row,col+1)+movingCountCore(visited,threshold,rows,cols,row,col-1)+
                        movingCountCore(visited,threshold,rows,cols,row-1,col)+movingCountCore(visited,threshold,rows,cols,row+1,col);
        }
        return count;
    }

面试题14:剪绳子

动态规划 f(n) = max{f(i) * f(n-i)}; o(n[2]);o(n);
贪婪算法 尽量多剪长度为3的,剩下长度为4时,剪成两个长度为2 o(1)
    public static int maxProductAfterCutting_solution1(int lenth){
        if (lenth<2){
            return 0;
        }else if (lenth == 2){
            return 1;
        }else if (lenth == 3){
            return 2;
        }


        int[] products = new int[lenth+1];

        //用于乘的组件 product[4]过后才是记录的值
        products[1] = 1;
        products[2] = 2;
        products[3] = 3;


        for (int x = 4;x <= lenth;x++){
            int max = 0;
            for (int y = 1;y <= x/2;y++){
                int product = products[y]*products[x-y];
                if (max<product){
                    max = product;
                }
            }
            products[x] = max;
        }
        return products[lenth];
    }
    public static int maxProductAfterCutting_solution2(int length){
        if (length<2){
            return 0;
        }else if (length == 2){
            return 1;
        }else if (length == 3){
            return 2;
        }
        
        int timesOf3 = length/3;
        
        if (length - timesOf3*3 == 1){
            timesOf3 = timesOf3 -1;
        }
        int timesof2 = (length - timesOf3*3)/2;
        
        return (int)(Math.pow(3,timesOf3) * Math.pow(2,timesof2));
    }

面试题15_1:二进制中1的个数

在电脑中存储的是二进制的树;可以通过与1进行某位的判断;可以通过n&(n-1)将最右的1置0;
public static int NumberOf1(int n){
        int count = 0;
        while (n!=0){
            count++;
            n = n & (n-1);
        }
        return count;
    }

面试题15_2:

若一个整数是2的整数次方,则其二进制表示中只有1位为1;
public static boolean if2(int num){
        if ( num <= 0){
            return false;
        }
        if ( (num & (num-1)) == 0){
            return true;
        }
        return false;
    }

面试题15_3:

求两个数二进制表示中不同的位的个数;求异或,再计算1的个数;
 public static int diff(int num1,int num2){
        int num3 = num1 ^ num2;
        int count = 0;
        while (num3 != 0){
            count = count+1;
            num3 = num3 & (num3-1);
        }
        return count;

    }



猜你喜欢

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