剑指offer答案Java版

文章目录

1.二维数组找指定元素:

从右上角开始找,比目标大,删除整列,比目标小,删除整行

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows=array.length;//表示行数
        int cols=array[0].length;//第一行元素个数,即列数
     
        int row=0;
        int col=cols-1;
        while(row<rows&&col>=0){
            if(array[row][col]==target){
                return true;
            }else if(array[row][col]>target){
                col--;
            }else row++;
        }
        return false;//如果没有在循环中经历return退出,则表示没找到target
    }
}

2.在原数组的基础上空格换%20

双指针,从后往前,ptr2指向新生成数组最后的元素,ptr1指向原数组的末尾。str中空格也算长度。

stringBuffer的方法setCharAt(int,char)

public class Solution {
    public String replaceSpace(StringBuffer str) {
        int blanknum=0;
        for(int i=0;i<str.length();i++){
            if(str.charAt(i)==' ')
                blanknum++;
        }
        int originalStrLen=str.length();
        int newStrLen=originalStrLen+blanknum*2;
        str.setLength(newStrLen);
        int ptr1=originalStrLen-1;
        int ptr2=newStrLen-1;
            while(ptr1>=0&&ptr2>ptr1){
                if(str.charAt(ptr1)==' '){
                    str.setCharAt(ptr2--,'0');
                    str.setCharAt(ptr2--,'2');
                    str.setCharAt(ptr2--,'%');
                }else{
                    str.setCharAt(ptr2--,str.charAt(ptr1));
                }
                ptr1--;
            }
        return str.toString();
    }
}

3.从后往前输出链表

用栈实现先进后出

import java.util.ArrayList;
import java.util.Stack;    
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Stack<Integer> st=new Stack<Integer>();
            ArrayList<Integer> result=new ArrayList<Integer>();
            while(listNode!=null){
                st.push(listNode.val);//自动装箱
                listNode=listNode.next;
            }
        while(!st.empty()){
            result.add(st.peek());//不取出
            st.pop();//弹出栈
        }
        return result;
        
    }
}

4.前序中序构造二叉树:

public class Solution {
    public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        int length=pre.length;
        return buildTree(pre,in,0,length-1,0,length-1);
        }
    public TreeNode buildTree(int [] pre,int [] in,int preleft,int preright,int inleft,int inright){
         if(preleft>preright) return null;
            int mid=inleft;//对右子树的中序左边界变化了
            while(mid<inright&&in[mid]!=pre[preleft]) mid++;//通过while找到中序中 
            TreeNode root=new TreeNode(in[mid]);
            root.left=buildTree(pre,in,preleft+1,preleft+mid-inleft,inleft,mid-1);
            root.right=buildTree(pre,in,preleft+mid-inleft+1,preright,mid+1,inright);
            return root;
    }
}

5.两个栈实现队列:

栈中后进来的元素弹出到另一个栈后,变成了底部的元素,也就是后来的元素88

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();  
    public void push(int node) {
        stack1.push(node);
    }
    public int pop() {
        if(stack2.size()==0){//只有在栈2为空,栈1不为空时,才能把栈1顶部的元素压入栈2中,否则则先弹出栈2的元素
            while(stack1.size()>0){
            stack2.push(stack1.pop());
        }
        }
         return stack2.pop();
    }
}

6.从前往后算斐波那契数列f(n)

public class Solution {
    public int Fibonacci(int n) {
        if(n<2){
            return n;
        }
        int frear=1;
        int ffront=0;
        int fn=0;
        for(int i=2;i<=n;i++){//共n-1次求和
            fn=frear+ffront;//f(1)+f(0)依次类推
            ffront=frear;
            frear=fn;
        }
        return fn;
    }
}

7.跳台阶:

起始值变化了,f(1)=1,f(2)=2

public class Solution {
    public int JumpFloor(int target) {
       if(target<3){
            return target;
        }
        int frear=2;
        int ffront=1;
        int fn=0;
        for(int i=3;i<=target;i++){
            fn=frear+ffront;
            ffront=frear;
            frear=fn;
        }
        return fn;
    }
}

8.依次可以从1步到n步:

f(n)=2^( n-1),根据 f(n)=f(n-1)+f(n-2)+……f(1) + 1,n>=2,做f(n-1)相减,得到f(n)为等比数列

public class Solution {
    public int JumpFloorII(int target) {
        int res=1;
        for(int i=1;i<target;i++){
            res=res*2;
        }
        return res;
    }
}

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

分两个指针,index1指向头,index2指向尾

public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int index1=0;
        int index2=array.length-1;
            int indexmid=index1;//处理旋转0个元素,已经排序的情况
            while(array[index1]>=array[index2]){
                if(index2-index1==1){//表示已经找到,index2的位置即最小的数字
                    indexmid=index2;
                    break;
                }
                indexmid=(index1+index2)/2;
                
                if(array[index1]==array[index2]&&array[indexmid]==array[index1]){
                    return mininorder(array,index1,index2);
                }
                if(array[indexmid]>=array[index1]){//分清楚mid在前后哪个区间内
                    index1=indexmid;
                }else if(array[indexmid]<=array[index2]){
                    index2=indexmid;
                }
            }
    return array[indexmid];//找到最小下标后,统一返回
    }
    //处理两侧和中间相等的情况,顺序查找
    public int mininorder(int [] array,int index1,int index2){
        int min=array[index1];
        for(int i=index1+1;i<=index2;i++){
            if(array[i]<min){
                min=array[i];
            }
        }
        return min;
    }
}

10.机器人的运动范围:

回溯法格式:
方法名:
{递归终止条件
访问到,设置该位被访问过
递归方法(到达下一个位置,该位置的情况等价于前一个位置)
}

public int movingCount(int threshold, int rows, int cols)
    {
        boolean[][] visited=new boolean[rows][cols];//默认为false
        return countingsteps(threshold,rows,cols,0,0,visited);
    }
    public int countingsteps(int threshold,int rows,int cols,int row,int col,boolean [][] visited){
        
        if (row < 0 || row >= rows || col < 0 || col >= cols
                || visited[row][col] || digitsum(row,col) > threshold)  return 0;//不能走到,所以该行,列的步数为0
        visited[row][col]=true;//能访问到
        return 1+countingsteps(threshold,rows,cols,row+1,col,visited)
                +countingsteps(threshold,rows,cols,row-1,col,visited)
                +countingsteps(threshold,rows,cols,row,col-1,visited)
                +countingsteps(threshold,rows,cols,row,col+1,visited);
        
    }
    public int digitsum(int row,int col){
        int sum=0;
        while(row>0){
            sum=sum+row%10;
            row=row/10;
        }
        while(col>0){
            sum=sum+col%10;
            col=col/10;
        }
        return sum;
    }

11.剪绳子f(n)的最大分段长度乘积:

动态规划,求最大值可以分解为子问题的最大值,各子问题的解有重复,自下而上求解,自上而下分析

public class Solution {
    public int cutRope(int target) {
        if(target==2){
            return 1;
        }
        if(target==3){
            return 2;
        }//对特殊情况处理
        int [] products=new int[target+1];
        products[0]=0;//数组元素从0开始
        products[1]=1;
        products[2]=2;
        products[3]=3;//对4以上只切一刀,1-3的长度不切
        
        for(int i=4;i<=target;i++){
            int max=0;
            for(int j=1;j<=i/2;j++){
                int temp=products[j]*products[i-j];//分解为子问题
                if(max<temp){
                    max=temp;
                }
            }
            products[i]=max;
        }
         return products[target];
    }
}
贪心算法:n>5,尽可能分成3为一块的,n=4,分成2*2

if(to2==0){
            return to3*3;
        }else if(to3==0){
            return to2*2;
        }else
        {return to3*3*to2*2;}等价于 return (int)(Math.pow(3,to3))*(int)(Math.pow(2,to2));
完整:

public int cutRope(int target) {
        if(target==2){
            return 1;
        }
        if(target==3){
            return 2;
        }
        int to3=target/3;
        if(target-to3*3==1){//处理最后为剩余长度为4的情况
            to3-=1;
        }
         int to2=(target-to3*3)/2;
       return (int)(Math.pow(3,to3))*(int)(Math.pow(2,to2));
    }

12.位运算,一个数减1与它求与运算,就相当于把这个数最右边的一个1变成0,统计二进制中有多少个1,就是求可以进行多少次这样的运算

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

13.数值的整数次方

public double Power(double base, int exponent) {
        if((int)base==0&&exponent<0) return 0.0;//对底为0,指数为负的错误情况处理
        int absExponent=exponent;
        if(absExponent<0) absExponent=(-absExponent);//对负数的指数取绝对值
        double result=getResult(base,absExponent);//得到次方结果
        if(exponent<0) result=1.0/result;//负数次方取倒数
        return result;  
  }
    public double getResult(double base,int exponent){//对指数开方,递归实现
        if(exponent==0) return 1;
        if(exponent==1) return base;
        
        double result=getResult(base,exponent>>1);//指数除以2
        result*=result;
        if((exponent&0x1)==1){//判断是否是奇数
            result=result*base;
        }
        return result;
    }

14.打印从1到最大的n位数:

全排列输出所有整数,递归对第一位从0到9,然后0的时候到第二位从0到9,依次到下一位,递归终止条件数组长度为n,输出字符型数组

public void print1toMax(int n){
        if(n<=0) return;
        char[] num=new char[n];
        for(int i=0;i<10;i++){
            num[0]=i+'0';//数字变字符
            recur(num,n,0);
        }
    }
    public void recur(char[] num,int length,int index){
        if(index=length-1){//终止条件
            printNum(num);
            return;
        }
        for(int i=0;i<10;i++){
            num[index+1]=i+'0';//对每一位下位递归
            recur(num,length,index+1);
        }
    }
    public void printNum(char[] num){
        boolean beginis0=true;//以0开头
        int nlength=strlen(num);
        for(int i=0;i<nlength;i++){
            if(beginis0&&num[i]!=0){//第一个不为0开头,后面的数都输出,该步判断为false
                beginis0=false;
            }
            if(!beginis0){//第一个不为0的数以后都直接从此输出
                system.out.print(num[i]);
            }
        }
}

15. 删除链表中重复的结点

public ListNode deleteDuplication(ListNode pHead)
    {
       if(pHead==null||pHead.next==null) return pHead;
        ListNode Head=new ListNode(0);
        Head.next=pHead;
        ListNode pre=Head;
        ListNode last=Head.next;
        while(last!=null){
                if(last.next!=null&&last.next.val==last.val){
                while(last.next!=null&&last.next.val==last.val){
                    last=last.next;
                }
                    pre.next=last.next;
                    last=last.next;
                }else{
                    pre=last;
                    last=last.next;
                }
            }
          return Head.next;//pHead???
        }

16.正则表达式匹配:

当数组【下标】==非空字符时,需要先判断下标是否越界

public boolean match(char[] str, char[] pattern)
    {
        if(str==null||pattern==null) return false;
        int stringindex=0;//用下标来表示数组元素起始的位置
        int patternindex=0;
        return corematch(str,pattern,stringindex,patternindex);
    }
    public boolean corematch(char[] str,char[] pattern,int stringindex,int patternindex){
        if(stringindex==str.length&&patternindex==pattern.length){
            return true;//都到达末尾匹配成功
        }
        if(stringindex!=str.length&&patternindex==pattern.length){
            return false;//模式先结束,匹配失败
        }
           
        if(patternindex+1<pattern.length&&pattern[patternindex+1]=='*'){//判断数组是否越界,对第二个字符是*进行分类处理
            if((stringindex!=str.length&&pattern[patternindex]==str[stringindex])||(pattern[patternindex]=='.'&&stringindex!=str.length)){
                return corematch(str,pattern,stringindex+1,patternindex+2)//使用一次*前的字符
                    ||corematch(str,pattern,stringindex+1,patternindex)//再次使用*前的字符
                    ||corematch(str,pattern,stringindex,patternindex+2);//使用0次*前的字符
            }else{
                return corematch(str,pattern,stringindex,patternindex+2);//*前的字符与字符串的第一个字符不匹配,到*后的字符
            }
        }
        if(stringindex!=str.length&&pattern[patternindex]==str[stringindex]||pattern[patternindex]=='.'&&stringindex!=str.length){//对str的下标进行判断,因为已经排除了pattern已经末尾的情况,不需要再对patternindex再判断
            return corematch(str,pattern,stringindex+1,patternindex+1);
        }
        return false;
    }

17表示数值的字符串

 public int index=0;//两个方法之外的全局变量
    public boolean isNumeric(char[] str) {
        if(str.length==0) return false;
        boolean flag=scanInteger(str);//先对.左边的整数扫描
        
        if(index<str.length&&str[index]=='.'){//.右边的数时无符号整数,为了使index增加,使scan表达式写在或的前面,.左右有无数字都可以
            index++;
            flag=scanUnsignedInteger(str)||flag;
        }
        if(index<str.length&&(str[index]=='e'||str[index]=='E')){
            index++;
            flag=flag&&scanInteger(str);//e后面必须是整数,e前面必须有数字
        }
        return flag&&index==str.length;//字符必须到字符串的我为
    }
  
    public boolean scanInteger(char[] str){
        if(index<str.length&&(str[index]=='+'||str[index]=='-')){
            index++;
        }
        return scanUnsignedInteger(str);
    }
      public boolean scanUnsignedInteger(char[] str){
        int start=index;
        while(index<str.length&&str[index]>='0'&&str[index]<='9'){
            index++;
        }
          return index>start;//有数字,返回true5
    }

18.调整数组顺序,使得奇数在偶数前面

public void reOrderArray(int [] array) {
        if(array.length==0) return;
        int begin=0;
        int end=array.length-1;
        while(begin<end){
            while(begin<end&&begin<array.length&&((array[begin]&0x1)!=0)){找偶数
                begin++;
            }
            while(begin<end&&end<array.length&&((array[end]&0x1)==0)){找奇数
                end++;
            }
           if(begin<end&&begin<array.length&&end<array.length){
            int temp=array[begin];
            array[begin]=array[end];
            array[end]=temp;
        }  
        }
    }
    public boolean isEven(int x){可扩展判断函数
        return (x&1)==0;
    }
方法二:类似冒泡排序,每次都保证第一个是奇数,
public void reOrderArray(int [] array) {
        if(array.length==0) return;
        for(int i=0;i<array.length;i++){
            for(int j=array.length-1;j>i;j--){
                if((array[j]&0x1)==1&&(array[j-1]&0x1)==0){
                    int temp=array[j-1];
                    array[j-1]=array[j];
                    array[j]=temp;
                }
            }
        }
}

19.倒数第k个节点

  public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null||k==0) return null;//倒数0个返回null
        ListNode ahead=head;
        ListNode behind=null;
        
        for(int i=0;i<k-1;i++){//第一个先走k-1步
            if(ahead.next!=null){
                ahead=ahead.next;
            }else{
                return null;
            }
        }
        behind=head;
        while(ahead.next!=null){//从k步开始同时走,最后第二个位于n-k+1,就是倒数第k个
            ahead=ahead.next;
            behind=behind.next;
        }
        return behind;
    }

20.链表的入口节点:

用过的节点尽量在一个函数中再次利用,降低创建新节点的时间,降低时间复杂度

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        ListNode meetNode=MeetNode(pHead);
        if(meetNode==null) return null;
        
        int nodeNum=1;
        ListNode p1=meetNode;
        while(p1.next!=meetNode){//统计环中节点数量
            p1=p1.next;
            nodeNum++;
        }
        p1=pHead;
        for(int i=0;i<nodeNum;i++){找到p1初始位置,头结点往后走环中节点数目的步数
            p1=p1.next;
        }
        ListNode p2=pHead;
        while(p1!=p2){
            p1=p1.next;
            p2=p2.next;
        }
        return p1;
    }
    public ListNode MeetNode(ListNode pHead){//慢节点位于头结点的后一个节点,快节点位于头结点的后两个节点,相遇节点在环中
        if(pHead==null) return null;
        ListNode slow=pHead.next;
        if(slow==null) return null;
        ListNode fast=slow.next;
        while(slow!=null&&fast!=null){
            if(slow==fast) return slow;
            slow=slow.next;
            fast=fast.next;
            if(fast!=null){
                fast=fast.next;
            }
        }
        return null;
    }

21.反转链表

public ListNode ReverseList(ListNode head) {
        ListNode reversedHead=null;
        ListNode pNode=head;
        ListNode pPre=null;
        while(pNode!=null){//涉及到一个节点的下一个节点时,首先判断这个节点是不是null
            ListNode pnext=pNode.next;//先保存该节点的下一个节点
            if(pnext==null){
                reversedHead=pNode;
            }
            pNode.next=pPre;//反转链表方向
            pPre=pNode;//下一个节点的的pre要改变
            pNode=pnext;//指向下一个节点
        }
        return reversedHead;
    }

22.合并两个排序的列表,每次到下一个任务与前一个相同,递归

public ListNode Merge(ListNode list1,ListNode list2) {
        if(list1==null){//任意一个列表为空,返回另外一个list
            return list2;
        }else if(list2==null){
            return list1;
        }
        
        ListNode mergeNode=null;
        if(list1.val<list2.val){
            mergeNode=list1;
            mergeNode.next=Merge(list1.next,list2);
        }else{
            mergeNode=list2;
            mergeNode.next=Merge(list1,list2.next);
        }
        return mergeNode;
    }

23.树的子结构:

1.找树A中与树B根节点相同的节点,2判断左右子树是否相等

public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        boolean result=false;
        if(root1!=null&&root2!=null){
            if(equal(root1.val,root2.val)){
                result=doesTree1hasTree2(root1,root2);
            }
            if(!result)
                result=HasSubtree(root1.left,root2);//树B的根节点不动,对树A的左右子节点分别去查找其是否有和B的根节点相同的点
            if(!result)
                result=HasSubtree(root1.right,root2);
        }
        return result;
    }
    public boolean doesTree1hasTree2(TreeNode root1,TreeNode root2){
        if(root2==null)//B遍历完,找到了
            return true;
        if(root1==null)//B没有遍历完,A先结束,没找到
            return false;
        if(!equal(root1.val,root2.val))//当值不相等,此子树的结构在A中以一个根节点来找则没找到,以上为终止条件
            return false;
        return doesTree1hasTree2(root1.left,root2.left)&&
            doesTree1hasTree2(root1.right,root2.right);//其左右节点是否是A中根节点左右子节点相同
    }
    public boolean equal(double val1,double val2){//对double类型判断相等的处理
        if((val1-val2)>-0.0000001&&(val1-val2)<0.0000001){
            return true;
        }else{
            return false;
        }
    }

24.树的镜像:

对根节点的左右节点进行替换,再对其左右子节点的分别左右子节点进行替换

public void Mirror(TreeNode root) {
        if(root==null) return;
        if(root.left==null&&root.right==null) return;//到达叶节点时,终止 
        TreeNode temp=root.left;
        root.left=root.right;
        root.right=temp;
        if(root.left!=null){
            Mirror(root.left);
        }if(root.right!=null){
            Mirror(root.right);
        }
    }

25.对称的二叉树:

根左右和根右左两种遍历相同时就为对称二叉树

boolean isSymmetrical(TreeNode pRoot)
    {
     if(pRoot==null) return true;//处理空树
     return isSymmetrical(pRoot.left,pRoot.right);
    }
    boolean isSymmetrical(TreeNode root1,TreeNode root2){
        if(root1==null&&root2==null) return true;
        if(root1==null||root2==null) return false;
        if(root1.val!=root2.val) return false;//两种遍历值不等是,false
        return isSymmetrical(root1.left,root2.right)&&//根左右和根右左,左和右对应
                isSymmetrical(root1.right,root2.left);
       }

26.顺时针打印矩阵:

每次打印一圈,起始点从(0,0)到(1,1)依次往后


public class Solution {
    public ArrayList<Integer> printMatrix(int [][] matrix) {
        ArrayList<Integer> result=new ArrayList<>();
        int rows=matrix.length;
        int cols=matrix[0].length;
        int start=0;
        while(rows>start*2&&cols>start*2){//每次顺时针打印一圈的条件
            printInCircle(matrix,result,rows,cols,start);
            start++;
    }
        return result;
    }
        public void printInCircle(int [][] matrix,ArrayList<Integer> result,int rows,int cols,int start){
            int endX=cols-start-1;//终止列下标
            int endY=rows-start-1;//终止行下标
            for(int i=start;i<=endX;i++){//从左到右打印一行
                result.add(matrix[start][i]);
            }
            if(endY>start){//从上到下打印一列全局
                for(int i=start+1;i<=endY;i++){
                    result.add(matrix[i][endX]);
                }
            }
            if(endX>start&&endY>start){//从右往左打印一行
                for(int i=endX-1;i>=start;i--){
                    result.add(matrix[endY][i]);
                }
            }
            if(endX>start&&start<endY-1){//从下到上打印一列
                for(int i=endY-1;i>start;i--){
                    result.add(matrix[i][start]);
                }
            }
        }

27.包含min函数的栈,dataList存放数据栈,minList存放最小值的栈,min变量记录最小值,push时最小栈每次都push当前最小的值,pop时,最小栈pop之后栈顶的元素仍然是数据栈中最小的元素

   private ArrayList<Integer> dataList=new ArrayList<>();
    private ArrayList<Integer> minList=new ArrayList<>();
    private Integer min=Integer.MAX_VALUE;
    
    public void push(int node) {
        dataList.add(node);
        if(min>node){
            minList.add(node);
            min=node;
        }else{
            minList.add(min);
        }
    }
    public int getSize(){
        return dataList.size();
    }
    public void pop() {
        int end=getSize()-1;
        dataList.remove(end);
        minList.remove(end);
        min=minList.get(end-1);
    }
   
    public int min() {
        return min;
    }

28.栈的压入,弹出序列:

每次把入栈序列插入栈时,比较栈顶元素是否和出栈序列的头元素相等,相等则弹出元素,弹出序列的下一个若和入栈序列的顶部又相等则继续弹出。否则则继续按入栈序列压入,直到栈顶元素等于出栈序列当前index的元素

public boolean IsPopOrder(int [] pushA,int [] popA) {
        if(pushA.length==0||popA.length==0){
            return false;
        }
        Stack<Integer> s=new Stack<>();
        int popIndex=0;
        for(int i=0;i<pushA.length;i++){
            s.push(pushA[i]);
            while(!s.isEmpty()&&s.peek()==popA[popIndex]){
                s.pop();
                popIndex++;
            }
        }
        return s.isEmpty();//当栈不为空,说明出栈的元素与栈顶的元素不等,即在栈顶之前,所以不是一个出栈序列
    }

29.从上到下打印二叉树:

广度优先搜索,用ArrayList模拟队列,remove(index),add在末尾

        ArrayList<Integer> result=new ArrayList<>();
        ArrayList<TreeNode> queue=new ArrayList<>();
    
        if(root==null){//处理空指针
            return result;
        }
        queue.add(root);
        while(queue.size()!=0){
            TreeNode temp=queue.remove(0);
            if(temp.left!=null){
                queue.add(temp.left);
            }
            if(temp.right!=null){
                queue.add(temp.right);
            }
            result.add(temp.val);
        }
        return result;
    }

30.二叉搜索树的后序遍历序列:

左右根,数组不变,变下标

  public boolean VerifySquenceOfBST(int [] sequence) {
        int len=sequence.length;
        boolean result=Verify(sequence,0,len-1);
        return result;
    }
    public boolean Verify(int [] sequence,int start,int end){
        if(sequence==null||sequence.length==0){
            return false;
        }
        int root=sequence[end];
        //找出左子树的下标i-1
        int i=0;
        for(;i<end;i++){
            if(sequence[i]>root)
                break;
        }
        //对右子树的值进行判断
        int j=i;
        for(;j<end;j++){
            if(sequence[j]<root)
                return false;
        }
               //对左子树进行递归判断
        boolean left=true;
        if(i>0){
              left=Verify(sequence,0,i-1);
        }
        //对右子树进行判断
        boolean right=true;
        if(i<end){
            right=Verify(sequence,i,end-1);
        }
        return (left&&right);
    }

31.二叉树中和为某一值的路径:

递归,首先是终止条件,然后判断结果,符合就加入,每次当前节点递归完,回到其父节点节点,list中去掉当前节点

    private ArrayList<ArrayList<Integer>> result=new ArrayList<>();
    private ArrayList<Integer> list=new ArrayList<>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        if(root==null) return result;
        list.add(root.val);
        target-=root.val;
        if(target==0&&root.left==null&&root.right==null){
            result.add(new ArrayList<Integer>(list));//list是引用类型,不创建新的,在回退过程中会使得list中的数值一个个被remove掉,最后数组里元素为空
        }
        FindPath(root.left,target);
        FindPath(root.right,target);
        list.remove(list.size()-1);
        return result;
    }

32.复杂链表的复制:

分三步,在x.next时,要判断x是否为空


public RandomListNode Clone(RandomListNode pHead)
    {
        if(pHead==null) return null;
              //第一步:复制每个节点
        RandomListNode pNode=pHead;
        while(pNode!=null){
            RandomListNode pCloned=new RandomListNode(pNode.label);
            pCloned.next=pNode.next;
            pNode.next=pCloned;
            pNode=pCloned.next;
        }
        //指定复制节点的随机指针
        pNode=pHead;
        while(pNode!=null){
            pNode.next.random=pNode.random==null? null:pNode.random.next;
            pNode=pNode.next.next;
        }
        //拆分链表,将偶数位置的节点重新组合在一起,返回
        pNode=pHead;
        RandomListNode pCloned=pNode.next;
        while(pNode!=null){
            RandomListNode ClonedNode=pNode.next;//用ClonedNode来当做复制链表的操作指针
            pNode.next=ClonedNode.next;
            ClonedNode.next=pNode.next==null? null:pNode.next.next;
            pNode=pNode.next;
        }
        return pCloned;//复制链表的需要返回的头指针不改变
    }

33.二叉搜索树与双向链表:

中序遍历,左根右

 TreeNode tail=null;//当前链表的最后节点
    TreeNode listHead=null;//双向链表的头结点
    public TreeNode Convert(TreeNode pRootOfTree){
        convertSub(pRootOfTree);
        return listHead;   
    }
    public void convertSub(TreeNode pRootOfTree){
        if(pRootOfTree==null) return;  
        convertSub(pRootOfTree.left);
        if(tail==null){//作初始赋值,都复制为二叉树中最小的节点
            tail=pRootOfTree;
            listHead=pRootOfTree;
        }else{
            tail.right=pRootOfTree;//对两个节点连接操作
            pRootOfTree.left=tail;
            tail=pRootOfTree;//将尾节点移至当前最大的节点
        }
         convertSub(pRootOfTree.right);
    }

33.序列化和反序列化二叉树:

都是前序遍历

    public int index=-1;
    String Serialize(TreeNode root) {
        StringBuffer sb=new StringBuffer();//对新的左右子树添加新的sb,要么是“#,”,要么是“val,”
        if(root==null){
            sb.append("#,");
            return sb.toString();
        }
        sb.append(root.val+",");
        sb.append(Serialize(root.left));
        sb.append(Serialize(root.right));
        return sb.toString();
  }
    TreeNode Deserialize(String str) {
        index++;
        String[] strr=str.split(",");
        TreeNode node=null;//用于index到“#”的情况,即空节点
        if(!strr[index].equals("#")){
            node=new TreeNode(Integer.valueOf(strr[index]));
            node.left=Deserialize(str);
            node.right=Deserialize(str);
        }
        return node;//当index到子字符串位置为#时,父节点的左右子节点为if前为null的node,最后作为根节点返回
  }

34.字符串的排列:

string(char 【】 ch)将字符数组转换为字符串

public ArrayList<String> Permutation(String str) {
       List<String> resultList=new ArrayList<>();
        if(str.length()==0){
            return (ArrayList)resultList;
        }
        fun(str.toCharArray(),resultList,0);
        Collections.sort(resultList);//对字符串List按字典表顺序排列
        return (ArrayList)resultList;//强制转换
    }
    public void fun(char[] ch,List<String> list,int i){
        if(i==ch.length-1){//当到ch的长度时,结果串添加
            if(!list.contains(new String(ch))){//判断结果中是否有重复
                list.add(new String(ch));
                return;
            }
        }else{//回溯法
           for(int j=i;j<ch.length;j++){
               swap(ch,i,j);
               fun(ch,list,i+1);//0-0,1-1,被收录,回到0-0(2),1-2,被收录,回到0-0(2),再回到0-0
               swap(ch,i,j);//回到交换前的状态
           }
        }
    }
    public void swap(char[] ch,int i,int j){
        if(i!=j){
            char t=ch[i];
            ch[i]=ch[j];
            ch[j]=t;
        }
    }

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

因为数组超过一半的数字比其他所有数字之和要多,times记录为当前数字超过其他数字之和的个数,只有当超过为0的时候,才设置为下一个数,并把次数设置为1,次数大于0的情况下不改变result,所以是最后设为1的数字。但是有可能最后设为1的数字没有超过数组长度的一半,所以需要检验。


  public int MoreThanHalfNum_Solution(int [] array) {
        if(CheckInvalidArray(array))//判断数组是否为空
            return 0;
        int result=array[0];
        int len=array.length;
        int times=1;
        for(int i=1;i<len;i++){
            if(times==0){//当次数为0,设置result为下一个
                result=array[i];
                times=1;
            }else if(array[i]==result){
                times++;
            }else{
                times--;
            }
        }
        if(!CheckMoreThanHalf(array,len,result))
            result=0;
        return result;
    }
    public boolean CheckInvalidArray(int [] array){
        if(array==null||array.length==0){
            return true;
        }
        return false;
    }
    public boolean CheckMoreThanHalf(int [] array,int len,int result){
        int times=0;
        for(int i=0;i<len;i++){
            if(array[i]==result){
                times++;
            }
        }
        if(times*2<=len){//判断是否超过数组长度的一半
            return false;
        }
        return true;
    }

36.最小的k个数:

用最大堆存储当前最小的k个数,当最大堆不满k个,就把遍历到的元素加入最大堆,当已经满k个,就把遍历到的数和最大堆的顶(最大的数)来比较

    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> result=new ArrayList<>();
        if(input==null||k>input.length||k==0){
        return result;
        }
        PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(k,new Comparator<Integer>(){
        
        @Override
        public int compare(Integer o1,Integer o2){//入堆的数按此规则(Comparator重写的方法)排序,最大的值在最大堆的顶部
        return o2.compareTo(o1);//o2比较o1,最大堆
        }
        });
        for(int i=0;i<input.length;i++){
        if(maxHeap.size()!=k){
        maxHeap.offer(input[i]);
        }else if(maxHeap.peek()>input[i]){
        Integer temp=maxHeap.poll();
        temp=null;
        maxHeap.offer(input[i]);
        }
        }
        for(Integer integer : maxHeap){
        result.add(integer);
        }
        return result;
    }

37.数据流中的中位数:

最大堆的值小于最小堆的值,使得中位数要么出现在最大堆顶和最小堆顶的平均值,要么就是最小堆的堆顶

    //最小堆
    private PriorityQueue<Integer> minHeap=new PriorityQueue<Integer>();
    //最大堆
    private PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(15,new Comparator<Integer>(){
        @Override
        public int compare(Integer o1,Integer o2){
        return o2.compareTo(o1);    
        }
    });

    int count=0;//记录目前已有的数个数的奇偶
    public void Insert(Integer num) {
    if(count%2==0){//当前个数为偶数,存入最大堆,并把最大堆的最大值给最小堆
        maxHeap.offer(num);
        int max=maxHeap.poll();
        minHeap.offer(max);
    }else{//当前个数为奇数,存入最小堆,并把最小堆的最小值给最大堆
        minHeap.offer(num);
        int min=minHeap.poll();
        maxHeap.offer(min);
    }
        count++;//数据流采入的数据个数+1
    }

    public Double GetMedian() {
        if(count%2==0){
            return (maxHeap.peek()+minHeap.peek())/2.0;
        }else{//奇数时,出现在最小堆的堆顶
            return minHeap.peek()/1.0;
        }
    }

38.连续子数组的最大和

    public int FindGreatestSumOfSubArray(int[] array) {
        if(array==null||(array.length==0)){
            return 0;
        }
        int curSum=0;
        int maxSum=0x80000000;//最小的值
        for(int i=0;i<array.length;i++){
            if(curSum<=0){//当前和为负,最大和肯定不要当前的负数
                curSum=array[i];
            }else{//当前和为正,把当前的数加上
                curSum+=array[i];
            }
            if(curSum>maxSum){
                maxSum=curSum;
            }
        }
        return maxSum;
    }

39.整数中1出现的次数:

i是置位点,把整数拆成两部分
若i为百位:
百位为>=2,百位上为1的个数有((a/10)+1)*100
若百位为1,百位上为1的个数有(a/10)*100+b+1
若百位为0,百位上为1的个数有(a/10)*100
这三种情况下((a/10)+1)||(a/10)=(a+8)/10

public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        int i=1;
       
        for(;i<=n;i*=10){
             int a=n/i;
             int b=n%i;
            if(a%10==1){
                count=count+((a+8)/10)*i+(b+1);
            }else{
                count=count+((a+8)/10)*i;
            }
        }
        return count;
    }

40.把数组排成最小的数(整型数组的数拆起来再组合最小的数)

    public String PrintMinNumber(int [] numbers) {
        int len=numbers.length;
        String result="";
        ArrayList<Integer> list=new ArrayList<>();
        
        for(int i=0;i<len;i++){
            list.add(numbers[i]);
        }
        Collections.sort(list,new Comparator<Integer>(){//内部快排,全部排
            @Override
            public int compare(Integer str1,Integer str2){//修改参数是Integer
                String s1=str1+""+str2;
                String s2=str2+""+str1;
                return s1.compareTo(s2);//按拼起来数字小的,按此顺序排
            }
        });
        for(int j:list){
            result+=j;//Integer+string组成string
        }
        return result;
    }

41.第n个丑数(只包含质因子2,3,5):

丑数乘以2,3,5仍然是丑数,每次都加入最小的丑数后,该下标++,防止丑数重复

    public int GetUglyNumber_Solution(int index) {
        if(index<=0){
             return 0;
        }
        int i2=0,i3=0,i5=0;
        ArrayList<Integer> list=new ArrayList<>();
        list.add(1);
        while(list.size()<index){
            int m2=list.get(i2)*2;
            int m3=list.get(i3)*3;
            int m5=list.get(i5)*5;
            int min=Math.min(m2,Math.min(m3,m5));
            list.add(min);
            if(min==m2) i2++;
            if(min==m3) i3++;
            if(min==m5) i5++;
        }
        return list.get(list.size()-1);
    }

42.第一个只出现一次的字符:

利用字母的ASCII码作hash作为数组的index,A-Z对应65-90,a-z对应97-112,中间6个不做处理

    public int FirstNotRepeatingChar(String str) {
        int[] words=new int[58];
        for(int i=0;i<str.length();i++){
            words[(int)str.charAt(i)-65]+=1;//记录该字母出现的次数
        }
        for(int i=0;i<str.length();i++){
            if(words[(int)str.charAt(i)-65]==1)
                return i;
        }
        return -1;
    }

43.数组中的逆序对:

归并排序的过程中来计算逆序对对数


int count;
    public int InversePairs(int [] array) {
        count=0;
        if(array!=null){
            mergeSortUp2Down(array,0,array.length-1);
        }
        return count;
    }
    public void mergeSortUp2Down(int [] a,int start,int end){//自上而下的归并排序
        if(start>=end)
            return;
        int mid=(start+end)>>1;//向左取中间值
        mergeSortUp2Down(a,start,mid);
        mergeSortUp2Down(a,mid+1,end);
        merge(a,start,mid,end);
    }
    public void merge(int [] a,int start,int mid,int end){//将相邻的两组合并
        int[] temp=new int[end-start+1];
        int i=start,j=mid+1,k=0;
        while(i<=mid&&j<=end){
            if(a[i]<=a[j]){
                temp[k++]=a[i++];
            }else{找到逆序对,i到mid的的数都可以和j组成逆序对(i到mid递增)
                temp[k++]=a[j++];
                count+=(mid+1-i);
                count%=1000000007;
            }
        }
        for(;i<=mid;i++){//将剩余的值复制到temp
            temp[k++]=a[i];
        }
        for(;j<=end;j++){
            temp[k++]=a[j];
        }
        for(int s=0;s<temp.length;s++){//将排序好的数组复制给原数组
            a[start+s]=temp[s];
        }
    }

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

将第一个链表节点依次存入hashmap中,containsKey是否包含键

public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode current1=pHead1;
        ListNode current2=pHead2;
        HashMap<ListNode,Integer> map=new HashMap<>();
        
        while(current1!=null){
        map.put(current1,null);
        current1=current1.next;
        }
        while(current2!=null){
        if(map.containsKey(current2)){
         return current2;
        }else{
         current2=current2.next;
        }
}
         return null;
}

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

排序数组-》二分查找

  public int GetNumberOfK(int [] array , int k) {
       int len=array.length;
        if(len==0){
            return 0;
        }
        
        int firstK=getFirstK(array,k,0,len-1);
        int lastK=getLastK(array,k,0,len-1);
        if(firstK!=-1&&lastK!=-1){
            return lastK-firstK+1;
        }
        return 0;
    }
//找出第一个k,递归写法
    public int getFirstK(int [] array,int k,int start,int end){
        if(start>end){
            return -1;
        }
        int mid=(start+end)>>1;
        if(array[mid]>k){
            return getFirstK(array,k,start,mid-1);
        }else if(array[mid]<k){
            return getFirstK(array,k,mid+1,end);
        }else if(mid-1>=0&&array[mid-1]==k){//更新end
            return getFirstK(array,k,start,mid-1);
        }else{
            return mid;
        }
    }
//循环写法while(),更新start,end边界,找最后一个k,更新start
    public int getLastK(int [] array,int k,int start,int end){
        int mid=(start+end)>>1;
        int len=array.length;
        while(start<=end){
            if(array[mid]>k){
                end=mid-1;
            }else if(array[mid]<k){
                start=mid+1;
            }else if(mid+1<len&&array[mid+1]==k){
                start=mid+1;
            }else{
                return mid;
            }
            mid=(start+end)>>1;
        }
        return -1;
    }

46.二叉树的深度

public int TreeDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        int left=TreeDepth(root.left);
        int right=TreeDepth(root.right);
        return Math.max(left,right)+1;//子树的深度加上父节点(根节点)的深度
    }

47.判断二叉树是否是平衡的:

对左右子树分别递归返回其深度,若已经出现不平衡则返回-1,回到父节点的方法时,直接返回-1,否则返回左右子树的最大深度+1

   public boolean IsBalanced_Solution(TreeNode root) {
        return getDepth(root)!=-1;
    }
    public int getDepth(TreeNode root){
        if(root==null) return 0;
        int left=getDepth(root.left);
        if(left==-1) return -1;
        int right=getDepth(root.right);
        if(right==-1) return -1;
        return Math.abs(left-right)>1? -1:(Math.max(left,right)+1);
    }

48.数组中只出现一次的数字:

数组中只有两个数字只出现了一次,其他数字都出现了两次,对所有数字进行排序

    ArrayList<Integer> list=new ArrayList<>();
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
       Arrays.sort(array);
       for(int i=0;i<array.length;i++){
       if(i+1<array.length&&array[i]==array[i+1]){//对遇到两个相等的数字时,指针+1,for循环指针+1,相当于到跳过了当前的两个相等的数字
       i++;
       }else{
       list.add(array[i]);
       }
       }
       if(list.size()==2){
       num1[0]=list.get(0);
       num2[0]=list.get(1);
       }
    }  

49.和为S的连续正数序列:

滑动窗口,先确定求和值,再根据和与sum的大小比较,确定边界范围,两个边界从左侧向右扩展

  public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
        ArrayList<ArrayList<Integer>> result=new ArrayList<>();
        int plow=1,phigh=2;
        while(plow<phigh){
            int cur=(plow+phigh)*(phigh-plow+1)/2;
            if(cur==sum){
                ArrayList<Integer> list=new ArrayList<>();
                for(int i=plow;i<=phigh;i++){
                    list.add(i);
                }
                result.add(list);
                plow++;
            }else if(cur<sum){
                phigh++;
            }else{
                plow++;
            }
        }
        return result;
    }

50.和为S的两个数字,输出从小到大的两个数=在返回的list中小的在前,大的在后。

和相同的情况下越两边的数相乘越小。两边夹逼。

  public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
        ArrayList<Integer> result=new ArrayList<>();
        if(array==null||array.length<2){
            return result;
        }
        int i=0,j=array.length-1;
        while(i<j){
            if(array[i]+array[j]==sum){
                result.add(array[i]);
                result.add(array[j]);
                i++;
                j--;
                return result;//找到第一组就返回
            }else if(array[i]+array[j]<sum){
                i++;
            }else{
                j--;
            }
        }
         return result;//若经过while循环没有返回,说明没有找到这样的两个数字,返回空list,即一开始创建的result
    }

51.左旋转字符串:

左移位n位,XY转换为为YX,(YX)=(XTYT)T,三次reverse


public String LeftRotateString(String str,int n) {
        char[] cha=str.toCharArray();//转换为char【】,以便翻转
        if(cha.length<n) return "";
        reverse(cha,0,n-1);
        reverse(cha,n,cha.length-1);
        reverse(cha,0,cha.length-1);
        StringBuffer sb=new StringBuffer(cha.length);//sb把char拼接起来,再转换成string
        for(char c:cha){
            sb.append(c);
        }
        return sb.toString();
    }
    public void reverse(char[] cha,int left,int right){
        char temp;
        while(left<right){
            temp=cha[left];
            cha[left]=cha[right];
            cha[right]=temp;
            left++;
            right--;
        }
    }

52.翻转单词顺序列:

用空格分离,sb拼接string【】的元素,找出加空格(“空格”)的i的规律

public String ReverseSentence(String str) {
        if(str.trim().equals("")) return str;
        String[] str1=str.split(" ");
        int len=str1.length;
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<len;i++){
            sb.append(str1[len-i-1]);
            if(i<len-1){
                sb.append(" ");
            }
        }
        return sb.toString();
    }

53.扑克牌顺子:

用d数组来记录每个数字出现的个数


public boolean isContinuous(int [] numbers) {
        if(numbers==null||numbers.length==0)
            return false;
        int[] d=new int[14];
        d[0]=0;
        int max=-1;
        int min=14;
        for(int i=0;i<numbers.length;i++){
            d[numbers[i]]++;
            if(numbers[i]==0){//0不计入最大值和最小值
                continue;
            }
            if(d[numbers[i]]>1){//出现对子就不是顺子
                return false;
            }
            if(numbers[i]>max){
                max=numbers[i];
            }
            if(numbers[i]<min){
                min=numbers[i];
            }
        }
        if(max-min>=5){//总共5个数,最大值和最小值之间必须满足此要求
            return false;
        }
        return true;
    }

54.圆圈中最后剩下的数:

f(n,m)=0(n=1),=f(n-1,m)(n>1)


  // 基于循环的实现
public int LastRemaining_Solution(int n, int m) {
        if(n<1||m<1){//编号0-n-1,数数0-m-1,处理无意义的情况
            return -1;
        }
        int last=0;
        for(int i=2;i<=n;i++){
            last=(last+m)%i;
        }
        return last;
    }

55.求1+2+3+…+n,不能利用公式和循环:

采用递归实现循环


public int Sum_Solution(int n) {
        int ans=n;
        boolean t=(ans!=0)&&((ans+=Sum_Solution(n-1))!=0);//&&左边当ans==0时,右边的Sum(-1)就不会执行,右边!=或者==0都可以,不产生实际作用
        return ans;
    }

56.不用加减乘除做加法:

加法分三步:1.原来的两个数各位相加,不算进位(异或);2.原来的两个数计算进位(与+向左移1位)3.第一步和第二步得到的数作为新的两个数,重复1,2两步,直到进位为0,求和的结果就是第一步得到的数。

  public int Add(int num1,int num2) {
       int temp;
       while(num2!=0){
           temp=num1^num2;
           num2=(num1&num2)<<1;
           num1=temp;
       }
        return num1;
    }

57.把字符串转换成整数:

去空后,str转换为char[],(int)(字符-‘0’)把与0的ascii码差值变成int

    public int StrToInt(String str) {
       if(str==null||str.length()==0||str.trim().equals(""))//对字符串为空的处理
           return 0;
        char[] ch=str.trim().toCharArray();
        int flag=1,res=0,start=0;//flag记录正负,start记录数字开始的下标
        if(ch[0]=='+')
            start=1;
        if(ch[0]=='-'){
            start=1;
            flag=-1;
        }
            for(int i=start;i<ch.length;i++){
                if(ch[i]>'9'||ch[i]<'0'){//比较两个字符的ascll码值
                    return 0;
                }
              
                if(flag==1&&res*10>Integer.MAX_VALUE-(int)(ch[i]-'0')){//不能有比最大值还大的值,所以移项
                    return 0;
                }
                if(flag==-1&&res*10*flag<Integer.MIN_VALUE+(int)(ch[i]-'0')){
                    return 0;
                }
                res=res*10+(int)(ch[i]-'0');//当都满足合法数值的条件时
            }
        return res*flag;
    }

58.数组中重复的数字:

用hashmap来存储每个数字,是否重复用containsKey


public boolean duplicate(int numbers[],int length,int [] duplication) {
        HashMap<Integer,String> map=new HashMap<>();
        boolean flag=true;
        for(int i=0;i<length;i++){
            if(map.containsKey(numbers[i])){
                duplication[0]=numbers[i];
                break;
            }
            map.put(numbers[i],null);
        }
        if(flag=true&&duplication[0]!=-1){
            return true;
        }
        return false;
    }
解法2:用boolean数组来表示是否访问过,第一次遍历,把数组元素置为1,有重复元素就会再次访问该位的Boolean
    boolean[] k = new boolean[length];
        for (int i = 0; i < k.length; i++) {
            if (k[numbers[i]] == true) {
                duplication[0] = numbers[i];
                return true;
            }
            k[numbers[i]] = true;
        }
        return false;
    }

59.构建乘积数组:

对B【0】和B【n-1】特殊处理,对中间的拆成两半,分别得出积再相乘

public int[] multiply(int[] A) {
        int[] B=new int[A.length];
        int n=A.length;
        B[0]=1;
        B[n-1]=1;
        for(int i=1;i<n;i++){
            B[0]*=A[i];
            B[n-1]*=A[i-1];
        }
        for(int i=1;i<n-1;i++){
            int left=1,right=1;
            for(int j=0;j<i;j++){
                left*=A[j];
            }
            for(int j=i+1;j<n;j++){
                right*=A[j];
            }
            B[i]=left*right;
        }
        return B;
    }

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

用hashmap存放字符及其出现的次数,containsKey方法,每次插入一个字符,用ArrayList来存储字符

Character是char的包装类,就像Integer和int ,以及Long和long一样。
Character是char的包装类,注意它是一个类,提供了很多方法的
//
//Insert one char from stringstream
    HashMap<Character,Integer> map=new HashMap<>();
    ArrayList<Character> list=new ArrayList<>();
    public void Insert(char ch)
    {
        if(map.containsKey(ch)){
            map.put(ch,map.get(ch)+1);
        }else{
            map.put(ch,1);
        }
        list.add(ch);
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
        for(char key:list){//加强for,对list的每一个元素
            if(map.get(key)==1){//只出现一次
                return key;
            }
        }
        return '#';
    }

61.二叉树的下一个节点:

求当前节点中序遍历的下一个节点


public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if(pNode==null) return null;
        
        if(pNode.right!=null){//右子树不为空,则为其右节点的左子节点(直到该节点为叶节点)
            TreeLinkNode pNext=pNode.right;
            while(pNext.left!=null){
                pNext=pNext.left;
            }
            return pNext;
        }
        
        while(pNode.next!=null&&pNode.next.left!=pNode){//当右节点为空时,查找满足当前节点为其父节点的左子节点的节点
            pNode=pNode.next;
        }
        return pNode.next;//其下一个节点为查找到的节点的父节点,并包含了无右节点的根节点的下一个节点(null)
    }

62.矩形覆盖:

n>=3时时斐波拉契数列,竖着放一块,剩下f(n-1),横着放一块,下一块确定,剩下f(n-2)两者相加。

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

63.之字形打印二叉树:

用两个栈来分别装入奇数层和偶数层节点,奇数层遍历的时候,将奇数层的左右依次进入偶数层的栈,这样偶数层出栈时就是右先出左后出,同理,遍历偶数层的时候,右左依次进入奇数层的栈,奇数层出栈的时候,从左往右。
注意:因为stack可以push(null),所以如果不加判空处理,在最后一层的时候,会进入下一层本来不存在的层,s1/s2有null值,而不为空,导致创建了一个空的list,把s1/s2的nullpop出来以后,会被加入res中,导致结果不对,因此要加入判空处理。(对栈:stack.empty(),对ArrayList.isEmpty())

 public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        int layer=1;
        Stack<TreeNode> s1=new Stack<>();
        s1.push(pRoot);
        Stack<TreeNode> s2=new Stack<>();
        
        ArrayList<ArrayList<Integer>> res=new ArrayList<>();
        while(!s1.empty()||!s2.empty()){
            if(layer%2==1){
                ArrayList<Integer> list=new ArrayList<Integer>();
                while(!s1.empty()){
                    TreeNode node=s1.pop();
                    if(node!=null){
                        System.out.print(node.val);
                        list.add(node.val);
                        s2.push(node.left);
                        s2.push(node.right);
                    }
                }
                if(!list.isEmpty()){//判空
                layer++;
                System.out.println();
                res.add(list);  
                }
            }else{
                ArrayList<Integer> list=new ArrayList<>();
                while(!s2.empty()){
                    TreeNode node=s2.pop();//如果不判空,最后会将nullpush出来,后不执行if,跳出while循环,会加入空的list
                    if(node!=null){
                        System.out.print(node.val);
                        list.add(node.val);
                        s1.push(node.right);
                        s1.push(node.left);
                    }
                }
                if(!list.isEmpty()){//判空
                layer++;
                System.out.println();
                res.add(list);
                }
            }
        }
        return res;
    }

64.把二叉树打印成多行:

递归写法(深度优先),根节点的左子树,左节点的左子树,左子树的左1,左1的空空,左子树的右1,直到根节点的左子树完全遍历完,再遍历右子树,需要根据当前深度给对应层级的ArrayList添加元素,ArrayLIST下标从0开始,深度从1开始

    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> list=new ArrayList<>();
        depth(pRoot,1,list);
        return list;
    }
    public void depth(TreeNode pRoot,int depth,ArrayList<ArrayList<Integer>> list){
        if(pRoot==null) return;//终止条件
        if(depth>list.size()){//深度超过size,创建一个空的ArrayList
            list.add(new ArrayList<Integer>());
        }
        list.get(depth-1).add(pRoot.val);//根据当前深度来给相应的ArrayList《Integer》增加元素
        
        depth(pRoot.left,depth+1,list);//对左子树,深度+1
        depth(pRoot.right,depth+1,list);
    } 

65.二叉搜索树的第K个节点:

中序遍历即可把一个二叉搜索树按从小到大遍历,将数值复制到数组中,以便找到第k小的数,同时中序遍历的时候,放入hashmap中,最后遍历hashmap,寻找符合第k小的TreeNode。

public class Solution {
    int[] a=new int[10];
    int i=0;
    HashMap<TreeNode,Integer> map=new HashMap<>();
    TreeNode KthNode(TreeNode pRoot, int k)
    {
        if(k<=0) return null;
        mid(pRoot);
        for(Map.Entry<TreeNode,Integer> mapEntry : map.entrySet()){
            if(mapEntry.getValue().equals(a[k-1])){
                return mapEntry.getKey();
            }
        }
        return null;
    }
    public void inorder(TreeNode pRoot){
        if(pRoot==null) return;
        inorder(pRoot.left);
        map.put(pRoot,pRoot.val);
        a[i++]=pRoot.val;
        inorder(pRoot.right);
    }
}

66.滑动窗口的最大值:

两重循环:大循环控制滑动窗口的边界,内循环找出每个滑动窗口内的最大值


public ArrayList<Integer> maxInWindows(int [] num, int size)
    {
        ArrayList<Integer> list=new ArrayList<>();
        if(size==0) return list;
        for(int i=0;i<=num.length-size;i++){
            int max=0;
            for(int j=i;j<i+size;j++){
                if(num[j]>max){
                    max=num[j];
                }
            }
            list.add(max);
        }
        return list;
    }

67.矩阵中的路径:

回溯法,用boolean数组记录每个元素的访问状态


public class Solution {
    public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
    {
        boolean[] flag=new boolean[matrix.length];
        for(int i=0;i<rows;i++){//对矩阵中的每一个元素都作为起始点,是否可能形成一条路径
            for(int j=0;j<cols;j++){
                if(judge(matrix,i,j,rows,cols,str,0,flag)){
                    return true;
                }
            }
        }
        return false;
    }
    public boolean judge(char[] matrix,int i,int j,int rows,int cols,char[] str,int k,boolean[] flag){
        int index=i*cols+j;//计算当前行列的元素在一维数组中的位置
        if(i<0||j<0||i>=rows||j>=cols||matrix[index]!=str[k]||flag[index]==true)//如果元素超出矩阵的边界,或者当前位置的元素和路径中的已经不相等或者当前已经访问过了,就返回,表示这条路走不通
            return false;
        if(k==str.length-1)//每一个元素都对应上且到了路径中的最后一个数,则找到了一条可行的路径
            return true;
        flag[index]=true;
        //对当前元素的左右上下分别递归查询,只要从当前元素出发向上下左右的任意一条路径可行,就返回true
        if(judge(matrix,i-1,j,rows,cols,str,k+1,flag)||
          judge(matrix,i+1,j,rows,cols,str,k+1,flag)||
          judge(matrix,i,j+1,rows,cols,str,k+1,flag)||
          judge(matrix,i,j-1,rows,cols,str,k+1,flag)){
            return true;
        }
        flag[index]=false;//从index出发的所有路径都不符合,则在主程序两层循环到达下一个元素作为起始元素之前,把当前元素设置未被访问,并当前元素起始的路径都不符合,返回false
        return false;
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值