Fork me on GitHub

剑指offer(二)

概述

  继续刷题。。。

第九题

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

 解题思路

这道题的名字叫做变态跳台阶问题,那为什么叫变态我就不知道了,其实和第8题思路差不多。

  • 假设第一次青蛙跳1级,那剩余n-1级,也就是还有f(n-1)种跳法
  • 假设第一次青蛙跳2级,那剩余n-2级,也就是还有f(n-2)种跳法
  • ...
  • 假设第一次青蛙跳n级,那剩余n-n级
  • 总结下来就是 f(n) = f(n-1) + f(n-2) + f(n-3) +...+ f(0)
  • 由于f(0) = 1,f(1) = 1,f(2) = 2f(1),f(3) = 2f(2),所以f(n) = 2f(n-1) = 2(n-1)

代码实现

public class Solution {
    public int JumpFloorII(int target) {
        if(target <= 0){
            return 0;
        }
        return (int)Math.pow(2,target-1);
    }
}

 

第十题

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
比如n=3时,2*3的矩形块有3种覆盖方法:

解题思路

这道题叫做矩形覆盖问题,其实和第7题,第8题,第9题差不多,这道题也是使用斐波那契数列解决

  • 由于直接考虑整个矩形覆盖无从下手,那就考虑刚开始的地方,如果第一个2*1的小矩形竖着放(也就是还是2*1),那大矩形剩余2*(n-1),那就还有f(n-1)种覆盖方法
  • 如果第一个小矩形横着放(也就变成1*2),那这个小矩形下面的空间只能再横着放一个小矩形,那大矩形剩余2*(n-2),也就还有f(n-2)种覆盖方法
  • 综上所述,f(n) = f(n-1) + f(n-2)

代码实现

public class Solution {
    public int RectCover(int target) {
        if(target <= 0){
            return 0;
        }
        if(target == 1){
            return 1;
        }
        if(target == 2){
            return 2;
        }
          
        int a = 1;
        int b = 2;
        int c = 0;
        for(int i = 3;i <= target;i++){
            c = a + b;
            a = b;
            b = c;
        }
        return c;
    }
}

 

第十一题

输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。

解题思路

  这个题如果不仔细思考很简单,如果仔细思考,就比较复杂了,比如题中说的是一个整数,那这个整数是int类型,还是long类型的,虽然牛客网上代码限定是int类型,因为int类型无论是32位编译器还是64位编译器都是占用4位,也就是32个字节,如果是这样,那就简单了,因为负数在计算机中本身就是使用补码存储的,所以可以使用如下方式解决。

代码

直接获取整数的二进制表示,统计一下1的个数,我并不喜欢这种解决方式(这种时间复杂度是O(n))

public class Solution {
    public int NumberOf1(int n) {
        int t=0;
            char[]ch=Integer.toBinaryString(n).toCharArray();
            for(int i=0;i<ch.length;i++){
                if(ch[i]=='1'){
                    t++;
                }
            }
            return t;
    }
}

还有一种就是通过位运算来统计,时间复杂度为O(m),其中m为1的个数

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

由于我在第一次做这个题的时候,并不知道怎么获取计算机中整数二进制表示,而且没有想到使用位运算,想了一种非常复杂的方法,先贴在这里,方便以后看

import java.util.Stack;
public class Solution {
    public int NumberOf1(int n) {
        if (n == 0){
            return 0;
        }
        int bark = n;
        n = Math.abs(n);
        Stack<Integer> stack = new Stack();
        while(n != 0){
            int result = n % 2;
            if(result == 0){
                stack.push(0);
                n = n/2;
            }else{
                stack.push(1);
                n = (n - result)/2;
            }
        }
        
        int result = 0;
        int count = 0;
        if (bark > 0){
            while(!stack.isEmpty()){
                result = (int)stack.pop();
                if(result == 1){
                    count++;
                }
            }
        }else{
            int[] array = new int[32];
            int length = stack.size();
             for(int i=0;i<array.length;i++){
                if(array.length - i == stack.size()){
                    result = (int)stack.pop();
                    if(result == 1){
                    array[i] = 0;
                    }else{
                        array[i] = 1;
                    }
                }else{
                    array[i]=1;
                }
            }
            for(int i=array.length-1;i>=0;i--){
                if(array[i]==0){
                    array[i] = 1;
                    break;
                }else{
                    array[i]=0;
                    if(i-1<0){
                        return -1;
                    }else{
                        if(array[i-1] == 0){
                            array[i-1] = 1;
                            break;
                        }
                    }
                }
            }
            for(int i=0;i<array.length;i++){
                if(array[i]==1){
                    count++;
                }
            }
        }
        return count;
    }
}
View Code

 

第十二题

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

保证base和exponent不同时为0

解题思路

这道题,咋一看很简单,直接写个for循环,或者使用系统自带的函数都可以轻松解决,但是这个题并不是看你能不能把结果搞出来,而是看你怎么搞。

 

如果直接使用for循环,时间复杂度为O(n),比如计算38,需要8次计算,那有没有比这个次数更少的方法呢?答案是有的,就是快速求幂算法,下面简单介绍一下这个算法。

 

如果计算38,我们肯定要计算3*3,这是32,那其实38 = (34 * 34) = (3 * 32) * (3 * 32),所以最后我们只需要计算3次,第一次计算3*3=3,第二次计算3 * 3= 34,第三次计算34 * 34 = 38

 

好有了上面的基础,我们就可以正式来解决我们的问题,我们发现8的8位二进制表示为:0000 1000,我们发现其实二进制中1的位置右边3个0,正好是要计算的3次,(这里表述有问题,大家酬和着看,意思就是这个意思)

 

代码

直接计算结果的方法(简单又粗暴)

public class Solution {
    public double Power(double base, int exponent) {
        return Math.pow(base,exponent);
  }
}

使用快速幂运算

   def fast_power(self, base, exponent):
        if base == 0:
            return 0
        if exponent == 0:
            return 1
        e = abs(exponent)
        tmp = base
        res = 1
        while(e > 0):
            #如果最后一位为1,那么给res乘上这一位的结果
            if (e & 1 == 1):
                res =res * tmp
            e = e >> 1
            tmp = tmp * tmp
        return res if exponent > 0 else 1/res

 

第十三题

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

解题思路

  这个题我想的思路很简单,直接把奇数和偶数先找出来分别存放,之后再合并到一起。

代码

public class Solution {
    public void reOrderArray(int [] array) {
        int[] a = new int[array.length];
        int[] b = new int[array.length];
        
        int j = 0;
        int k = 0;
        for(int i = 0;i<array.length;i++){
            if(array[i] % 2 == 1){
                a[j] = array[i];
                j++;
            }else{
                b[k] = array[i];
                k++;
            }
        }
        System.arraycopy(a,0,array,0,j);
        System.arraycopy(b,0,array,j,k);

    }
}

 

第十四题

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

解题思路

这个题按照常规的思考方式来,就是先遍历一遍,记录一下总数,之后再去遍历找到倒数第k个,这个时间复杂度是很高的,有一种更好的解决办法,使用快慢指针解决,具体就是第一个指针先走,当走到第K个节点的时候,第二个指针开始走,当第一个指针走到结尾的时候,第二个指针指向的就是倒数第k个节点

代码

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode p = head;
        int i;
        for(i = 0;head != null;i++){
            if(i > k-1){
                p = p.next;
            }
            head = head.next;
        }
        if(i < k){
            return null;
        }
        return p;
    }
}

 

第十五题

输入一个链表,反转链表后,输出新链表的表头。

解题思路

这个题很简单,就是遍历的时候修改一下指针的方向就可以了,可能出问题的地方就是要搞清楚每个变量到底是指向哪个节点,不要两个变量指向同一个节点,一个变量把这个节点修改了,那另一个变量自然也就跟着变了。

代码

public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head == null){
            return null;
        }
        ListNode temp = new ListNode(head.val);
        while(head.next != null){
            ListNode temp1 = new ListNode(head.next.val);
            temp1.next = temp;
            temp = temp1;
            head = head.next;
        }
        return temp;
    }
}

 

第十六题

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

解题思路

这个题和上一个题差不太多,就是遍历两个链表,然后逐个比较大小,最后构建一个新的链表

代码

/*
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 == null && list2 == null){
            return null;
        }
        
        ListNode next1;
        ListNode next2;
        ListNode next = null;
        if(list1.val < list2.val){
            next = list1;
            list1 = list1.next;
        }else{
            next = list2;
            list2 = list2.next;
        }
        ListNode temp = next;
        while(list1 != null && list2 != null){
            if(list1.val >= list2.val){
                next2 = list2.next;
                temp.next = list2;
                temp = temp.next;
                list2 = next2;
            }else{
                next1 = list1.next;
                temp.next = list1;
                temp = temp.next;
                list1 = next1;
            }
        }
        if(list1 != null){
            temp.next = list1;
        }
        if(list2 != null){
            temp.next = list2;
        }
        return next;
    }
}

 

写在最后的话

  最近股市上涨,我也跟风买,谁知买在了高点,每天心心念念想着盯盘,为了百分之几的利润高兴或者难过,今天全部清仓,赔了10几个点,反而变得清净了,果然是人若没有执着,没有贪,没有嗔,没有痴,就会变得平静,就会有定。  

posted @ 2020-07-16 16:36  猿起缘灭  阅读(139)  评论(0编辑  收藏  举报