刷刷编程基础题~(1)

咳咳咳,今晚开始刷剑指offer,以前做过一部分,这次认真再来一下

1.二维数组中的查找

题目描述

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。

请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

解题思路:

第一种方法:

把每一行看成有序递增的数组,
利用二分查找,
通过遍历每一行得到答案,
时间复杂度是nlogn

第二种方法:

从左下角的元素开始比较,target大的话row++,target小的话col--


//第一种
public class Solution {
    public boolean Find(int [][] array,int target) {
        //对每列二分检索(每列每行都一样)
        for(int i=0;i<array.length;i++){
            int low=0;
            int high=array[0].length-1;
            while(low<=high){//别忘了这里
                int mid=(low+high)/2;
            	if(target==array[i][mid])
            	{
                	return true;
            	}else{
                	if(target>array[i][mid]){
                    	low=mid+1;
                	}else{
                    	high=mid-1;
                	}
            	}
            } 
        }
        return false;
    }
}

//第二种
public class Solution {
    public boolean Find(int [][] array,int target) {
        int row=0;
        int col=array[0].length-1;
        while(row<array.length&&col>=0){//边界这里其实有点问题
            if(target==array[row][col]){
            	return true;
            }else if(target>array[row][col]){
                row++;
            }else{
                col--;
            }
        	
        }
        return false;
    }
}


2.替换空格

题目描述

请实现一个函数,将一个字符串中的空格替换成“%20”。

例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

解体思路:

这种题有很多种方法,可以巧妙地用已有的方法

第一种:

(简单粗暴)正则表达式

题中给定的参数传的是StringBuffer类的,先str.toString()转化为String类【那个括号不要忘了】

然后用String类的replaceAll("\\s","%20");

\s表示空格,前面的 \ 用来转义第二个 \

!!注意:replaceAll方法返回一个String,而不是更改原来的字符串,所以要新定义一个String a=s.replaceAll("","")

第二种:

转化为String类后,在转化成char[]数组,遍历,声明一个StingBuffer类,遇到空格,加上%20,否则加原来的字符


//第一种
public class Solution {
    public String replaceSpace(StringBuffer str) {
        return str.toString().replaceAll("\\s", "%20");
    }
}

//第二种
public class Solution {
    public String replaceSpace(StringBuffer str) {
    	String s=str.toString();
        char[] c=s.toCharArray();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<c.length;i++){
            if(c[i]==' '){
                sb.append("%20");
            }else{
                sb.append(c[i]);
            }
        }
       return sb.toString();
    }
}

3.从尾到头打印链表

题目描述


输入一个链表,从尾到头打印链表每个节点的值。 

输入描述:
输入为链表的表头
输出描述:
输出为需要打印的“新链表”的表头


解题思路:

第一种:

利用堆栈"先进后出"

第二种:

递归【!!!二刷的时候还不熟悉

判断当前结点是否为空,不空的话,递归调用该函数,不停地找next,到了尾节点,其next为空,此时,将尾节点添加进list中,递归开始往回了,不停地倒着加入节点


//第一种
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<Integer> stack=new Stack<Integer>();
        while(listNode!=null){
            stack.push(listNode.val);
            listNode=listNode.next;
        }
        ArrayList<Integer> arr=new ArrayList<Integer>();
        while(!stack.isEmpty()){
            arr.add(stack.pop());
        }
        return arr;
    }
}

//第二种
/**
*    public class ListNode {
*        int val;
*        ListNode next = null;
*
*        ListNode(int val) {
*            this.val = val;
*        }
*    }
*
*/
import java.util.ArrayList;
public class Solution {
    ArrayList<Integer> list=new ArrayList<Integer>();//在函数外面声明
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        if(listNode!=null){
            this.printListFromTailToHead(listNode.next);//用this调用
            list.add(listNode.val);
        }
        return list;
    }
}


4.重建二叉树

题目描述

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。

假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。


解题思路:

先判断这两种遍历数组是否为空,不要用pre==null,用pre.length==0

根据题目给出的前序遍历、后序遍历数组,
首先找出根节点,然后再根据中序遍历找到左子树和右子树的长度,
分别构造出左右子树的前序遍历和中序遍历序列,
最后分别对左右子树采取递归,递归跳出的条件是序列长度为1.

先序遍历第一个位置肯定是根节点node,
中序遍历的根节点位置在中间p,这样就可以知道左子树和右子树的长度

用Arrays.copyOfRange(int[] ,int from,int to)【取不到to】

递归递归【递归递归!!二刷的时候还是不够熟练】


/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.*;


public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre.length==0||in.length==0){
            return null;
        }
        TreeNode node=new TreeNode(pre[0]);
        for(int i=0;i<in.length;i++){
            if(pre[0]==in[i]){
                node.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i));
                node.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length));
            }
        }
        return node;
    }
}


5.用两个栈实现队列

题目描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

解题思路:

这个题之前整理过,主要注意的是,一个栈负责压入,一个栈负责弹出

弹出时,那个栈要保证里面是空的,从压入栈中压入后,弹出

压入时,也要保证压入栈里没有东西,这两点一样,【弹出压入这两点一样,只要实现一个就行,全都放在弹出那】

注意到要将int类型转为Integer类型!!!

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    
    public void push(int node) {
        stack1.push(new Integer(node));//注意到要将int类型转为Integer类型//不一定用转换,直接也行
    }
    
    public int pop() {
        if(stack2.isEmpty()){//判断栈空不空,用isEmpty()!!!
            while(!stack1.isEmpty()){
            	stack2.push(stack1.pop());
        	}
        }
        
        return stack2.pop().intValue();//注意到要将Integer类型转为int类型//不一定用转换,直接也行
    
    }
}


6.旋转数组的最小数字【有点疑惑】

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

【其实这个题就是求数组中最小元素,只是因为它自身的特性可以减小时间复杂度来求】

解题思路:

根据题意说明是一个递增数组的旋转,所以如题所示【3,4,5】,【1,2】还是局部递增的,

在这种的数组中查找,一般选择二分的方法;

基本模型有了,下面试着分析:
1.先取出中间的数值,和最后一个比较5>2 说明mid之前的某些部分旋转到了后面,

所以下次寻找 low = mid+1 开始;

2.取出的中间值要是小于high,说明mid-high之间都应为被旋转的部分,所以最小应该在mid的前面,

但是也有可能当前的mid 就是最小的值 所以下次需找的应该 从mid开始,也即high = mid 开始

3.当*mid == *high的时候,说明数组中存在着相等的数值,

可能是这样的形式 【2,2,2,2,1,2】所以应该选择的high 应该递减1 作为下次寻找的上界。


import java.util.ArrayList;

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int low=0;
        int high=array.length-1;
        
        while(low<high){
            int mid=low+(high-low)/2;//最好用这种,不用(high+low)/2
            if(array[mid]>array[high]){
                low=mid+1;
            }else if(array[mid]<array[high]){
                high=mid;
            }else {
                high=high-1;
            }
        }
        return array[low];//返回最小
    
    }
}


7.斐波那契数列

现在要求输入一个整数n,请你输出斐波那契数列的第n项。
n<=39

解题思路:

迭代方法,用两个变量记录fn-1和fn-2
还有P.S.  f(n) = f(n-1) + f(n-2),第一眼看就是递归啊,简直完美的递归环境

if(n<=1) 
    return n;
else return Fibonacci(n-1)+Fibonacci(n-2);


但是不要用递归!!

public class Solution {
    public int Fibonacci(int n) {
        if(n<2){
            return n;
        }
        int numfn1=0;
        int numfn2=1;
        int current=0;
        for(int i=2;i<=n;i++){
            current=numfn1+numfn2;
            numfn1=numfn2;
            numfn2=current;
        }
        return current;

    }
}

8.跳台阶

题目描述

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

解题思路:

f(n) = f(n-1) + f(n-2)同上同上

最后一次可能跳一阶,可能跳两阶,


public class Solution {
    public int JumpFloor(int target) {
        if(target<=2){
            return target;
        }
        int f1=2;// 当前台阶后退一阶的台阶的跳法总数(初始值当前台阶是第3阶)
        int f2=1;// 当前台阶后退二阶的台阶的跳法总数(初始值当前台阶是第3阶)
        int current=0;
        for(int i=3;i<=target;i++){
            current=f1+f2;
            f2=f1;//后退一阶在下一次迭代变为后退两阶
            f1=current;// 当前台阶在下一次迭代变为后退一阶
        }
        return current;

    }
}


9.变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。

求该青蛙跳上一个n级的台阶总共有多少种跳法。


题目解析:

重要的是分析化简,,,

关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:
f(1) = 1
f(2) = f(2-1) + f(2-2)         //f(2-2) 表示2阶一次跳2阶的次数。
f(3) = f(3-1) + f(3-2) + f(3-3) 
...
f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n) 
 
说明: 
1)这里的f(n) 代表的是n个台阶有一次1,2,...n阶的 跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
3) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2) 
4) n = 3时,会有三种跳得方式,1阶、2阶、3阶,
    那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)
    因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
5) n = n时,会有n中跳的方式,1阶、2阶...n阶,得出结论:
    f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1)
6) 由以上已经是一种结论,但是为了简单,我们可以继续简化:
    f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2)
    f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1)
    可以得出:
    f(n) = 2*f(n-1)
    
7) 得出最终结论,在n阶台阶,一次有1、2、...n阶的跳的方式时,总得跳法为:
              | 1       ,(n=0 ) 
f(n) =     | 1       ,(n=1 )
              | 2*f(n-1),(n>=2)

【注!!】

也可以看成,最后一步走1,2,3,n阶

则f(n)=f(n-1)+f(n-2)+....+f(n-n);

而f(n-1)刚好等于f(n-2)+...f(n-n)

所以得出f(n)=2*f(n-1)

public class Solution {
    public int JumpFloorII(int target) {
        if (target <= 0) {
            return -1;
        } else if (target == 1) {
            return 1;
        } else {
            return 2 * JumpFloorII(target - 1);
        }
    }
}



10. 矩形覆盖

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。

请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?


解题思路

依旧是斐波那契数列
2*n的大矩形,和n个2*1的小矩形
其中target*2为大矩阵的大小
有以下几种情形:
1、target = 1大矩形为2*1,只有一种摆放方法,return1;
2、target = 2 大矩形为2*2,有两种摆放方法,return2;
3、target = n 分为两步考虑:

如果第一格竖着放,只占一个格,还剩n-1格  f(target-1)种方法

如果前两格横着放两个,占两个格,还剩n-2格  f(target-2)种方法

public class Solution {  
    public int RectCover(int target) {  
        if(target==0||target==1||target==2){  
            return target;  
        }else if(target<0){return -1;}  
        else{  
            return RectCover(target-1)+RectCover(target-2);  
        }  
  
    }  
}  

11.二进制中1的个数
题目描述

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

解题思路:

第一种:

如果一个整数不为0,那么这个整数至少有一位是1。

如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,

原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。

举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。

减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.

我们发现减1的结果是把最右边的一个1开始的所有位都取反了。

这个时候如果我们再把原来的整数和减去1之后的结果做与运算,

从原来整数最右边一个1那一位开始所有位都会变成0。

如1100&1011=1000.

也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.

那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。


第二种:

Java自带的函数

Java.lang.Integer.bitCount()方法

统计参数i转成2进制后有多少个1

【要不是这道题还真不知道这个函数呢】


第三种:

把这个数逐次 右移 然后和1 与,
就得到最低位的情况,其他位都为0,
如果最低位是0和1与 之后依旧 是0,如果是1,与之后还是1。
对于32位的整数 这样移动32次 就记录了这个数二进制中1的个数了 


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

public class Solution {
public int  NumberOf1(int n) {
         return Integer.bitCount(n);
     }
}


//这个方法也是很6的
public class Solution {
    public int NumberOf1(int n) {
        return Integer.toBinaryString(n).replaceAll("0","").length(); }
}


public class Solution {
public int  NumberOf1(int n) {
    int count=0;
    for(int i=0;i<32;i++){
        if((n>>i&1)==1){//!!!注意这里用i标记,每次循环,就重新右移i位
            count++;
        }
    }
    return count;
     }
}

12.数值的整数次方

题目描述

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


解题思路:

第一种:

咳咳,Java自带的函数~

第二种:

算法的本质就是模拟数学规律,我们可以先模拟一下幂运算就是乘法的连乘,那么用算法写出来,然后再考虑几个测试用例的极端情况,如exponent==0或者exponent<0的情况,然后按逻辑写出你的代码


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

public class Solution {
    public double Power(double base, int exponent) {
        if(exponent==0){
            return 1;
        }else if(exponent>0){
            double num=base;
            for(int i=1;i<exponent;i++){
                num=num*base;
            }
            return num;
        }else{
            double num2=base;
            int flag=-exponent;
            for(int i=1;i<flag;i++){
                num2=num2*base;
            }
            num2=1/num2;
            return num2;
        }
        
  }
}

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

题目描述

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

解题思路:

第一种:

空间换时间,创建一个数组,奇数从头放,偶数从奇数个数的末尾开始放

或者,直接两个数组

第二种:

在一个数组中,类似冒泡算法,前偶后奇数就交换


import java.util.*;
public class Solution {
    public void reOrderArray(int [] array) {
        ArrayList<Integer> odd=new ArrayList<Integer>();//奇数
        ArrayList<Integer> even=new ArrayList<Integer>();//偶数
        
        for(int i=0;i<array.length;i++){
            if(array[i]%2==0){
                even.add(array[i]);
            }else{
                odd.add(array[i]);
            }
        }
        for(int i=0;i<odd.size();i++){
            array[i]=odd.get(i);
        }
        for(int i=odd.size();i<array.length;i++){
            array[i]=even.get(i-odd.size());
        }
    }
}

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 temp=array[j];
                    array[j]=array[j-1];
                    array[j-1]=temp;
                }
            }
        }
    }
}


14.链表中倒数第k个结点

题目描述

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

解题思路:

两个指针,先让第一个指针和第二个指针都指向头结点,然后再让第一个指针走(k-1)步,到达第k个节点。然后两个指针同时往末尾移动,当第一个结点到达末尾的时候,第二个

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值