剑指Offer 刷题笔记(20/66)——JAVA

11.二进制中1的个数

题目:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。(链接)
解析

按位与运算符“&”是双目运算符: 其功能是参与运算的两数各对应的二进位相与。(1&1=1; 1&0=0; 0&0=0)
n&(n-1)作用:将n的二进制表示中的最低位为1的改为0, 如: n = 10100(二进制);则(n-1) =10011
n&(n-1) = 10000 可以看到原本最低位为1的那位变为0。
n & (n-1)的其它应用

每次n和n-1按位与,二进制原本最低位为1的那位变为0。二进制1的个数减一,最终值为0时,没有1存在,count值就为1的个数。

public class Solution {
    public int NumberOf1(int n) {
        int count=0;
        while(n!=0){
            n &= (n-1);
            count++;
        }
        return count;
    }
}

12.数值的整数次方

题目:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。(链接)
解析:求 baseexponent:指数函数
快速幂:
快速幂的目的就是做到快速求幂,假设我们要求a^b,按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn),快了好多好多。它的原理如下:
  假设我们要求ab,那么其实b是可以拆成二进制的,该二进制数第i位的权为2(i-1),例如当b==11时,a11 = a^ (2 ^ 0+2^ 1+2^ 3)
  11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算也就是a1 * a2 * a8 。

public class Solution {
    public double Power(double base, int exponent) {
        if(exponent==0)
            return 1.0;
        int e = exponent > 0 ? exponent :-exponent;
        double res=1;
        while(e!=0){
            if((e & 1) != 0)
                res *=base;
            base *= base;
            e>>=1;
        }
        return exponent > 0 ? res : 1/res;
  }
}

13.调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。(链接)
解析类似冒泡算法,前偶后奇数就交换。

public class Solution {
    public void reOrderArray(int [] array) {
        for(int i=0;i<array.length;i++){
            for(int j=array.length-1;j>i;j--){
                if(array[j]%2==1 && array[j-1]%2 ==0)
                {
                    int x = array[j-1];
                    array[j-1]=array[j];
                    array[j]=x;
                }
            }
        }
    }
}

14.链表中倒数第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) {
        ListNode pre=head,last=head;
        int i=0;
        for(;pre!=null;i++){
            pre=pre.next;
            if(i>=k){
                last=last.next;
            }
        }
        return i<k ? null : last;
    }
}

15.反转链表

题目:输入一个链表,反转链表后,输出新链表的表头。(链接)
解析:有2种方法:①递归②非递归。
代码是非递归方法,利用链表的头插法会得到反序的链表这一性质来做。

/*
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||head.next==null) 
            return head;
        ListNode pre=null,next=null;
        while(head!=null){
            next=head.next;
            head.next=pre;
            pre=head;
            head=next;
        }
        return pre;
    }
}

16.合并两个排序的链表

题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。(链接)
解析:有2种法:①递归②非递归。
代码是递归方法:两个链表中,哪个头结点的值小就返回哪个头结点,然后合并剩下的链表,最后令头结点的值较小的那个头结点的next指向合并后的剩余链表。

/*
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;
        if(list2==null)
            return list1;
        if(list1.val<=list2.val){
            list1.next = Merge(list1.next,list2);
            return list1;
        }
        else{
            list2.next = Merge(list1,list2.next);
            return list2;
        }
    }
}

17.树的子结构

题目:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)(链接)
解析:首先,判断B是不是A的子结构,然后再判断B是不是A的左子树的子结构,B是不是A的右子树的子结构;
利用好 || 和 && 的短路特性。

/**
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(root1==null || root2==null)
            return false;
        //判断根节点,其左子树,其右子树是否为tree2;
        return isSubtree(root1,root2) || HasSubtree(root1.left,root2) ||HasSubtree(root1.right,root2);  
    }
    
    public boolean isSubtree(TreeNode node1,TreeNode node2){
        if(node2 == null)
            return true;
        if(node1 == null)
            return false;
        if(node1.val == node2.val)
            return isSubtree(node1.left,node2.left) && isSubtree(node1.right,node2.right);
        return false;
    }
}

18.二叉树的镜像

题目:操作给定的二叉树,将其变换为源二叉树的镜像。(链接)
解析:递归求解,把根节点的左右子树都转化成镜像二叉树,然后再交换左右指针就行了。

/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}
*/
public class Solution {
    public void Mirror(TreeNode root) {
        if(root == null)
            return;
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        Mirror(root.left);
        Mirror(root.right);
    }
}

19.顺时针打印矩阵

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 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> res = new ArrayList<Integer>();
        int left=0, right=matrix[0].length-1, top=0, bottom=matrix.length-1;
        while(left<=right && top <=bottom){
            for(int i=left;i<=right;i++)
                res.add(matrix[top][i]);
            for(int i=top+1;i<=bottom;i++)
                res.add(matrix[i][right]);
            if(top!= bottom)
            for(int i=right-1;i>=left;i--)
                res.add(matrix[bottom][i]);
            if(left!=right)
            for(int i=bottom-1;i>top;i--)
                res.add(matrix[i][left]);
            left++;right--;bottom--;top++;
        }
        return res;
    }
}
import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> res = new ArrayList<Integer>();
        int left=0, right=matrix[0].length-1, top=0, bottom=matrix.length-1;
        int x=0,y=0;
        while(left<=right && top<=bottom){
            for(;y<=right && left<=right && top<=bottom;y++)
                res.add(matrix[x][y]);
            for(top++,y--,x++; x<=bottom && left<=right && top<=bottom;x++)
                res.add(matrix[x][y]);
            for(right--,x--,y--; y>=left && left<=right && top<=bottom;y--)
                res.add(matrix[x][y]);
            for(bottom--,y++,x--; x>=top && left<=right && top<=bottom;x--)
                res.add(matrix[x][y]);
            left++;x++;y++;
        }
        return res;
    }
}

20.包含min函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。(链接)
解析:题目要求O(1)的时间复杂度内得到最小值。那么只能空间换时间了;
每次压栈操作时,如果压栈元素比当前最小元素更小,就把这个元素压入最小元素栈,原本的最小元素就成了次小元素。同理,弹栈时,如果弹出的元素和最小元素栈的栈顶元素相等,就把最小元素的栈顶弹出。
如data依次入栈,5, 4, 3,8,10,11,12,1
则min 依次入栈,5, 4, 3,x, x , x , x , 1

import java.util.Stack;

public class Solution {

    Stack<Integer> dataStack = new Stack<Integer>();
    Stack<Integer> minStack = new Stack<Integer>();
    public void push(int node) {
        dataStack.push(node);
        if( minStack.empty() || node <= min())
            minStack.push(node);
    }
    
    public void pop() {
        if(dataStack.peek() == min())
            minStack.pop();
        dataStack.pop();
    }
    
    public int top() {
        return dataStack.peek();
    }
    
    public int min() {
        return minStack.peek();
    }
}

猜你喜欢

转载自blog.csdn.net/Sakura_Jun/article/details/84634053