剑指offer

 

 


 

目录

二维数组中的查找

 

替换空格

从尾到头打印链表

重建二叉树

二叉树的下一个节点

用两个栈实现队列

二叉树的下一个节点

算法与数据操作

  一、递归和循环

斐波那契数列

跳台阶

变态跳台阶

矩形覆盖

  二、查找和排序

二进制中1的个数

数值的整数次方

代码的规范性

代码的完整性

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

链表中倒数第k个结点

反转链表

复杂链表的复制

二叉搜索树与双向链表

数组中只出现一次的数字

数组中的逆序对

对称的二叉树

把二叉树打印成多行

序列化二叉树




  • 二维数组中的查找

     

题目

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

思路

右上角(或者左下角)开始 

大于待查找值——从右向左剔除所在列

小于待查找值——从上向下剔除所在行

代码

——JAVA

public class Solution {
    public boolean Find(int target, int [][] array) {
        if(array.length==0||array[0].length==0)
//注意理解————在二维数组中
//数组名.length指示数组的行数;数组名[行下标] .length指示该行中的元素个数。
            return false;
        int col=array[0].length-1;//右上角的列数 
        int row=0;//右上角的行数
        int temp=array[row][col];
        while(target!=temp){
            if(col>0 && row<array.length-1){
                if(target>temp){
                    row=row+1;//排除上面的行
                }
                else if(target<temp){
                    col=col-1;//排除右边的列
                }
                temp=array[row][col];//相等则确定target
            }
            else{
                return false;//没有target则返回错误
            }
        }
        return true;
    }
}

  • 替换空格

题目

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

注意:空格(1个字符)→“%20”(3个字符)

应用

用于网络编程中,将URL参数中的特殊符号转换为服务器可以识别的字符

e.g. 空格(ASCⅡ码为32)十六进制0x20→%20                       #(ASCⅡ码为35)十六进制0x20→%23)

思路

C++中,假定考虑在原来的字符串上进行替换,保证输入的字符串后面有足够的空余内存

如果从头到尾进行扫描替换,复杂度为O(n2);而从尾到头复制替换,所有字符只复制一次,复杂度降为O(n)

先遍历得到空格数目以及需要增加的内存,再从后向前,设置两个指针,分别指向原始的字符串末尾和替换后的字符末尾

JAVA中,下面给出的解法是直接创建新的字符串空间(不够好,最好考虑在原始字符串上进行修改)

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。

和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的

StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

  String:适用于少量的字符串操作的情况

  StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

StringBuffer 类支持的主要方法:

序号方法描述
1public StringBuffer append(String s)
将指定的字符串追加到此字符序列。
2public StringBuffer reverse()
 将此字符序列用其反转形式取代。
3public delete(int start, int end)
移除此序列的子字符串中的字符。
4public insert(int offset, int i)
将 int 参数的字符串表示形式插入此序列中。
5replace(int start, int end, String str)
使用给定 String 中的字符替换此序列的子字符串中的字符。

问题

对c++中基于原始字符串修改的方法不够理解

1.新建字符串

2.在原有字符串上更改,需要重新设置长度setLength()

str.toString用法理解

代码1

public class Solution {
    public String replaceSpace(StringBuffer str) {
        StringBuffer res=new StringBuffer();//创建新空间
        int len=str.length()-1;//字符串编号
        //从后往前 遍历寻找空格并替换
        for(int i=len;i>=0;i--){
            if(str.charAt(i)==' '){
                res.append("02%");//替换
            }
            else
                res.append(str.charAt(i));
        }
        return res.reverse().toString();//反序得到修改后字符串
    }
}

代码2

public class Solution {//在原有字符串上修改
    public String replaceSpace(StringBuffer str) {
    	int length=str.length();
        int num_Space=0;
        for(int i=0;i<length;i++){
            if(str.charAt(i)==' '){num_Space++;}
        }//统计字符串中空格个数
        int newlength=length+num_Space*2;//一个字符变为三个字符需要增加的字符串长度
        str.setLength(newlength);//重置字符串长度
        //int index=length-1;
        int newindex=newlength-1;//设置两个指示点,从后向前
        for(int index=length-1;index>=0;index--){//这里也可以for(;index>=0;index--)
            if(str.charAt(index)==' '){
                str.setCharAt(newindex--,'0');
                str.setCharAt(newindex--,'2');
                str.setCharAt(newindex--,'%');
            }
            else{
                str.setCharAt(newindex--,str.charAt(index));
            }
            
        }
        return str.toString();
    }
}

测试用例

 特殊输入测试(字符串是nullptr指针;字符串是一个空字符串;只有一个空格字符;有连续多个空格)


  • 从尾到头打印链表

题目

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

思路

1.栈——后进先出,具有一定鲁棒性

2.递归——若链表太长,则可能导致函数调用栈溢出

代码

/**
*    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> list=new ArrayList<Integer>();
        while(!stack.isEmpty()){
            list.add(stack.pop());//从栈中输出,保证后进先出
        }
        return list;//要有个ArrayList作为返回值
    }
}

  • 重建二叉树

题目

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路

前序遍历——根左右

中序遍历——左根右

后序遍历——左右根

可以得到根节点,再根据递归思想分别得到左右子树的具体节点

递归思想:每次将左右子树当成新的子树进行处理,中序的左右子树索引很好找,前序的开始结束索引通过计算中序中左右子树的大小来计算,然后递归求解,直到startPreorder>endPreorder || startInoreder>endInorder说明子树整理完毕

方法每次返回一个根节点

问题

不确定public,static什么时候用;对递归的理解不够透彻

代码

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public TreeNode reConstructBinaryTree(int [] preorder,int [] inorder) {
        if(preorder==null||inorder==null||preorder.length<1||inorder.length!=preorder.length)
        {return null;}
        TreeNode root= ConstructBinaryTree(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
        return root;
    }
    
    /**构建二叉树
    *  preorder前序遍历序列:ps开始位置,pe结束位置
    *  inorder中序遍历序列:is开始位置,ie结束位置
    */
    
    private TreeNode ConstructBinaryTree(int [] preorder,int ps,int pe,int [] inorder,int is, int ie){
        //前序遍历的开始位置大于结束位置时,说明有问题
        if(ps>pe||is>ie){
            return null;
        }
        TreeNode root=new TreeNode(preorder[ps]);
        for(int i=is;i<=ie;i++){
            if(preorder[ps]==inorder[i]){
                root.left=ConstructBinaryTree(preorder,ps+1,ps+(i-is),inorder,is,i-1);//前序:根左右;中序:左根右
                root.right=ConstructBinaryTree(preorder,(i-is)+ps+1,pe,inorder,i+1,ie);
            }
        }
        return root;
        
    }
}

  • 二叉树的下一个节点

题目

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路

中序遍历:左 -> 根 -> 右
分三种情况:

  1. 如果当前节点为空,直接返回空;//return作用      :a、返回一个值,这个值可以是任意类型。b、使程序返回到操作系统(即终止程序)
  2. 如果当前节点有右子树,则返回右子树的最左子树;
  3. 如果当前节点没有右子树,再分两种情况:
    • 看看当前节点是不是它的父节点的左子树,如果是,则返回它的父节点;
    • 如果当前节点不是它的父节点的左子树,则把父节点赋给当前节点,再判断当前节点是不是它的父节点的左子树,直到当前节点是不是它的父节点的左子树,返回它的父节点。

代码 

/*
public class TreeLinkNode {
    int val;
    TreeLinkNode left = null;
    TreeLinkNode right = null;
    TreeLinkNode next = null;

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        //节点为空,返回空
        if(pNode==null){
            return null;
        }
        //当前节点有右子树,返回右子树的最左子树
        if(pNode.right!=null){
            TreeLinkNode node=pNode.right;
            while(node.left!=null){
                node=node.left;
            }
            return node;
        }
        //当前节点无右子树
        //判断当前节点若非其父节点的左子树,将其父节点赋给当前节点,继续判断,直至满足,返回其父节点
        else{
            while(pNode.next!=null){
                TreeLinkNode root=pNode.next;
                if(pNode==root.left){return root;}
                else{pNode=root;}
            }
        return null;
    }
        

    }
}

  • 用两个栈实现队列

题目

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

思路

注意要把stack1里pop出来的元素再push回去,不然在以下用例会出错

push1,push2,push3,pop,pop,push4,pop,push5,pop

代码

import java.util.Stack;

public class Solution {
    Stack<Integer> stack1 = new Stack<Integer>();
    Stack<Integer> stack2 = new Stack<Integer>();
    //(先进先出)队列的push,pop函数
    public void push(int node) {
        stack1.push(node);//
    }
    
    public int pop() {
        while(!stack1.isEmpty()){
            stack2.push(stack1.pop());
        }
        int popnode=stack2.pop();
        while(!stack2.isEmpty()){
            stack1.push(stack2.pop());
        }//int queue=stack2.pop();


        return popnode;
    }
}

  • 二叉树的下一个节点

题目

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。

思路

中序遍历:左 -> 根 -> 右

 

算法与数据操作

递归:需要在内存栈中分配空间 ,容易引发调用栈溢出

  一、递归和循环

  • 排序、查找(算法重点)——二分查找,归并排序,快速排序
  • 二维数组搜索路径——回溯法
  • 求最优解——动态规划(自上而下的递归思路)、(自下而上的循环思路)——若此时还需要新思路,考虑贪婪算法
  • 位运算:与(&&)、或(||)、异或、左移、右移

斐波那契数列

(从0开始,第0项为0)

思路:f(0)=0,f(1)=1,f(2)=1,f(n)=f(n-1)+f(n-2)

注意:1.递归,思路效率很低(o(n^2))

2.从下往上计算o(n) 

public class Solution {
    public int Fibonacci(int n) {
        return getFibonacci(n);
    }
    
    private int getFibonacci(int n){
        if(n==0||n==1){
            return n;
        }
        int n1=0;
        int n2=1;
        int value=0;
        for(int num=0;num<=n-2;num++)
        {
            value=n2+n1;
            n1=n2;
            n2=value;
        }
        return value;
    }
}

3.不够实用:矩阵[1 1;1 0]的n-1次方——用递归实现(n/2的平方再将其平方即得到n次方)即时间复杂度o(logn)


跳台阶

思路:

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

(先后次序不同算不同的结果)

即斐波那契数列


变态跳台阶

思路:找规律

f(n) = 2 * f(n-1)

同上


矩形覆盖

思路:斐波那契

 

  二、查找和排序

 


二进制中1的个数

思路:

1.Java自带函数:Integer.bitCount()                熟悉API使用

2.如果一个整数不为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,就可以进行多少次这样的操作。

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

数值的整数次方

题目:

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

 

思路:

 

  • n为非负数时,>> 1/ 2的结果是一样的
  • n为负数且还是偶数时,>> 1/ 2的结果是一样的
  • n为负数且还是奇数时,>> 1/ 2的结果是不一样的
  •  
  • -5 / 2 = -25 / 2 = 2。这表明     除二是向零取整
  • -5 >> 1 = -35 >> 1 = 2。这表明 右移一位是向下取整

 

方法:

1.符合逻辑思考:循环或者递归(如下)

//循环   
 public double Power(double base, int n) {
        boolean Normal=true;
        if(base==0){
            if(n==0){Normal=false;}
            return 0;
        }
        if(n==0){
            return 1;
        }
        if(n==1){
            return base;
        }
        int abs=n;
        if(n<0){
            abs=-n;
        }
        double result=1;
        for(;abs>0;abs--){
            result*=base;
        }
        if(n<0){
            result=1/result;
        }
        return result;
  }
}
//递归
public class Solution {
    public double Power(double base, int exponent) {
        boolean Normal=true;//记录是否正常返回值
        //0的0次方无意义,非0次方为0
        if(base==0){
            if(exponent<=0){
                Normal=false;
            }
            return 0;
        }
        //非零数字的0次方
        if(exponent==0){
            return 1;
        }
        //非零数字的一次方
        if(exponent==1){
            return base;
        }
        //
        int absexponent=exponent;
        if(exponent<0){
            absexponent=-exponent;
        }
        
        double result=Power(base,absexponent>>1);//右移1位代替除以2
        result*=result;
        if((exponent&1)==1){
            result=result*base;
        }
        if(exponent<0){
            result=1/result;
        }
        return result;
  }
}

2.

public double Power(double base, int n) {
    double res = 1,curr = base;
    int exponent;
    if(n>0){
        exponent = n;
    }else if(n<0){
        if(base==0)
            throw new RuntimeException("分母不能为0"); 
        exponent = -n;
    }else{// n==0
        return 1;// 0的0次方
    }
    while(exponent!=0){
        if((exponent&1)==1)
            res*=curr;
        curr*=curr;// 翻倍
        exponent>>=1;// 右移一位
    }
    return n>=0?res:(1/res);       
}

代码的规范性

代码的完整性

1.功能测试:如果是int、longlong都无法表示的大数,可能需要用特殊的数据结构来表示

2.边界测试

3.负面测试

错误处理方法:

1.返回值——与系统API一致,但不能方便使用计算结果

2.全局变量——能够方便使用计算结果,但是用户可能会忘记检查全局变量

3.抛出异常——可以为异常分类,逻辑清晰,但是有些语言不支持,且对性能有影响

 


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

题目:

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

代码:

    public void reOrderArray(int [] array) {
        if(array.length==0||array.length==1||array==null){
            return;
        }
        List<Integer> list_odd=new ArrayList<>();
        List<Integer> list_even=new ArrayList<>();//只记录奇数偶数的index
        
        for(int i:array){
            if(i%2==1){
                list_odd.add(i);
            }
            else{
                list_even.add(i);
            }
        }
        list_odd.addAll(list_even);
        for(int j=0;j<array.length;j++){
            array[j]=list_odd.get(j);
        }
    }
}

链表中倒数第k个结点

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

代码:(采用两个指针,第一个指向原链表的第k个位置,第二个指向原链表的第1个位置;再同时后移直到第一个指向链表最后一个位置,此时第二个指针指向结点则为我们需要的)

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        if(head==null||k<=0){
            return null;
        }
        
        ListNode node1=head;//用于做参考,先指向链表第k个位置,最后到最后一个位置
        ListNode node2=head;//用于获取,从head第1个位置到倒数第k个位置
        for(int i=1;i<k;i++){
            if(node1.next!=null){
               node1=node1.next;
            }
            else{
                return null;
            }
        }
        while(node1.next!=null){
            node1=node1.next;
            node2=node2.next;
        }
        return node2;
        
    }
}

反转链表

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

代码:

/*
public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.Stack;
public class Solution {
    public ListNode ReverseList(ListNode head) {
        if(head==null){
            return null;
        }
        Stack<Integer> stack=new Stack<>();
        ListNode index=head;
        stack.push(head.val);
        while(index.next!=null){
            stack.push(index.next.val);
            index=index.next;
        }
        ListNode rehead=head;
        while(head.next!=null){
        head.val=stack.pop();
        head=head.next;
        }
        head.val=stack.pop();
        return rehead;
    }
}

参考代码:

public class Solution {
 public static ListNode ReverseList(ListNode head) {
 if(head==null)
 return null;
 ListNode reversedHead=null;
 ListNode current=head;
 ListNode tmp=null;
 ListNode pre=null;
 while(current!=null){
 tmp=current.next;
 current.next=pre;
 if(tmp==null)
 reversedHead=current;
            pre=current;
 current=tmp;
  
 }
 return reversedHead;
 }
}

复杂链表的复制

总是不对,真的不知道为啥


二叉搜索树与双向链表

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

分析:排序的双向列表,即新建一个树结构,其中节点为现有树结构经过中序遍历后的顺序,这些节点的left,right不变即可

二叉搜索树(Binary Search Tree)————或为空,或者是具有下列性质的二叉树

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

它的左、右子树也分别为二叉排序树

(即中序遍历为递增的)

 

思路

1.遍历(常常会超出运算空间或时间)

2.非遍历

3.Morris遍历


public class Solution {
    public TreeNode Convert(TreeNode pRootOfTree) {
        TreeNode p = pRootOfTree, pre = null, res = null;
        while (p != null) {

            while (p.left != null) {  //当前节点左子树非空时
                TreeNode q = p.left;      //设置q,为当前节点的左子树根节点
                while (q.right != null) {   //循环:只要q右子树非空 就令q为其右子树
                    q = q.right;
                }
                q.right = p;      // 使用为空的右子树记录当前节点
                TreeNode tmp = p.left;
                p.left = null;
                p = tmp;             //使当前节点更新为当前节点的左子树根节点,直至为null退出
            }

            p.left = pre;

            //判断当前节点是否需要被加入双向链表中
            if (pre == null) {
                res = p;//中序遍历数列的第一项,即最小一个左节点
            } 
            else {
                pre.right = p;
            }

            pre = p;//用pre记录下当前节点
            p = p.right;//更新当前节点为当前节点的右子树根节点
        }
        return res;//返回中序遍历数列
    }
}

数组中只出现一次的数字

1.hashMap

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
        for(int i=0;i<array.length;i++){
            if(map.containsKey(array[i]))
                map.remove(array[i]);
            //使得map中只有两个出现了一次的值
            else
                map.put(array[i],1);
        }
        int [] a=new int[array.length];
        int i=0;
        for(Integer k: map.keySet()){
            a[i]=k;
            i++;
        }
        num1[0]=a[0];
        num2[0]=a[1];
    }
}

2.异或

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
    public void FindNumsAppearOnce(int [] a,int num1[] , int num2[]) {
        if(a.length==0)
            return;
        //全体异或
        int num=0;
        for(int i=0;i<a.length;i++){
            num^=a[i];
        }
        //寻找为1的位数
        int index=0;
        while((num&1)==0 && index<8){
            num=num>>1;
            index++;
        }
        //分组异或
        for(int i=0;i<a.length;i++){
            int tmp=a[i];
            tmp=tmp>>index;
            if((tmp&1)==1){
                num1[0]^=a[i];
            }else{
                num2[0]^=a[i];
            }
            
        }
        
    }
}

数组中的逆序对

首先分成两个数组,分别递归归并排序,结果存到辅助数组

逆序对个数 = 数组彼此间逆序对的个数 + 子组内的逆序对个数

组内逆序对如何确定——建立辅助数组用于存储较大的值

分别从大到小对比两个数组中每个元素的大小(这里需要考虑是 奇数 的情况)

 

public class Solution {
    public int InversePairs(int [] array) {
        int len = array.length;
        
        if(array== null || len <= 0){
            return 0;
        }
        
        return mergeSort(array, 0, len-1);
    }
    
    
    public int mergeSort(int [] array, int start, int end){
        if(start == end)
            return 0;
        int mid = (start + end) / 2;
        int left_count = mergeSort(array, start, mid);//对两个子数组分别归并排序
        int right_count = mergeSort(array, mid + 1, end);
        int i = mid, j = end;
        int [] copy = new int[end - start + 1];
        int copy_index = end - start;//辅助数组
        int count = 0;
        while(i >= start && j >= mid + 1){
            //保证i从mid开始减小直到start,j从end开始减小直到mind+1
            if(array[i] > array[j]){//若第一组值大于第二组
                copy[copy_index--] = array[i--];//将较大值存入辅助数组
                count += j - mid;//逆序对个数为第二组个数
                if(count > 1000000007){
                    count %= 1000000007;//防止数据量过大
                }
            }else{//若第一组值小于第二组
                copy[copy_index--] = array[j--];//仍然将较大值存入辅助数组
            }
        }
        
        while(i >= start){//第二组提前处理完了,说明第一组取值都较小,按顺序存进去即可
            copy[copy_index--] = array[i--];
        }
        while(j >= mid + 1){//第一组提前处理完了
            copy[copy_index--] = array[j--];
        }
        
        i = 0;
        while(start <= end) {
            array[start++] = copy[i++];
        }
        
        return (left_count+right_count+count)%1000000007;//数组内部逆序对+左右子组逆序对
    }
}

 

对称的二叉树

1.递归

2.非递归(引入栈的数据结构,push存储左右结点,pop出来进行判断,并重新存入下一层四个左右结点)

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

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

    }

}
*/
import java.util.Stack;
public class Solution {
    boolean isSymmetrical(TreeNode pRoot)
    {
        //1.递归方法
        // if(pRoot==null)
//             return true;
        
//         return isSym(pRoot.left,pRoot.right);
//     }
//     public boolean isSym(TreeNode left,TreeNode right){
//         if(left==null && right==null)
//             return true;
//         if(left==null || right==null)
//             return false;
//         if(left.val==right.val){
//             return isSym(left.left,right.right) && 
//                 isSym(left.right,right.left);//关键语句
//         }
//         return false;
        
        //2.非递归方法
        if(pRoot==null)
            return true;
        Stack<TreeNode> s=new Stack<TreeNode>();
        s.push(pRoot.left);
        s.push(pRoot.right);
        while(!s.isEmpty()){
            TreeNode right=s.pop();
            TreeNode left=s.pop();
            if(right==null&&left==null)
                continue;//当前循环结束 且进入下一次循环
            if(right==null || left==null)
                return false;
            if(right.val!=left.val)
                return false;
            s.push(left.left);
            s.push(right.right);
            s.push(left.right);
            s.push(right.left);
        }
        return true;
    }
}

把二叉树打印成多行

import java.util.ArrayList;


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

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

    }

}
*/
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> list=new ArrayList<ArrayList<Integer>>();
        LayerNode(pRoot,1,list);//第一层
        return list;
    }
    
    public void LayerNode(TreeNode p,int depth,ArrayList<ArrayList<Integer>> list){
        if(p==null)
            return;
        if(depth>list.size())//当层数大于当前list内存储层数时,新建一个用于存储新层
            list.add(new ArrayList<Integer>());
        list.get(depth-1).add(p.val);//关键步骤:加入当前根节点的值
        LayerNode(p.left,depth+1,list);
        LayerNode(p.right,depth+1,list);
        
    }
    
}

序列化二叉树

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

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

    }

}
*/
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    String Serialize(TreeNode root) {
        if(root==null){
            return"#,";
        }
        StringBuffer res=new StringBuffer(root.val+",");
        res.append(Serialize(root.left));
        res.append(Serialize(root.right));
        return res.toString();
  }
    TreeNode Deserialize(String str) {
       String[] res=str.split(",");
        Queue<String> queue=new LinkedList<String>();
        for(int i=0;i<res.length;i++){
            queue.offer(res[i]);
        }
        return preOrder(queue);
  }
    TreeNode preOrder(Queue<String> queue){
        String val=queue.poll();//取出并删除
        if(val.equals("#")){//基本类型的比较用==,String类型用equals()方法
            return null;
        }
        TreeNode node=new TreeNode(Integer.parseInt(val));//Integer.parseInt()返回int型整数
        node.left  = preOrder(queue);
        node.right = preOrder(queue);
        return node;
    }
}

 

 

链表中环的入口结点


首先判断有没有环(通过快慢指针是否相等)

接着判断入口位置(相遇位置与链表头 和入口的距离相等)

注意!判断边界情况

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead.next==null || pHead.next.next==null)
            return null;
        ListNode slow=pHead.next;
        ListNode fast=pHead.next.next;
       while(fast!=null){
           if(slow==fast){
               fast=pHead;
               while(fast!=slow){
                   fast=fast.next;
                   slow=slow.next;
               }
               return slow;
               
           }
           slow=slow.next;
           fast=fast.next.next;
       }
        return null;
    }
}

Python2

思路:建立临时链表存储遍历该链表,然后出现重复则为入口 

class Solution:
    def EntryNodeOfLoop(self, pHead):
        tempList = []
        p = pHead
        while p:
            if p in tempList:
                return p
            else:
                tempList.append(p)
            p = p.next

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值