剑指offer算法分析

1.二维数组中的查找:

  二维数组,每行都从左往右递增,每列都从上往下递增。现在该数组中查找是否有指定的数。

   解析:首先想到递归思想、二分查找思想。接着,由于左上角或右下角的数据类似于二分查找的中位数,故可以以此为切入点,不断切割二维数组,最终得到查找结果。



  2.替换空格:

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

     解析:1.replaceAll(" ","%20")。

                2.StringBuilder();



3.从尾到头打印链表:

     输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

    解析:1.stack()    stack.push()  stack.pop();



4.重建二叉树:

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

  解析:可以通过前序遍历的结果,将二叉树分成左右子树,进而使用递归思想,返回根节点的左右节点。



5.用两个栈实现队列:

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

  解析:push()时只操作第一个栈,pop时把第一个栈元素压入第二个栈,删除后在把元素还回第一个栈。



6.旋转数组的最小数字:

    输入一个非减排序数组的一个旋转,输出该旋转数组的最小元素。

  解析:找断点,最小的数字为该断点。



7.斐波那契数列:

输出斐波那契数列的第n项。

解析:递归,F(n)=F(n-1)+F(n-2)



8.跳台阶:   

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

解析:斐波那契数列原型。



9.变态跳台阶:

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

解析:F(n)=F(n-1)+F(n-2)+...+F(1)



10.矩形覆盖:

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

解析:递归,F(n)=F(n-1)+F(n-2)



11.二进制中1的个数:

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

解析:按位与操作,n&(n-1)能够消除二进制n中最右边的1,while循环直到结果为0。



12.数值的整数次方:

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

解析:方法一 递归 ,F(n) =F(n/2)*F(n/2)。注意return条件,n为奇数{1.n>0,F(n) =F(n/2)*F(n/2)*base,

2.n<0,F(n) =F(n/2)*F(n/2)/base}

方法二:二进制的与操作与移位操作。10^13。将13用二进制表示为1101。则10^13=10^1000*10^0100*10^0001。

  while(exponent!=0)   。。。。。。。。 13>>=1。 



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

    使得所有奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和偶数的位置不变。

解析:双指针,一个指向当前排序奇数的数组位置,一个指向偶数所到的位置,一次遍历,当两指针重合结束,还存在偶数顺序完全倒序的问题,需要将右边偶数转变顺序。



14.链表中倒数第k个节点:

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

解析:设立两个节点来遍历,第二个节点与第一个节点间隔k



27.字符串的排列:

输入一个字符串,按字典序打印出该字符串中字符的所有排列

解析:1.先对字符串中的字符进行排序。2.递归不断减少字符串的个数。



28.数组中出现次数超过一半的数字

有一个数字超过一半,请找出这个数字,如果不存在输出0

解析:一个指针,一次遍历,遇到不同数字减一,遇到相同数字加一,当计数器为0就换指针指的下个数重新计数。



29.最小的K个数

输入n个整数,找出其中最小的K个数。

解析:排序



30.连续子数组的最大和

如果向量中包含负数,是否应该包含某个负数,例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8.

解析:不断累加,不断更新cmp值,小于0则重新开始,因为小于0的子向量对后面的序列毫无益处。



31.整数中1出现的次数

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?

解析:数学问题,找出规律,以个位为一个台阶、十位为一个台阶、百位为一个台阶、千位为一个台阶。



32.把数组排成最小的数

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

解析:每两个数进行比较,写一个比较函数,比较函数为两个数从高位开始比较,数大的排在后面,如果相等,移一位继续比较,如果一个数没了位数,把最后一位和第二个数的移位比较。对数组进行重新排序,然后组合。



33.丑数

把只包含质因子2、3和5的数称作丑数。例如6、8都是丑数,但14不是,因为它包含质因子7。习惯上我们把1当作是第一个丑数。求按从小到大的顺序的第N个丑数。

解析:维护2、3、5的三个队列。变种为三个指针,一开始都指向1,分别乘以2、3、5,2最小,维护2的指针+1,指向2,继续将三个指针指向数乘以2、3、5进行比较移位。



34.第一个只出现一次的字符

在一个字符串中找到第一个只出现一次的字符,并返回它的位置,如果没有则返回-1。

解析:维护一个256的int型数组,数组的下标对应字符的ascii码,一次遍历str后查找数组中最小的坐标。



35.数组中的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

 



36.两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

解析:有公共节点的话,公共节点之后的节点全部相同。遍历完成之后回溯。可以用两个栈实现



37.数字在排序数组中出现的次数

统计一个数字在排序数组中出现的次数。

解析:二分?找到一个点,然后向两边扩展。



38.二叉数的深度

输入一颗二叉树,求该树的深度。从根节点到叶结点依次经过的节点形成树的一条路径,最长路径的长度为树的深度。

解析:深度优先遍历。

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

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

    }

}
*/
import java.util.ArrayList;
public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root==null)
            return 0;
         ArrayList<Integer> path = new ArrayList<>();
	      /*int count =0;*/
	      
	     helpDepth(root,path,1);
	        
           int result =0;
          for(Integer i:path){
              if(i>result)
                  result =i;
          }
	      return result;
        
    }
    	 public void helpDepth(TreeNode root,ArrayList<Integer> path,int count){
		 
		 if(root==null)
			 return;
		 
		 if(root.left==null&&root.right==null){
			 path.add(count);
			 return;
		 }
		 
		 helpDepth(root.left, path, count+1);
		 helpDepth(root.right, path, count+1);
		 
	 }
}


39.平衡二叉树

输入一棵二叉树,判断该二叉树是否是平衡二叉树

解析:1..树的深度用深度优先遍历出所有路径长度,然后判断所有长度的最小值和最大值是否差值大于1。



40.数组中只出现一次的数字

一个整形数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。(考察异或逻辑)

解析:数组中的所有数字进行异或运算,结果等于两个只出现一次的数字异或的结果,找出这个结果中左边二进制第一个1的位置,然后将该数与数组进行&操作,形成两个子数组。

import java.util.ArrayList;
public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
         int cmp = array[0];
		 for(int i=1;i<array.length;i++){
			 cmp = cmp^array[i];
		 }
		 
		 int two =cmp;
		 int one=0;
		 int j =1;
		 while(one==0){
			one =j&two;
			j=j*2;
		 }
		 j=j/2;
		 
		 ArrayList<Integer> arr1 = new ArrayList<>();
		 ArrayList<Integer> arr2 = new ArrayList<>();
		 
		 for(int k=0;k<array.length;k++){
			 if((array[k]&j)==0){
				 arr1.add(array[k]);
			 }else
				 arr2.add(array[k]);
		 }
		 
		 num1[0]=arr1.get(0);
		 for(int p=1;p<arr1.size();p++){
			 num1[0]=num1[0]^arr1.get(p);
         }
		 
		 num2[0]=arr2.get(0);
		 for(int q=1;q<arr2.size();q++){
			 num2[0]=num2[0]^arr2.get(q);
		 }
        
    }
}


41.和为S的连续正数序列

找出所有和为S的连续正数序列?

解析:维护两个指针,一个指向左边界,一个指向右边界,不断右移该滑动窗口。



42.和为S的两个数字

输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。

解析:两个指针,一个指向头,一个指向尾部,找到两个数。

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
         int lo =0;
        int hi =array.length-1;
        ArrayList<Integer> res = new ArrayList<>();
        
        if(hi==-1)
            return res;
        while(lo!=hi){
        	if(sum>array[lo]+array[hi]){
        		lo++;
        	}else if(sum<array[lo]+array[hi]){
        		hi--;
        	}else{
        		res.add(array[lo]);
        		res.add(array[hi]);
        		break;
        	}
        }
    
        return res;
    }
}


43.左旋转字符串

解析:没难度。



44.翻转单词顺序列

解析: str.trim()   



45.扑克牌顺子

解析:排序 然后看他们之间的间隔数是否小于0(大小王)的个数。



46.孩子们的游戏(圆圈中最后剩下的数)

解析:两种方法:1.维护一个链表,每个节点包含index和boolean,标志是否访问过。2.维护一个boolean数组,循环K次。



47.求1+2+3+...+n

要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句。

解析:递归



48.不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解析:二进制运算。&算进位,^算不带进位的加法。

public class Solution {
    public int Add(int num1,int num2) {
        
        int with = num1&num2;
            with<<=1;
        int or = num1^num2;
        while(with!=0){
          int temp1 = or^with;
            with = or&with;
            with<<=1;
            or = temp1;
        }
        return or;
    }
}


49.把字符串转换成整数

解析: '9'-'0'=9;字符的比较。



50.数组中重复的数字

解析:维护一个长度相同n的数组,一次遍历原数组记录所有数出现的次数,再遍历该维护的数组。



51.构建乘积数组

给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

解析:上下三角,对角线不算。



52.正则表达式匹配

解析:KMP算法

import java.util.regex.Pattern;
public class Solution {
    //状态机
    public boolean match(char[] str, char[] pattern)
    {
     String str1 = new String(str);
     String patter = new String(pattern);
        
     boolean res= Pattern.matches(patter,str1);
        return res;
    }
}


53.表示数值的字符串

实现一个函数用来判断字符串是否表示数值。

解析:正则表达式:str.matches(" ");               string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");



54.字符流中第一个不重复的字符

解析:维护一个256的int数组,存它的坐标。



55.链表中环的入口节点

给一个链表,若其中包含环,请找出该链表的环的入口节点,否则,输出null。

解析:第一个相同的节点。==比较对象,equals比较内容。



56.删除链表中重复的节点

在一个排序的链表中,存在重复的节点,删除重复的节点,返回链表头指针。

解析:从左往右遍历,维护一个count指针,遇到重复的数+1,不同数清零。



57.二叉树的下一个结点

解析:考察中序遍历。



58.对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

解析:递归实现,左序左节点==右序右节点。有点精妙!!!!!因为其实每次递归,递归调用的函数形参都是关于二叉树对称的。

public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        if(pRoot==null)
            return true;
        
        return helpDefine(pRoot.left,pRoot.right);
        
    }
    public boolean helpDefine(TreeNode left,TreeNode right)
    {
        if(left==null&&right==null)
            return true;
        if(left==null)
            return false;
        if(right==null)
            return false;
        return helpDefine(left.left,right.right)&&helpDefine(left.right,right.left);
        
    }
    
}


59.按之字形顺序打印二叉树

请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。

解析:层序遍历。可以维护两个栈来实现。



60.把二叉树打印成多行

从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

解析:层序遍历。



61.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树。

解析:前序遍历,将其转化为string输出并解析成为二叉树。如何反序列化二叉树结果?

public class Solution {
    String Serialize(TreeNode root) {
        StringBuilder builder = new StringBuilder<>();
        
        helpSerialize(root,builder);
        
        return builder.toString();
              
  }
    public void helpSerialize(root,builder){
        if(root==null){
            builder.add("#,");
            return ;
        }
        builder.add(root.val+",");
        helpSerialize(root.left,builder);
        helpSerialize(root.right,builder);
        
    }
    
    TreeNode Deserialize(String str) {
        char []arr = str.split(",");
        
       
  }
}


62.二叉搜索树的第k个节点

给定一棵二叉搜索树,请找出其中的第k小的节点。

解析:前序遍历,回溯第k次为其值。

public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        TreeNode res =null;
        if(pRoot==null)
            return null;
      
        int count =0;
        
        helpNode(pRoot,res,count,k);
        
        return res;
        
        
    }
    public void helpNode(TreeNode pRoot,TreeNode res,int count,int k){
        
         if(pRoot==null)
            return ;
         if(res!=null)
            return ;
        
        helpNode(pRoot.left,res,count,k);
        count++;
        if(count==k){
            res = pRoot;
            return ;
        }
        helpNode(pRoot.right,res,count,k);
    }


}


63.数据流中的中位数

解析:申明一个成员变量。



64.滑动窗口的最大值

解析:没啥特点。



65.矩阵中的路径

解析:类似于深度优先遍历的思路。



66.机器人的运动范围

解析:这种类似于点的不可达问题很多都能向深度优先遍历上靠。

public class Solution {
    public int movingCount(int threshold, int rows, int cols)
    {
        int flag[][] = new int[rows][cols];
        
        helpCount(threshold,rows,cols,flag,0,0);
        
        int res =0;
        for(int i=0;i<rows;i++){
            for(int j =0;j<cols;j++){
                res = res+flag[i][j];
            }
        }
        return res;
    }
    
    public void helpCount(int threshold,int rows,int cols,int flag[][],int curx,int cury){
       
       if(curx<0||curx>=rows||cury<0||cury>=cols)
           return;
       if(flag[curx][cury]==1)
           return;
       
       int rex=0;
       int rey=0;
       int replx = curx;
       int reply = cury; 
       while( replx!=0){
           rex = rex+replx%10;
           replx = replx/10;
       }
       while(reply!=0){
           rey = rey+reply%10;
           reply = reply/10;
       }
       if(rex+rey>threshold)
           return;
        
        flag[curx][cury]=1;
        
        helpCount(threshold,rows,cols,flag,curx+1,cury);
        helpCount(threshold,rows,cols,flag,curx-1,cury);
        helpCount(threshold,rows,cols,flag,curx,cury+1);
        helpCount(threshold,rows,cols,flag,curx,cury-1);
        
        
        
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值