0
点赞
收藏
分享

微信扫一扫

剑指Offer题解过程

剑指Offer题

1.重建二叉树

示例1

输入

[1,2,3,4,5,6,7],[3,2,4,1,6,5,7]

返回值

{1,2,5,3,4,6,7}

前置知识:

二叉树的前序遍历:根左右 二叉树的中序遍历:左根右 二叉树的的后序遍历:左右根

解题思路

首先我们肯定是要通过递归来实现重建的

递归就是从大问题转化为小问题的过程

根据二叉树的特性,二叉树的左右子树也是也是一颗二叉树(所以他是最适合递归的数据结构 不管是遍历还是建立)

所以我们现在要来分解了 根据前序遍历我们知道数组第一位都是根节点 然后是左子树右子树部分

1 (根节点)234 (左子树)567(右子树) 根左右

那么我们就从中序遍历找到根节点的位置

324(左子树) 1 (根节点)657(右子树) 左根右

再次递归

root.left= b(234 ,324)

root.right=b(567 ,657)

退出的位置就是 传入的是空数组就return null;

这里我用到Arrays的函数来截取数组,当然也可以通过下标来截取,但是参数就要添加。

Arrays.copyOfRang

import java.util.Arrays;

Arrays.copyOfRange(T[ ] original,int from,int to)

将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组。

注意这里包括下标from,不包括上标to。

2.构建斐波那契数列

题目描述

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。

//递归 

public class Solution {
    public int Fibonacci(int n) {
      if(n==0)return 0;
        if(n==1)return 1;
        return  Fibonacci(n-1)+Fibonacci(n-2);   
    }
}
//时间复杂度O(2^n)运行时间:852ms 占用内存:9484k

//动态规划
public class Solution {

    public int Fibonacci(int n) {
      int []dp = new int[n+1];
        if(n==0)return 0;
        if(n==1)return 1;
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}
//时间复杂度O(N) 空间复杂度O(n) 运行时间 13ms

3.两个栈实现队列

我的理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZMuyFyNI-1618561745425)(C:\Users\77308\AppData\Roaming\Typora\typora-user-images\image-20201110140607502.png)]

事实上

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-obV9SiUp-1618561745427)(C:\Users\77308\AppData\Roaming\Typora\typora-user-images\image-20201110140835802.png)]

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(node);
    }
    
    
    public int pop() {
     if(stack2.empty()){
        while(!stack1.empty()){
            stack2.push(stack1.pop());
     }}
          return stack2.pop();  
         
}

4.跳台阶

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

public class Solution {
    public int JumpFloor(int target) {
    int []dp = new int [target+1];
        if(target==1)
            return 1;
        dp[0]=1;
        dp[1]=1;
        for(int i = 2;i<= target; i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[target];
    }
}
//时间复杂度O(n) 空间复杂度O(n)


// ###继续优化
// 发现计算f[5]的时候只用到了f[4]和f[3], 没有用到f[2]...f[0],所以保存f[2]..f[0]是浪费了空间。
// 只需要用3个变量即可。


int Fibonacci(int n) {
     if (n == 0 || n == 1) return n;
        int a = 1, b = 1, c;
        for (int i=2; i<=n; ++i) {
            c = a + b;
            a = b;
            b = c;
        }
        return c;
}
// 时间复杂度:O(n)空间复杂度:O(1)

5.矩形覆盖

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

比如n=3时,2*3的矩形块有3种覆盖方法:

剑指Offer题解过程_子树

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

//遇到这种问题都是要找规律的
//n = 1 的时候
//只能横着覆盖,一种
//n = 2 的时候
//可以横着和竖着覆盖,两种
//n = 3 的时候
//第三级横着覆盖,用了一级,剩下 n = 2,有两种覆盖方法
//第三季竖着覆盖,用了两级,剩下 n = 1,有一种覆盖方法
//总共有 3 种
//n = 4 的时候
//第 4 级横着覆盖,用了一级,剩下 n = 3,有三种覆盖方法
//第 4 级竖着覆盖,用了两级,剩下 n = 2,有两种覆盖方法
//总共有 5 种方法
//n = n 的时候
//第 n 级横着覆盖,用了一级,剩下 n = n - 1,所以关注第 n - 1 种有几种覆盖方法
//第 n 级竖着覆盖,用了两级,剩下 n = n - 2,所以关注第 n - 2 种有几种覆盖方法
//总和为两种情况的总和

// 所以 f(n)=f(n-1)+f(n-2)

6.1的个数

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

public class Solution {
    public int NumberOf1(int n) {
        int count;
        while(n != 0){
            n&=(n-1);count++;
        }
        return count;
    }
}
//牛逼之处不用我多说了吧

 
//这里我说一个其他的知识点 负数向右移动的话是在前面补1 因为负数在计算机中是以补码表示的

7.反转链表

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

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

    ListNode(int val) {
        this.val = val;
    }
}*/
import java.util.Vector;
public class Solution {
    public ListNode ReverseList(ListNode head) {
      ListNode cur=head;
      ListNode pre=null;
      ListNode nex=null;
        while(cur!=null){
            nex=cur.next;
            cur.next=pre;
            pre=cur;
            cur=nex;
        }
        return pre;
    }
}

//暴力做法就是创建一个Vector 遍历链表然后将数据存到矢量数组中,为什么用Vectoe因为他是动态数组,(放在栈中好点) 然后再建立一个链表

// 正常做法是建立3个变量 pre cur nex nex=cur.next;
//          cur.next=pre;
//          pre=cur;
//          cur=nex;

8.合并两个链表

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode Merge(ListNode list1,ListNode list2) {
        
          ListNode head = new ListNode(-1);
          ListNode pre =head;
      while(list1!=null&&list2!=null){     
          if(list1.val<list2.val){
             pre.next=list1;
             pre = list1;
             list1=list1.next;
          }
          else{
               pre.next=list2;
               pre = list2;
               list2=list2.next;
          }
          
      }
        
      if(list1==null){
           pre.next=list2;
      }
        
        if(list2==null)
           pre.next=list1;

      return head.next;
    }
}

//思路就是。。。。简单的思路
//我错了就是在于if里不能2个if  因为这样第一个if执行后 就可以继续执行下一个if  但是我们要的是每次移动就判断是否有空。
//还有就是结束后,直接pre=list就行 ,因为他是有序的,后面都是排好的

9.数组中只出现一次的数

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字

解题思路:

1、第一次循环将所有的数当做key存到map中,然后map.get(key) ++

第二个循环找出map中,value为1的数就是了

O(n)

O(n)

public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
    HashMap<Integer,Integer> num = new HashMap<>();
    for (int i = 0; i < array.length; i++) {
        if(num.containsKey(array[i]))
        num.put(array[i],2);
        else
        num.put(array[i],1);
    }
    int count = 0;
    for (int i = 0; i < array.length; i++) {
        if(num.get(array[i])==1){
        if(count == 0){
            num1[0]=array[i];
            count++;
        }
        else{
            num2[0]=array[i];
        }
        }
    }
    }

2、高级思路: 因为相同的数异或为0 一个数异或0也为0

所以第一次循环将所有的数都相互异或一遍,那么里面相同的数就会没了。

结果肯定是两个没有相同的数异或的结果,那么我们想要拿到这个数就要想办法将这个结果分开

最好的办法就是将原来的数组分为2个数组,分别放这两个数,那么我们就可以像第一步那样拿到这个数,怎么分呢

第一步异或的结果肯定是这两个数异或后的,那么这个二进制中为1的就是他们不同的地方(不同异或为1),所以我们就根据这个标识,再一个循环把这个数组中的第x位为1的和不为1的分开,那么这两个数就不在同一个数组里了,就可以求出来了、

怎么找到这个标识,就是找第几位为1,我们可以&1,然后将1左移1位,找到&1等于1的就行,比如判断第3位是不是1 就&4(100),

O(n) O(1)

//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
       public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int result = 0;
        for (int i = 0; i < array.length; i++) {
            result ^= array[i];
        }
        //找出从右边开始第一个不同的1
        int first1 = 1;
        while((result & first1)==0){
            first1 = first1 << 1;
        }
        num1[0] = 0;
        num2[0] = 0;
        for (int i = 0; i < array.length; i++) {
            if((first1 & array[i])==0){
                num1[0] ^= array[i];
            } else{
                num2[0] ^= array[i];
            }
        }
    }
}

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

1 暴力法

O(N^2)

O(N^2)

public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
      ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
        for (int i = 1; i <= sum/2; i++) {
            int temp = 0;
            ArrayList<Integer> curentResult = new ArrayList<>();
            for (int j = i; j <=sum/2+1 ; j++) {
                temp+= j;
                curentResult.add(j);
                if(temp == sum){
                    result.add(curentResult);
                    break;
                }
                if(temp > sum) break;
            }
        }
        return result;
        }

2.前缀法

O(N^2)

O(N)

public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
    ArrayList<ArrayList<Integer>> results = new ArrayList<>();
    int[] index = new int[sum+1];
    int tem = 0;
    for (int i = 1; i <= sum; i++) {
        tem += i;
        index[i]=tem;
    }
    for (int i = 1; i < sum/2+2; i++) {
        for (int j = i+1; j < sum/2+2 ; j++) {
            if((index[j]-index[i-1]) == sum){
                ArrayList<Integer> result = new ArrayList<>();
                for (int k = i; k <= j; k++) {
                    result.add(k);
                }
                results.add(result);
            }
        }
    }
    return results;
}

3.滑动窗口法

O(N)

O(1)

ArrayList<ArrayList<Integer>> results = new ArrayList<>();
int i = 1, j= 1;
int tmp = 0;
while(i<=sum/2){
    if(sum ==  tmp){
        ArrayList<Integer> result = new ArrayList<>();
        for (int k = i; k < j; k++) {
            result.add(k);
        }
        results.add(result);
        tmp+=j;
        j++;

    }
    else if (sum > tmp){
        tmp+=j;
        j++;
    }else{
        tmp-=i;
        i++;
    }
}
return results;
}

11.和为S的两个数字

给定一个数组,返回两个数字和为sun且乘积最小的两个数字。

和上题也很像,滑动窗口法(前提是有序的)

因为数组是有序的,所以可以用双指针,指向数组的首尾,具体步骤如下: 1.初始化:指针i指向数组首, 指针j指向数组尾部 如果arr[i] + arr[j] == sum , 说明是可能解 否则如果arr[i] + arr[j] > sum, 说明和太大,所以--j 否则如果arr[i] + arr[j] < sum, 说明和太小,所以++i

时间复杂度:O(n) 空间复杂度:O(1)

ArrayList<Integer> result = new ArrayList<>();
int i= 0 , j = array.length-1;
int min = Integer.MAX_VALUE;
while(i<array.length&&j>-1){
    if((array[i]+array[j])==sum){
        if((array[i]*array[j])<min){
            min = (array[i]*array[j]);
            result.clear();
            result.add(array[i]);
            result.add(array[j]);
        }
        i++;
    }else if ((array[i]+array[j])<sum){
        i++;
    }else{
        j--;
    }
}
return result;

12.JZ45扑克牌顺子

时间复杂度O(N) 空间复杂度O(1)

思路: 找出数组中最大值和最小值(0不算)

最大-最小-除0外的数的数字就是可以放0的空位 max-min-1-(numbers.length-2-count);

如果空位<0代表有重复数字 falese

如果空位<0的数量可以填充 true

如果空位大于0的数量 false

适用于不只是5个数 且负数也行

public class Solution {
    public boolean isContinuous(int [] numbers) {
     int  count = 0 , min = Integer.MAX_VALUE,max = Integer.MIN_VALUE;
        for (int i = 0; i < numbers.length; i++) {
            if(numbers[i]==0){
            count++;}
            if(numbers[i] > max && numbers[i]!=0){
                max = numbers[i];
            }
            if(numbers[i] < min && numbers[i]!=0){
                min = numbers[i];
            }
        }
        
        int emptyNum = max-min-1-(numbers.length-2-count);
            if(emptyNum<0){
            return false;
        }
        if(count-emptyNum>=0){
            return true;
        }
        
        return false;
    }
}

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

这道题真的是典型的递归题,递归就要数学归纳,找出规律,真的是我的弱项,好难!

两种做法

一、模仿法,使用一个队列,看题解说是约瑟夫环的问题,首先我们先将所有人的序号存进队列中,然后(m-1)% list.size 移除队列

就是完全模仿游戏来进行

时间复杂度 O(n)

空间复杂度 O(n)

import java.util.List;
import java.util.ArrayList;
public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        if(n == 0 || m == 0)
            return -1;
     List<Integer> list = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int bt = 0;
        while(list.size()>1){

            bt = (bt+m-1)%list.size();
            list.remove(bt);
        }
        return list.get(0);
    
    }
}

第二种就是递归了 首先归纳一下 我的理解就是递归每一层的结果都对上一层有帮助且公式是一样的,就是要总结一个公式。

那么,我们可以思考 当剩余n个的时候 删除的是 (m-1)%n 那么我们怎么根据下一层的结果来推断这一层的结果呢? 如果我们知道

下一层法f(n-1,m)最终剩下的的是第x个的话,那么,我们这一层剩下的就是从(m)%n作为第一个开始之后的第x个,以此类推,就可以得出一个公式 f(n)=f(m/n+f(n-1)%n) 当然必须要有出口 f(1) = 0 只有一个的话是不会剩下人的

http://zhedahht.blog.163.com/blog/static/2541117420072250322938/

import java.util.List;
import java.util.ArrayList;
public class Solution {
    public int f (int n,int m){
        if (n == 1) return 0;
        return (m%n+f(n-1,m))%n;
    }
    public int LastRemaining_Solution(int n, int m) {
        if(n == 0 || m == 0)
            return -1;
        return f(n,m);
     }
}

别人写的这就是小细节!~!!

class Solution {
public:
    int LastRemaining_Solution(unsigned int n, unsigned int m)
    {
        if(n==0)
            return -1;
        if(n==1)
            return 0;
        else
            return (LastRemaining_Solution(n-1,m)+m)%n;
    }
};

14.求1+2+3+...+n

题目重述:求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

结题思路: 因为不能使用for循环 那么和判断, 那么就要使用什么东西来替代判断和循环, 循环就使用递归,判断就使用&& ||

这样就简单了,条件1&&条件2 如果条件1成立那么就只会执行条件一,发生短路。

15.数组中重复的数字 -- Java 实现

这是比较容易的实现,使用map,但是

时间复杂度O(n)

空间复杂度O (n)

import java.util.*;
public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if(numbers == null || length == 0){
            return false;
        }
        Arrays.sort(numbers);
        for(int i=0;i<length-1;i++){
            if(numbers[i] == numbers[i+1]){
                duplication[0] = numbers[i];
                return true;
            }
        }
        return false;
    }
}

16.表示数值的字符串

来玩正则表达式吧!~ 就是正则表达式的应用

public class Solution {
    public boolean isNumeric(char[] str) {
        String s = String.valueOf(str);
        return s.matches( "[\\+-]?\\d*(\\.?\\d+)?([eE][\\+-]?\\d+)?" );
    }
}

^ 和 美元符号框定正则表达式,它指引这个正则表达式对文本中的所有字符都进行匹配。如果省略这些标识,那么只要一个字符串中包含一个数字这个正则表达式就会进行匹配。如果仅包含 ^ ,它将匹配以一个数字开头的字符串。如果仅包含$ ,则匹配以一个数字结尾的字符串。

[-+]?

正负号后面的 ? 后缀表示这个负号是可选的,表示有0到1个负号或者正号

\\d*

\d的含义和[0-9]一样。它匹配一个数字。后缀 * 指引它可匹配零个或者多个数字。

(?:\\.\\d*)?

(?: …)?表示一个可选的非捕获型分组。* 指引这个分组会匹配后面跟随的0个或者多个数字的小数点。

(?:[eE][+\\-]?\d+)?

这是另外一个可选的非捕获型分组。它会匹配一个e(或E)、一个可选的正负号以及一个或多个数字。

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

解题思路:用一个数组(模拟哈希,因为字符就128个,没有哈希冲突,所以不需要用哈希表)其次就是我们要的是第一个没有重复的,就是先进先出,那就是队列了。所以这里我们用到 数组(hash) 和 队列

首先

先判断字符是否在数组里,不在将字符放到队列中,在的话不管

接着将队列出队,判断数组的值是否是1,是就返回答案 (这里是因为我们后面可能又出现这个字符了,但是先前已经放进队列了,需要判断一下)

如果队列空了还是没有找到就是没有

时间复杂度O128 空间复杂度 O128因为只要数组中有了就不加进队列的、

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;
public class Solution {
    int[] charCnt = new int[128];
    Queue<Character> queue = new LinkedList<Character>();
    //Insert one char from stringstream
    public void Insert(char ch)
    {   
       if(charCnt[ch]++==0){
           queue.add(ch);
       }
       
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce()
    {
     while(!queue.isEmpty()){
        if(charCnt[queue.peek()]==1){
            return queue.peek();
        }else{
            queue.remove();
        }
     }
        return '#';
      }
    }

18.链表中环的入口结点

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StO5Qktd-1618561745431)(image-20210125220212202.png)]

解题思路

哈希判断:节点一直next,将节点的值放入set中,如果next有null,那么就是空,如果next的值存在set中就退出,这个值得节点就是环的入口,浪费一个n的空间

On On

import java.util.HashSet;
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        HashSet<Integer> nums = new HashSet<>();
            while(pHead!=null){
               if(nums.contains(pHead.val)){
                   return pHead;
               }
                else{
                      nums.add(pHead.val);
                }
                   pHead=pHead.next;
            }
            return null;}
}

快慢节点: 一个走快一个走慢速度是两倍,肯定会相遇的,那么设相遇的节点位置为p,我们就可以列一个等式2(a+b)=a+nb+(n-1)c,可以算出 a == c,那么 从起始点 和 p 一起走,就会遇到的话就是q。!!!!!精彩

On O1

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

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

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
         if(pHead == null || pHead.next == null){
            return null;
        }
        ListNode l = pHead;
        ListNode r = pHead;
       while(l != null && r != null){
         
        l=l.next.next;
        r=r.next;
        if(l==r){  //先求出相遇的点
            l=pHead;
        while(l!=r){
            l=l.next;
            r=r.next;
        }
            return l;
        }
       }
        return null;
    }
}

19.二叉树的下一个结点 --Java实现

解题思路:简单实现模拟法,先找到根节点,然后中序遍历。将遍历的节点放到List中,在接着找到数列中的下一个就是了。

On On

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

    TreeLinkNode(int val) {
        this.val = val;
    }
}
*/
import java.util.ArrayList;
public class Solution {
    ArrayList<TreeLinkNode> nums = new ArrayList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {   TreeLinkNode n = pNode;
        while(pNode.next!=null){
           pNode = pNode.next;
        }
        TreeLinkNode head = pNode;
        zx(head);
        for(int i=0;i<nums.size();i++){
            if(nums.get(i) == n){
                return i==nums.size()-1?null:nums.get(i+1);
            }
        }
     return null; 
    }
     public void zx(TreeLinkNode pNode){
        if(pNode!=null){
             zx(pNode.left);
             nums.add(pNode);
             zx(pNode.right);
        }
    }

}

分析法:

//有右子树 下一个节点就是他的右子树的最左节点 //没有右子树,看它是否是父节点的左子树 //是的话就是其父节点 //不是的话就是找到当前节点的父节点,且它是父节点的左子树 如果没有找到就是null

On O1

import java.util.ArrayList;
public class Solution {
    ArrayList<TreeLinkNode> nums = new ArrayList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {  
        //有右子树 下一个节点就是他的右子树的最左节点
        //没有右子树,看它是否是父节点的左子树
        //是的话就是其父节点
        //不是的话就是找到当前节点的父节点,且它是父节点的左子树 如果没有找到就是null
        if( pNode.right!=null){
            TreeLinkNode tmp = pNode.right;
            while(true){
                if(tmp.left==null){
                    return tmp;
                }else{
                    tmp = tmp.left;
                }
            }
        }
        else{
            while(pNode.next != null){
            TreeLinkNode parent = pNode.next;
            if(parent.left == pNode){
                return parent;
            }
            pNode = parent;
        }
            return null;
        }
    }
}

20.把二叉树打印成多行 --Java实现

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

输入

{8,6,10,5,7,9,11}

返回值

[[8],[6,10],[5,7,9,11]]

解题思路: 首先这道题肯定是可以用层级遍历来做的,重点就是层级来存储到一个list中,因为每一层就放在一个队列中。所以有递归和非递归两种做法,两种并没有优劣之分。但是空间复杂度就有的说了,层级的话,最开始的思路是给每个节点设置一个map,存储他的层级,并且按照顺序创建的的队列,如果层级>=result的size,那么就要新建一个,否则就根据他的层级放在哪个list中。

方法一:非递归 On On

import java.util.ArrayList;


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

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

    }

}
*/
import java.util.Queue;
import java.util.LinkedList;
import java.util.*;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {         
    ArrayList<ArrayList<Integer> > results = new ArrayList<>();
    HashMap<TreeNode,Integer> deeps = new HashMap<>();
    Queue<TreeNode> queue =  new LinkedList<>();
    if(pRoot != null){
    queue.offer(pRoot);
    deeps.put(pRoot,0);}
        while(!queue.isEmpty()){
            TreeNode temp = queue.poll();
            int deep = deeps.get(temp);
            if(temp.left!=null){
              queue.offer(temp.left);
               deeps.put(temp.left,deep+1);
            }
             if(temp.right!=null){
              queue.offer(temp.right);
               deeps.put(temp.right,deep+1);
            }
            if(deep>=results.size()){
                ArrayList<Integer> list = new ArrayList<>();
                list.add(temp.val);
                results.add(list);
            }else{
                   ArrayList<Integer> list = results.get(deep);
                   list.add(temp.val);
            }
        }
       return results;
    }
    
}

方法二:递归 递归可以保证递归的每层就是他的层级,使用前序遍历是为了保证创建的时候按从上到下来分层。

import java.util.ArrayList;


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

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

    }

}
*/
import java.util.Queue;
import java.util.LinkedList;
import java.util.*;
public class Solution {
   ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> lists = new ArrayList<>();
        if (pRoot == null) {
            return lists;
        }
        dfs(pRoot, 0, lists);
        return lists;
    }
 
    private void dfs(TreeNode pRoot, int deep, ArrayList<ArrayList<Integer>> lists) {
        if (pRoot == null) {
            return;
        }
        if (deep >= lists.size()) {
            ArrayList<Integer> list = new ArrayList<>();
            list.add(pRoot.val);
            lists.add(list);
        } else {
            ArrayList<Integer> list = lists.get(deep);//和非递归不同的是他不是按顺序来的,所以要get
            list.add(pRoot.val);
        }
        dfs(pRoot.left, deep + 1, lists);
        dfs(pRoot.right, deep + 1, lists);
    }
}

方法三: 非递归 On O1(妙) 根据出列后,队列中新添加进去的数目来确定下一层的大小,从而不用记录层数。

import java.util.Queue;
import java.util.LinkedList;
import java.util.*;
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
    ArrayList<ArrayList<Integer> > results = new ArrayList<>();
    Queue<TreeNode> queue =  new LinkedList<>();
     if(pRoot == null) return results;
          queue.offer(pRoot);
        while(!queue.isEmpty()){
           int deep = queue.size();
           ArrayList<Integer> list = new ArrayList<>();
            for(int i =0 ; i< deep;i++){
            TreeNode temp = queue.poll();
            list.add(temp.val);
            if(temp.left!=null){
              queue.offer(temp.left);
            }
             if(temp.right!=null){
              queue.offer(temp.right);
            }
            }
            results.add(list);
        }
       return results;
}
}

21.重建二叉树

经典的题目 如果不会就丢人了

递归的经典使用 思路就是把每次递归都看成是建一个二叉树 然后将前序数组放入 中序数组放入 ,找到根节点

退出标志就是其中有一个遍历数组的长度是0 这里 Arrays.copyOfRange(ar,from,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 head = new TreeNode(pre[0]);
        int index = 0;
        for(int i = 0; i< in.length; i++){
            if(in[i]==pre[0])  index=i;
        }
         head.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,index+1),
                                           Arrays.copyOfRange(in,0,index));
         head.right = reConstructBinaryTree(Arrays.copyOfRange(pre,index+1,pre.length),
                                           Arrays.copyOfRange(in,index+1,in.length));
          return head;
}
}

21.反转链表

基本题 用三个指针解决

public class Solution {
    public ListNode ReverseList(ListNode head) {
     ListNode pre = null;
     ListNode cur = head;
     ListNode next = null;
        while(cur!=null){
            
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

22.二维数组中的查找

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

输入

7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]

返回值

true

解题思路: 真的精彩,一般有序的查找我们都是使用二分的嘛,但是这是二维的,显然就不好二分了,那么我们可以使用什么方法呢?

这里有一个信息,每一行都是递增,这不重要,每一列都递增,这就厉害了,我们可以先把位置放到左下或者右上,保证他是在这一行、列里最大的,那么比他小的是不是就是这一列的前面,比他大的只能从下一行找了,就是这样,最坏 m行+n列就能找到!!!

O(m+n) O(1)

平时我们不是要O(m*n)呀

public class Solution {
    public boolean Find(int target, int [][] array) {
   int i = 0;
   int j = array[0].length-1;
     for(i = 0; i<array.length;i++ ){
         for(j = array[i].length-1; j>-1;j--){
             if(target> array[i][j]){
                 break;
             }
             else if(target<array[i][j]){
                 continue;
             }
             else{
                 return true;
             }
         }
     }
        return false;
    }
}

23.从尾到头打印链表

1·listNode 是链表,只能从头遍历到尾,但是输出却要求从尾到头,这是典型的"先进后出",我们可以想到栈!

2·也可以想到反转链表

3·最好是递归

1.用栈 On On

import java.util.List;
import java.util.ArrayList;
import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        Deque nums = new ArrayDeque<Integer>();
        while(listNode!=null){
            nums.push(listNode.val);
            listNode=listNode.next;
        }
        ArrayList<Integer> results = new ArrayList<>();
        while(!nums.isEmpty()){
            results.add((Integer)nums.pop());
        }
        return results;
    }
}

2.反转链表 On O1

public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
       ArrayList<Integer> results = new ArrayList<>();
       if(listNode == null)return results;
       ListNode pre=null; 
       ListNode cur=listNode; 
       ListNode nex=listNode.next;
        while(cur!=null){
            nex = cur.next;
            cur.next = pre;
            pre = cur;
            cur =nex;
        }
        while(pre !=null){
            results.add(pre.val);
            pre = pre.next;
        }
        return results;
    }
}

3.用递归 好像简单点 On O1

public class Solution {
    //1.用栈  On On
    //2.反转链表  On O1
    //3.用栈 好像简单点  On O1
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
    ArrayList<Integer> results = new  ArrayList<Integer> ();
        reverse(listNode,results);
        return results;
    }
    public void reverse(ListNode listNode , ArrayList<Integer> results){
        if(listNode!=null){
           reverse(listNode.next,results);
           results.add(listNode.val);
        }
    }
}

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

这里要求的是排序,可是要保证稳定性,那么快排就没用,只能用冒泡 插入 等 所以时间复杂度是On^2 O1

但是牺牲空间的话是可以达到On On,用一个数组存储 我的想法是使用一个双端队列 偶数从尾巴进去,奇数从头进去

类似插入

public class Solution {
    public void reOrderArray(int [] array) {
        int i = 0;
      for (int j=0; j<array.length; ++j) {
          if (array[j]%2==1) {
              int tmp = array[j];
              for (int k=j-1; k>=i; --k) {
                  array[k+1] = array[k];
              }
              array[i++] = tmp;
          }
      }
    }
}

类似冒泡

空间

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

解题思路 快慢指针 快指针先走k步,再一起走,快指针走到最后慢指针就是倒数的位置。

On O1

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

    ListNode(int val) {
        this.val = val;
    }
}*/
public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        ListNode low = head;
        int i =0;
        ListNode fast = head;
        while(fast!=null){
            if(i==k){
                fast = fast.next;
                low = low.next;
            }
            else{
             i++;
            fast=fast.next;
            }
            
        }
        if(i==k)return low;
        else return null;
    }
}

26.树的子结构 (必须二刷)

这道题有够恶心,看的时候感觉很简单,写起来 哎 一言难尽 以后写代码还是一步一步解析,不要贪图求快,重点 && 和 || 的短路作用

&& 第一个条件错了就不会下面的条件 || 是可以的 所以用来替代 if判断 ,坑爹的是我看的题解都是|| && 一个还好递归进行 你就傻了

解题思路 :

  1. 我首先的思路就是 两个树都先序遍历 放进队列中,然后看子队列是否是母队列 Om+m Om+m
  1. 使用递归,先序遍历父树,
  2. 在父树的每个节点都判断是否含有子树,一旦有的话直接返回是
  1. 判断根节点是否相同
  2. 再递归判断左节点 右节点
  3. 退出标志 子树为空 true 父树为空 子树不为空 父子节点不同
  1. 退出标志 节点为空或者找到我们要的子树

Om+m Om+m

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

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

    }

}
*/
public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        return 
            (root1 != null&& root2!= null)&&(
            isSubTree(root1,root2)
            || HasSubtree(root1.left,root2)
            || HasSubtree(root1.right,root2));
            }
      public boolean  isSubTree(TreeNode root1,TreeNode root2){
        if(root2 == null) return true;
        if(root1 == null||root1.val!=root2.val)return false;
        return isSubTree(root1.left,root2.left)
              &&isSubTree(root1.right,root2.right);
      }
}

26.包含min函数的栈 要求 O1

解题思路: 想要 O1那么就要空间换时间,因为我们找到最小的话,用排序的话就要On,那么我们可以用双栈,一个栈存值,一个是用来存最小值,因为栈的进出是顺序的,所以每次出栈是无所谓的,只要在进栈的时候将值和栈顶最小值对比,如果小就放进去,如果不是就继续将栈顶的值再压一次,其实这里也可以优化,只有是最小值或者是小于最小的才压,其余不压,出的时候只有等于最小值才出。O1 O(n-*)

import java.util.Stack;

public class Solution {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> minval = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if (minval.isEmpty()){
            minval.push(node);
        }
        else{
        if(node<=minval.peek()){
            minval.push(node);
        }else{
            minval.push(minval.peek());
        }
        }
    }
    
    public void pop() {
        stack.pop();
        minval.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minval.peek();
    }
}

//空间复杂度 O(n-*)
import java.util.Stack;

public class Solution {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> minval = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if (minval.isEmpty()){
            minval.push(node);
        }
        else{
        if(node<=minval.peek()){
            minval.push(node);
        }
        }
    }
    
    public void pop() {

        if(stack.peek()<=minval.peek()){
             minval.pop();
        }
        stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minval.peek();
    }
}

26.包含min函数的栈 要求 O1

解题思路: 想要 O1那么就要空间换时间,因为我们找到最小的话,用排序的话就要On,那么我们可以用双栈,一个栈存值,一个是用来存最小值,因为栈的进出是顺序的,所以每次出栈是无所谓的,只要在进栈的时候将值和栈顶最小值对比,如果小就放进去,如果不是就继续将栈顶的值再压一次,其实这里也可以优化,只有是最小值或者是小于最小的才压,其余不压,出的时候只有等于最小值才出。O1 O(n-*)

import java.util.Stack;

public class Solution {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> minval = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if (minval.isEmpty()){
            minval.push(node);
        }
        else{
        if(node<=minval.peek()){
            minval.push(node);
        }else{
            minval.push(minval.peek());
        }
        }
    }
    
    public void pop() {
        stack.pop();
        minval.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minval.peek();
    }
}
//优化空间 只有最小值的时候最小栈才出栈
import java.util.Stack;

public class Solution {

    Stack<Integer> stack = new Stack<>();
    Stack<Integer> minval = new Stack<>();
    public void push(int node) {
        stack.push(node);
        if (minval.isEmpty()){
            minval.push(node);
        }
        else{
        if(node<=minval.peek()){
            minval.push(node);
        }
        }
    }
    
    public void pop() {

        if(stack.peek()<=minval.peek()){
             minval.pop();
        }
        stack.pop();
    }
    
    public int top() {
        return stack.peek();
    }
    
    public int min() {
        return minval.peek();
    }
}

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

解题思路:

首先树一般使用递归

判断是否是二叉树,首先递归,1你的左子树是二叉树 你的右子树是二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sw7HFdDy-1618561745433)(image-20210207191826759.png)]

然后退出的条件就是根据后序遍历的特点来了

后序遍历 根节点在后面 左子树部分都在前面右子树都在后面

1、首先传入的是空的话直接是false

2、找到左子树的部分后如果后面有小于根的部分那么就是错的

4、直到左右子树都为空(后序遍历的数组为空)就是true

import java.util.*;
public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length==0)return false;
        return isBts (sequence);
    }
    public boolean isBts(int [] sequence){
        if(sequence.length==0)return true;
        int l = -1;
        int m = sequence.length-1;
        for(int i=0;i<m;i++){
            if(sequence[i]>sequence[m]&&l==-1)
                l=i;
            if(sequence[i]<sequence[m]&&l!=-1)
                return false;
        }
        if(l==-1)return true; 
        return isBts(Arrays.copyOfRange(sequence,0,l))
                &&isBts(Arrays.copyOfRange(sequence,l,m));
    }
}

28.-二叉树中和为某一值的路径

这题是典型的的二叉树深度遍历的题,我的经验就是树就是递归,这题就和求高度是一模一样的,难点在于路径队列的保存,我们都知道java是引用传递而不是值传递,保存后,在改变以前保存的也会改变,那么2步来解决:

1.找到后new一个队列ArrayList<Integer> s = new ArrayList<Integer>(result);

2每次返回上一层的时候删除队列中当前访问节点的值 result.remove(result.size()-1);//删除最后一个位置的数值

On On

import java.util.*;
/**
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

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

    }

}
*/
public class Solution {
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
        ArrayList<Integer> result = new  ArrayList<Integer>();
        ArrayList<ArrayList<Integer>> results = new ArrayList<ArrayList<Integer>>();
        dfs(root,target,0,result,results);
        return results;
    }
    public void dfs(TreeNode root,int target,int tmp,ArrayList<Integer> result
              , ArrayList<ArrayList<Integer>> results){
        if(root==null )return;
        result.add(root.val);
        tmp+=root.val;
        if(tmp==target&&root.left==null&&root.right==null){
            ArrayList<Integer> s = new  ArrayList<Integer>(result);
            results.add(s);
        }
        dfs(root.left,target,tmp,result,results);
        dfs(root.right,target,tmp,result,results);
        result.remove(result.size()-1);//删除最后一个位置的数值
    }
}

28.复杂链表的复制

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解题思路: 分析题目,这是一个复杂链表,且要实现深拷贝,就是每一个节点都要自己new,问题就是随机节点新建后,必须存起来,等next范文到的时候不能新new,因为是同一个. 所以我的做法是将new过的节点放进map中,遇到new过的就拿出来,否则就新new.

我的做法代码比较繁琐,看比较简单的做法是先遍历一遍,将所有的节点和新的节点放进map,接着继续遍历一遍,将map中的拿出来即可!!

On On

/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}
*/
import java.util.*;
public class Solution {
    public RandomListNode Clone(RandomListNode pHead)
    {    
        HashMap<RandomListNode,RandomListNode> randoms = new HashMap<>();
        if (pHead == null) return null;
        RandomListNode head = new RandomListNode(pHead.label);
        RandomListNode tmp = head;
        while(pHead!=null){
        //处理随机节点
            if(pHead.random==null){
              tmp.random=null;
          }else{
               //先判断随机结点是否已经拷贝
          if(randoms.containsKey(pHead.random)){
              tmp.random=randoms.get(pHead.random);
          }else{
              tmp.random = new RandomListNode(pHead.random.label);
              randoms.put(pHead.random,tmp.random);
          }
          }
            //处理下一个节点
          if(pHead.next==null){
              tmp.next=null;
          }else{
          if(randoms.containsKey(pHead.next)){
              tmp.next=randoms.get(pHead.next);
          }else{
              tmp.next = new RandomListNode(pHead.next.label);
              randoms.put(pHead.next,tmp.next);
          }}
            tmp = tmp.next;
            pHead = pHead.next;
        }
        return head;
    }
}

//简单做法
import java.util.HashMap;
public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null) return null;

            // target 作为将要返回的头,记住要new的
            RandomListNode target = new RandomListNode(pHead.label);
            // cur 获取链表头
            RandomListNode cur = pHead;
            // p   获取新链表头
            RandomListNode p = target;

            HashMap<RandomListNode, RandomListNode> map = new HashMap<>();

            // 由pHead将所有值存入map,每一个结点都要new的
            while (pHead != null) {
                map.put(pHead, new RandomListNode(pHead.label));
                pHead = pHead.next;
            }

            // target作为新链表的头,由cur,p移动来复制链表
            while (cur != null) {
                p.next = map.get( cur.next );
                p.random = map.get( cur.random );

                cur = cur.next;
                p = p.next;
            }

            return target;
    }
}

28.字符串的排列

真的惡心的題目

遞歸回溯 也是動態規劃 用set可以进行去重,并且可以达到按字母顺序排序。

时间复杂度:O(n!),比如3个字符的全n排列有6种 空间复杂度:O(n),原地交换 set

import java.util.*;
public class Solution {
    public ArrayList<String> Permutation(String str) {
       //递归法 递归退出 字符只剩一个
       //递归体 固定字符串中的每一个字符到第一个位置 后面的字符串继续递归
      ArrayList<String> result = new ArrayList<>();
      if(str.length()>0&&str!=null){
          dfs(str.toCharArray(),0,result);
          //用这个方法需要排序
          Collections.sort(result);
      }
        return result;
    }
    private void dfs(char []str,int index,ArrayList<String> result){
        //退出递归
        if(index == str.length-1)
            result.add(String.valueOf(str));
        else{
        HashSet<Character> charSet = new HashSet<>();
        for(int i = index;i<str.length;i++){
            if(i == index || !charSet.contains(str[i])){
            charSet.add(str[i]);
            swag(str,index,i);
            dfs(str,index+1,result);
            swag(str,i,index);
            }
        }
        }
    }
    private void swag(char []str,int i,int j){
        char tmp = str[i];
        str[i] = str[j];
        str[j] = tmp;
    }
}

我也是無語了,這樣寫就超時,我還是用回溯法吧

/**
     * 非递归法 : 采用字典枚举法,例如123456 显然为字典的第一个  654321 就是最后一个
     * 有规律
     * 一个全排列可看做一个字符串,字符串可有前缀、后缀。
     * 生成给定全排列的下一个排列.所谓一个的下一个就是这一个与下一个之间没有其他的。
     * 这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
     * <p>
     * [例]839647521是1--9的排列。1—9的排列最前面的是123456789,最后面的987654321,
     * 从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。
     * <p>
     * 【例】 如何得到346987521的下一个
     * 1,从尾部往前找第一个P(i-1) < P(i)的位置
     * 3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
     * 最终找到6是第一个变小的数字,记录下6的位置i-1
     * <p>
     * 2,从i位置往后找到最后一个大于6的数
     * 3 4 6 -> 9 -> 8 -> 7 5 2 1
     * 最终找到7的位置,记录位置为m
     * <p>
     * 3,交换位置i-1和m的值
     * 3 4 7 9 8 6 5 2 1
     * 4,倒序i位置后的所有数据
     * 3 4 7 1 2 5 6 8 9
     * 则347125689为346987521的下一个排列
     */
import org.junit.jupiter.api.Test;

import java.util.*;

public class Solution {
   
    @Test
    public void main() {
        System.out.println(System.currentTimeMillis());
        System.out.println(this.Permutation("abcdefgsss"));
        System.out.println(System.currentTimeMillis());
    }
    public ArrayList<String> Permutation(String str) {
        ArrayList<String> list = new ArrayList<String>();
        if(str.length()==0||str==""){
            return list;}
        //先找到第一个排列
        char[] tmp = str.toCharArray();
        Arrays.sort(tmp);
        list.add(String.valueOf(tmp));
        int length = tmp.length-1;
        //找下一个
        while (true) {
            int cur = length;
            int pre = cur - 1;
            while (pre > -1&&tmp[cur]<=tmp[pre]) {
                     cur--;
                     pre--;
            }
            //找到最後一個的標志
            if(pre == -1) break;

            int j = length;
            while(tmp[j]<tmp[pre]){
                j--;
            }
            swag(tmp,pre, j);
            reverse(tmp,pre);
            list.add(new String(tmp));

        }
        return list;
    }

    private void reverse(char[] tmp, int pre) {
        for(int j = 0;j<tmp.length/2;j++){
            int n = pre+1+j;
            int m = tmp.length-1-j;
            if(n<m)
            swag(tmp,n,m);
        }
    }

    private void swag(char[] tmp, int i, int j) {
        char charTmp = tmp[j];
        tmp[j] = tmp[i];
        tmp[i] = charTmp;
    }

}

29.求给定数组的topK小问题。

就是排序问题 复习一下 这题大根堆和快排好点 我思路是冒泡

import java.util.*;
public class Solution {
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
        ArrayList<Integer> resullt = new ArrayList<Integer>();
        if(k==0||k>input.length)return resullt;
        for(int i=0;i<k;i++){
            for(int j =input.length-1;j>i;j--){
                if(input[j]<input[j-1]){
                    int tmp = input[j];
                    input[j] = input[j-1];
                    input[j-1] = tmp;
                }
            }
            resullt.add(input[i]);
        }
        return resullt;
    }
}

30.把数组排成最小的数

时间复杂度Onlogn(明天看一下排序的源码) 空间复杂度 On

import java.util.*;

//方法一 暴力法 全排列然后比较大小
//方法二 重写compare方法 使用排序 两个字符串大小的比较按照 两种比较方法(我觉得好用的就是比较两个字符串s1, s2大小的时候,先将它们拼接起来,比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面,所以按这个规则,s2就应该排在s1前面。 ,突然大悟。)
//另一个是 例如 321 和 3  就是321 和 3 每个比 321 33  2<3所以 321<33

public class Solution {
    public String PrintMinNumber(int [] numbers) {

        String result="";
        ArrayList<Integer> list=new ArrayList<>();
        for (int num:numbers) list.add(num);

        Collections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                String s1=o1+""+o2;
                String s2=o2+""+o1;
                return s1.compareTo(s2);
            }
        });

        for (int i:list) result+=i;

        return result;
    }
}

31.剑指offer-33-丑数

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

2x*3y*5^z

所以只需要把得到的丑数不断地乘以2、3、5之后并放入他们应该放置的位置即可

public class Solution {
    public int GetUglyNumber_Solution(int index) {
         if(index <= 0)return 0;
        //思路 一个队列存放从小到大的每个丑数,将队列中的每个丑数都*2、3、5,就能获得所有的丑数,
     int[] result = new int [index];
     int p2 = 0,p3 = 0,p5 = 0;
        result[0] = 1;
        for(int i =1;i<index;i++ ){
          //每次将2 3 5 中的最小值放进去,保证所有的丑数都在result中
          result[i] = Math.min(result[p2]*2, Math.min(result[p3]*3, result[p5]*5));
          // 保证丑数中每个位置 *2 3 5 均不在result中,相等的话就代表已经处理过了,可以再前进一步*235
           if(result[i] == result[p2]*2) p2++;
           if(result[i] == result[p3]*3) p3++;
           if(result[i] == result[p5]*5) p5++;
        }
        return result[index-1];
    }
}

如果遍历的话是不现实的,前面无所谓,后面的显然会越来愈大

32.翻转单词顺序列

On On 很牛的写法 相当于翻转两次

public class Solution {
    public String ReverseSentence(String str) {
        if(str.length() == 0 || str == null){
            return "";
        }
        char[] c = str.toCharArray();
        for(int i = 0, l = 0; i <= c.length; i++){
            if(i == c.length || c[i] == ' ' ){
                reverse(c, l, i - 1);//单词翻转
                l = i + 1;//单词的初始位置
            }
            
        }
        reverse(c, 0, c.length - 1);//排序翻转
        return new String (c);
    }
    private void reverse(char [] c, int l, int h){
        while(l < h){
            swap(c, l++, h--);
        }
    }
    private void swap(char [] c, int l, int h){
        char t = c[l];
        c[l] = c[h];
        c[h] = t;
    }
}

@Override
        public int compare(Integer o1, Integer o2) {
            String s1=o1+""+o2;
            String s2=o2+""+o1;
            return s1.compareTo(s2);
        }
    });

    for (int i:list) result+=i;

    return result;
}

}

### 31.剑指offer-33-丑数

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

所以**只需要把得到的丑数不断地乘以2、3、5之后并放入他们应该放置的位置即可**,

```java
public class Solution {
    public int GetUglyNumber_Solution(int index) {
         if(index <= 0)return 0;
        //思路 一个队列存放从小到大的每个丑数,将队列中的每个丑数都*2、3、5,就能获得所有的丑数,
     int[] result = new int [index];
     int p2 = 0,p3 = 0,p5 = 0;
        result[0] = 1;
        for(int i =1;i<index;i++ ){
          //每次将2 3 5 中的最小值放进去,保证所有的丑数都在result中
          result[i] = Math.min(result[p2]*2, Math.min(result[p3]*3, result[p5]*5));
          // 保证丑数中每个位置 *2 3 5 均不在result中,相等的话就代表已经处理过了,可以再前进一步*235
           if(result[i] == result[p2]*2) p2++;
           if(result[i] == result[p3]*3) p3++;
           if(result[i] == result[p5]*5) p5++;
        }
        return result[index-1];
    }
}

如果遍历的话是不现实的,前面无所谓,后面的显然会越来愈大

32.翻转单词顺序列

On On 很牛的写法 相当于翻转两次

public class Solution {
    public String ReverseSentence(String str) {
        if(str.length() == 0 || str == null){
            return "";
        }
        char[] c = str.toCharArray();
        for(int i = 0, l = 0; i <= c.length; i++){
            if(i == c.length || c[i] == ' ' ){
                reverse(c, l, i - 1);//单词翻转
                l = i + 1;//单词的初始位置
            }
            
        }
        reverse(c, 0, c.length - 1);//排序翻转
        return new String (c);
    }
    private void reverse(char [] c, int l, int h){
        while(l < h){
            swap(c, l++, h--);
        }
    }
    private void swap(char [] c, int l, int h){
        char t = c[l];
        c[l] = c[h];
        c[h] = t;
    }
}

举报

相关推荐

剑指offer

剑指Offer

剑指 offer

剑指offer、leetcode

【 剑指 offer 01】

剑指Offer(二)

0 条评论