美文网首页
剑指Offer(java答案)(11-20)

剑指Offer(java答案)(11-20)

作者: 壮少Bryant | 来源:发表于2019-05-25 11:47 被阅读0次

    11、数值的整数次方

    题目描述
    给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

    思路1:本题主要考虑边界问题,全面不够高效的解法,注意:由于计算机表示小数(包括float和double型小数)都有误差,我们不能直接用==判断两个小数是否相等,如果两个小数的差的绝对值很小,比如小于0.0000001,就可以认为他们相等

    public class Solution {
        public double Power(double base, int exponent) {
            double res = 0.0;
            if (equal(base, 0.0) && exponent < 0) {
                throw new RuntimeException("0的负数次幂没有意义");
            }
            // 这里定义0的0次方为1
            if (exponent == 0) {
                return 1.0;
            }
            if (exponent < 0) {
                res = powerWithExponent(1.0/base, -exponent);
            } else {
                res = powerWithExponent(base, exponent);
            }
            return res;
        }
         
        private double powerWithExponent(double base, int exponent) {
            double res = 1.0;
            for (int i = 1; i <= exponent; i++) {
                res = res * base;
            }
            return res;
        }
         
        // 判断double类型的数据
        private boolean equal(double num1, double num2) {
            if (Math.abs(num1 - num2) < 0.0000001) {
                return true;
            }
            return false;
        }
    }
    
    

    思路2:n为偶数时:an=an/2 * a^n/2;
    n为奇数,an=(a(n-1)/2)* (a^(n-1/2))* a
    所以对乘法处进行优化,如果是32次方,等于16次方*16次方

     /*
        ** 对乘法进行优化的部分
        */
        private double powerWithExponent(double base, int exponent) {
            if(exponent==0){
                return 1;
            }
             
            if(exponent==1){
                return base;
            }
             
           double result = powerWithExponent(base,exponent>>1);//每次除以2
           result*=result;//最后相乘,如果是奇数,还要乘一个
            
            //如果是奇数次方的情况,最终除2余1要与base相乘
            if((exponent & 0x1)==1){
                result *= base;
            }
            return result;
        }
    

    12、打印1到最大的n位数

    题目描述:如n=3,则从1打印到999

    public class Solution {
    
        // ====================方法一====================
        public static void Print1ToMaxOfNDigits(int n) {
            if (n <= 0)
                return;
    
            char[] number = new char[n];
            
            //每一个字符设为0
            for (int i = 0; i < n; i++) {
                number[i] = '0';
            }
    
            while (!Increment(number)) {//如果加法溢出,则退出,否则打印数字
                PrintNumber(number);
            }
    
        }
    
        // 字符串number表示一个数字,在 number上增加1
        // 如果做加法溢出,则返回true;否则为false
        public static boolean Increment(char[] number) {
            boolean isOverflow = false;//溢出标志
            int nTakeOver = 0;//进位
            int nLength = number.length;
    
            for (int i = nLength - 1; i >= 0; i--) {//从后向前,最后一位数字加1
                int nSum = number[i] - '0' + nTakeOver;
                if (i == nLength - 1)
                    nSum++;
    
                if (nSum >= 10) {
                    if (i == 0)
                        isOverflow = true;
                    else {
                        nSum -= 10;
                        nTakeOver = 1;
                        number[i] = (char) ('0' + nSum);
                    }
                } else {
                    number[i] = (char) ('0' + nSum);
                    break;
                }
            }
    
            return isOverflow;
        }
    
        // 字符串number表示一个数字,数字有若干个0开头
        // 打印出这个数字,并忽略开头的0
        public static void PrintNumber(char[] number) {
            boolean isBeginning0 = true;
            int nLength = number.length;
    
            //标志位的思想,从第一位不为0的数字开始打印,如000123,打印123
            for (int i = 0; i < nLength; ++i) {
                if (isBeginning0 && number[i] != '0')
                    isBeginning0 = false;
    
                if (!isBeginning0) {
                    System.out.print(number[i]);
                }
            }
            System.out.println();
        }
    }
    
    

    思路2:用递归,代码简洁,思路不好想,每一位都是从0到9的全排列

    public class Solution {
    
        // // ====================方法二:递归====================
        public static void Print1ToMaxOfNDigits(int n) {
            if (n <= 0)
                return;
    
            char[] number = new char[n];
    
            for (int i = 0; i < 10; ++i) {
                number[0] = (char) (i + '0');
                Print1ToMaxOfNDigitsRecursively(number, n, 0);
            }
    
        }
    
        public static void Print1ToMaxOfNDigitsRecursively(char[] number, int length,int index) {
            if (index == length - 1) {
                PrintNumber(number);
                return;
            }
    
            for (int i = 0; i < 10; ++i) {
                number[index + 1] = (char) (i + '0');
                Print1ToMaxOfNDigitsRecursively(number, length, index + 1);
            }
        }
    
        // 字符串number表示一个数字,数字有若干个0开头
        // 打印出这个数字,并忽略开头的0
        public static void PrintNumber(char[] number) {
            boolean isBeginning0 = true;
            int nLength = number.length;
    
            // 标志位的思想,从第一位不为0的数字开始打印,如000123,打印123
            for (int i = 0; i < nLength; ++i) {
                if (isBeginning0 && number[i] != '0')
                    isBeginning0 = false;
    
                if (!isBeginning0) {
                    System.out.print(number[i]);
                }
            }
            System.out.println();
        }
    }
    
    

    13、在O(1)时间删除链表结点

    给定单向链表头指针和一个节点指针,在O(1)时间删除链表结点

    /*
        对于删除节点,我们普通的思路就是让该结点的前一个节点指向改节点的下一个节点
        */
        public void delete(Node head, Node toDelete){
            if(toDelete == null){
                return ;
            }
            if(toDelete.next != null){//删除的节点不是尾节点
                toDelete.val = toDelete.next.val;
                toDelete.next = toDelete.next.next;
            }else if(head == toDelete){//链表只有一个节点,删除头结点也是尾节点
                head = null;
            }else{ //删除的节点是尾节点的情况
                Node node = head;
                while(node.next != toDelete){//找到倒数第二个节点
                    node = node.next;
                }
                node.next = null;
            }
        }
    

    14、调整数组顺序使奇数位于偶数前面

    题目描述
    输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

    public class Solution {
        public void reOrderArray(int [] array) {
            //注释的部分使用快速排序的算法,很明显快速排序是不稳定的,这里需要用归并排序
            /*
            if(array.length == 0){
                return;
            }
            int high = array.length - 1;
            int low = 0;
            while(low < high){
                while(low < high && array[low] % 2 == 1){
                    low ++;
                }
                while(low < high && array[high] % 2 == 0){
                    high --;
                }
                int temp = array[low];
                array[low] = array[high];
                array[high] = temp;
            }*/
            
            //用用归并排序的思想,因为归并排序是稳定的
            int length = array.length;
            if(length == 0){
                return;
            }
            int[] des = new int[length];
            MergeMethod(array, des, 0,length - 1);
        }
        public void MergeMethod(int[] array, int [] des, int start, int end){
            if(start < end){
                int mid = (start + end) / 2;
                MergeMethod(array, des, start, mid);
                MergeMethod(array, des, mid + 1, end);
                Merge(array, des, start, mid, end);
            }
        }
        
        public void Merge(int[] array, int[] des, int start, int mid, int end){
            int i = start;
            int j = mid + 1;
            int k = start;
            while(i <= mid && array[i] % 2 == 1){
                des[k++] = array[i++];
            }
            while(j <= end && array[j] % 2 == 1){
                des[k++] = array[j++];
            }
            while(i <= mid){
                des[k++] = array[i++];
            }
            while(j <= end){
                des[k++] = array[j++];
            }
            
            for(int m = start; m <= end; m++){
                array[m] = des[m];
            }
        }
    }
    

    15、链表中倒数第k个结点

    题目描述
    输入一个链表,输出该链表中倒数第k个结点。

    思路:两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指正走(k-1)步,到达第k个节点。然后两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了

    /**
    public class ListNode {
        int val;
        ListNode next = null;
    
        ListNode(int val) {
            this.val = val;
        }
    }*/
    public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            if(head==null||k<=0){
                return null;
            }
            ListNode pre=head;
            ListNode last=head;       
            for(int i=1;i<k;i++){
                if(pre.next!=null){
                    pre=pre.next;
                }else{
                    return null;
                }
            }
            while(pre.next!=null){
                pre = pre.next;
                last=last.next;
            }
            return last;
        }
    }
    

    16、反转链表

    题目描述
    输入一个链表,反转链表后,输出链表的所有元素。

    /*
     public class ListNode {
     int val;
     ListNode next = null;
     
     ListNode(int val) {
     this.val = val;
     }
     }*/
    public class Solution {
        public ListNode ReverseList(ListNode head) {
            if (head == null)
                return null;
            if (head.next == null)
                return head;
     
            ListNode pPre = null;
            ListNode p = head;
            ListNode pNext = head.next;
            ListNode newHead = null;
     
            while (p != null) {
                pNext = p.next;//一定要记录下来后面的节点
                if (pNext == null)
                    newHead = p;
                p.next = pPre;//这里的方向已经转变
                pPre = p;
                p = pNext;//将保存的后面的节点作为下一次循环的p
     
            }
            return newHead;
     
        }
    }
    

    17、合并两个排序的链表

    题目描述
    输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

    /**
    public class ListNode {
        int val;
        ListNode next = null;
    
        ListNode(int val) {
            this.val = val;
        }
    }*/
    public class Solution {
         public ListNode Merge(ListNode list1, ListNode list2) {
             if(list1==null)
                return list2;
             else if(list2==null)
                return list1;
             ListNode mergeHead=null;
             if(list1.val<list2.val){
                 mergeHead=list1;
                 mergeHead.next=Merge(list1.next, list2);
             }
             else{
                 mergeHead=list2;
                 mergeHead.next=Merge(list1, list2.next);
             }
             return mergeHead;
    
        }
    }
    

    非递归方法:

    public class Solution {
        public ListNode Merge(ListNode list1,ListNode list2) {
            if(list1==null&&list2==null)
                return null;
            if(list1==null&&list2!=null)
                return list2;
            if(list1!=null&&list2==null)
                return list1;
            ListNode head = null;
            if(list1.val<list2.val){
                head = list1;
                list1 = list1.next;
            }
            else{
                head = list2;
                list2 = list2.next;
            }
            ListNode cur = head;
            cur.next=null;
            while(list1!=null&&list2!=null){
                if(list1.val<list2.val){
                    cur.next = list1;
                    list1 = list1.next;
                }
                else{
                    cur.next = list2;
                    list2 = list2.next;
                }
                cur = cur.next;
                cur.next = null;
            }
            if(list1==null&&list2!=null){
                cur.next =list2;
            }else if(list2==null&&list1!=null){
                cur.next = list1;
            }
            return head;
        }
    }
    
    

    18、树的子结构

    题目描述
    输入两颗二叉树A,B,判断B是不是A的子结构。

    思路:首先遍历A树,找到A的根节点和B的根节点相同的点,找到之后再遍历A和B的各个子节点是否相同

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
      
        public TreeNode(int val) {
            this.val = val;
      
        }
      
    }*/
    public class Solution {
       public boolean HasSubtree(TreeNode root1,TreeNode root2) {
            if(root2==null) return false;
            if(root1==null && root2!=null) return false;       
            boolean flag = false;
            if(root1.val==root2.val){
                flag = isSubTree(root1,root2);
            }
            if(!flag){
                flag = HasSubtree(root1.left, root2);
            }
            if(!flag){
                flag = HasSubtree(root1.right, root2);
            }
            return flag;
        }
          
        private boolean isSubTree(TreeNode root1, TreeNode root2) {
            if(root2==null) return true;
            if(root1==null && root2!=null) return false;       
            if(root1.val==root2.val){
                return isSubTree(root1.left, root2.left) && isSubTree(root1.right, root2.right);
            }
            return false;
        }
    }
    

    19、二叉树的镜像

    题目描述
    操作给定的二叉树,将其变换为源二叉树的镜像。
    二叉树的镜像定义:源二叉树
    8
    /
    6 10
    / \ /
    5 7 9 11
    镜像二叉树
    8
    /
    10 6
    / \ /
    11 9 7 5

    思路1:用栈结构(改成队列结构也可以),将节点依次入栈,每个入栈的节点都镜像他的子节点

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
        }
    }
    */
    
    import java.util.Stack;
     
    public class Solution {
        public void Mirror(TreeNode root) {
            if(root == null){
                return;
            }
            Stack<TreeNode> stack = new Stack<TreeNode>();
            stack.push(root);
            while(!stack.isEmpty()){
                TreeNode node = stack.pop();
                if(node.left != null||node.right != null){
                    TreeNode temp = node.left;
                    node.left = node.right;
                    node.right = temp;
                }
                if(node.left!=null){
                    stack.push(node.left);
                }
                if(node.right!=null){
                    stack.push(node.right);
                }
            }
        }
    }
    

    思路2:前序遍历的递归

    public class Solution {
        public void Mirror(TreeNode root) {
            if(root == null)  return;
            if(root.left != null || root.right != null)
            {   
            //创建临时节点,交换左右节点       
                TreeNode tempNode = null;           
                tempNode = root.left;
                root.left = root.right;
                root.right = tempNode;
                Mirror(root.left);
                Mirror(root.right);
                 
            }
        }
    }
    

    20、顺时针打印矩阵

    题目描述
    输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵:
    1 2 3 4
    5 6 7 8
    9 10 11 12
    13 14 15 16
    则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

    import java.util.ArrayList;
    public class Solution {
        public ArrayList<Integer> printMatrix(int [][] matrix) {
           ArrayList<Integer> list = new ArrayList<Integer>();
            int rows = matrix.length;
            int columns = matrix[0].length;
            
            if(matrix == null || columns <= 0 || rows <= 0){
                return null;
            }
            int start = 0;
            while(columns > start *2 && rows > start * 2){
                print1Circle(list,matrix,columns,rows,start);
                start++;
            }
     
            return list;
        }
         
        public void print1Circle(ArrayList<Integer> list, int[][] matrix,int columns, int rows, int start) {
            int endX = columns - 1 - start;
            int endY = rows - 1 - start;
            
            //从左往右打印一行
            for (int i = start; i <= endX; i++) {
                list.add(matrix[start][i]);
            }
            
            //从上往下打印一列,至少有两行
            if (start < endY){
                for (int i = start+1; i <= endY; i++) {
                    list.add(matrix[i][endX]);
                }
            }
             
             //从右往左打印一行,至少有两行两列
            if (start < endY && start < endX){
                for (int i = endX - 1; i >= start; i--) {
                    list.add(matrix[endY][i]);
                }
            }
            
             //从下往上打印一列,至少有三行两列
            if (start < endY -1 && start < endX){
                for (int i = endY - 1; i >= start + 1; i--) {
                    list.add(matrix[i][start]);
                }
            }     
            
        }
    }
    

    声明:此文章为本人原创,如有转载,请注明出处

    如果您觉得有用,欢迎关注我的公众号,我会不定期发布自己的学习笔记、AI资料、以及感悟,欢迎留言,与大家一起探索AI之路。

    AI探索之路

    相关文章

      网友评论

          本文标题:剑指Offer(java答案)(11-20)

          本文链接:https://www.haomeiwen.com/subject/hkpazqtx.html