剑指Offer-Java刷题个人题解与总结

 刷题

Java刷题技巧

Java输入输出问题

Scanner sc=new Scanner(System.in);

输入一行字符串:sc.nextLine()

输入一个基本类型:sc.nextInt、nextLong、nextDouble

输入一个字符:scanner.next().charAt(0);

输出

System.out.printf("%d",value);

力扣刷题常见的特殊情况

 1,常见的&操作便捷的含义

n&(-n)表示去lowbits,取二进制最低位,树形数组中应用。

n&(n-1)表示将二进制最低位进行取反。

2,常见的list转换成[]int、[]String方法

list-->[]int:list.stream().mapToInt(Integer::valueOf).toArray();

list-->[]String:list.toArray(new String[0]);

3,list转换成[][]int方法

ArrayList<int []> res=new ArrayList<>();

res.toArray(new int [0][]);

4,堆数据结构

PriorityQueue<Integer> q=new PriorityQueue<>(new Comparator<Integer>(

int compare(Integer o1,Integer o2) {return 0;}

));

5.Java中Long转换成int

(int)Long,可能出现溢出,且java不支持。

Long.intValue(),Long对象中包含此转换方法。

Integer.parseInt(String.valueOf(long)),先转成字符串,在转成int。

6、快速幂

思想:

88 * 0110 0011(2) = 88 * 0 * 2^7 
                  + 88 * 1 * 2^6 
                  + 88 * 1 * 2^5 
                  + 88 * 0 * 2^4 
                  + 88 * 0 * 2^3 
                  + 88 * 0 * 2^2 
                  + 88 * 1 * 2^1
                  + 88 * 1 * 2^0
代码:

int quickMulti(int A, int B) {
    int ans = 0;
    for ( ; B; B >>= 1) {
        if (B & 1) {
            ans += A;
        }
        A <<= 1;
    }
    return ans;

7、摩尔投票法

应用:找出数组中出现次数超过一半的数

具体步骤:相同的数,则保留记录,不同的数则删除,,直到末尾。

8、树状数组

应用:求逆序数

class BIT{
    int []tree;
    int n;
    public BIT(int n){
         tree=new int[n+1];
         this.n=n;
    }
    public int lowbit(int n){
        return (n)&(-n);
    }
    public int query(int index){
        int res=0;
        while(index!=0){
            res+=tree[index];
            index-=lowbit(index);
        }
        return res;
    }
    public void add(int index){
        while(index<=n){
            tree[index]++;
            index-=tree[index];
        }
    }
}

9、质数的判断方法

1,常见方法,直接通过遍历到n的开平法进行整除判断,效率不高。

2,通过标志方法,设置一个bool数组,先进行初始化,奇数设置为true,偶数设置为false,只需将前面为true表示为质数的倍数设置为flase即可,效率较上面高。

3,质数分布的规律:大于等于5的质数一定和6的倍数相邻。例如5和7,11和13,17和19等等;

bool isPrime( int num )
{
    //两个较小数另外处理
    if(num == 2||num == 3 )
        return 1;
    //不在6的倍数两侧的一定不是质数
    if(num % 6 != 1&&num % 6 != 5)
        return 0;
    int tmp = sqrt(num);
    //在6的倍数两侧的也可能不是质数
    for(int i = 5;i <= tmp;i += 6)
        if(num %i == 0||num % (i+ 2) == 0)
            return 0;
    //排除所有,剩余的是质数
    return 1;

10、博弈-NIm游戏

n个石子,两个人取,每次可以取1-m个石子,谁取到最后一个石子就赢得比赛,取的局面为(m+1)的时候必输,且为(m+1)的倍数时候也必输。

11,new Comparator<>(){}

int compare(Integer o1,Integer o2)方法中的简单写法为:return o1-o2;

一般最简单写法:

  • Collections.sort(arr,(o1,o2)->o1.val-o2.val);// 升序
  • Collection.sort(arr,(o1,o2)->{return o1.val-o2.val;}
  • new Comparator<>(){}中的int compare(Integer o1,Integer o2)
  • Arrays.sort(T[],new Comparator<>(){});

12,Arrays.copyOf(arr[],len)和System.arraycopy(src, srcPos, dest, destPos, length)

前者一般是数组的扩容,产生一个新的对象,后者是数组的复制,会对源数组进行赋值,不会产生新的数组。

13,快速排序

思想:随机取一个值作为基准(一般去做下标),对数组的值分为大于和小于基准两部分,然后采用递归的方式全部使得数组有序。

  public static void quickSort(int []nums,int l,int r){
        if(l<r){
            int index=partition(nums,l,r);
            //分治递归
            quickSort(nums,l,index-1);
            quickSort(nums,index+1,r);
        }
    }
    // partition就是让按照基准划分两部分
    public static int partition(int []nums,int l,int r){
        int flag=l;//标识
        int index=l+1;//标识右部分的初始位置
        for(int i=index;i<=r;i++){
            if(nums[i]<nums[flag]){
                // 交换
                swap(nums,i,index);
                index++;
            }
        }
        //将flag和前半部分最后一个进行交换
        swap(nums,index-1,flag);
        // index-1是标识的下标
        return index-1;
    }
    public static  void swap(int [] nums,int i,int j){
        int t=nums[i];
        nums[i]=nums[j];
        nums[j]=t;
    }

14,N皇后问题-回朔法

NxN的期盼,放置n个皇后,要求行列对角线不能重复。

  1. 思路一:一行一行进行试探,每次试探一步进行标记,然后求出所有的可能。
  2. 思路二:用arr[n]记录每次放皇后的列行,arr[i]表示第i行的皇后位置放在arr[i]位置上面,满足列不相等的情况只要arr[i]!=arr[j](j<i),对角线不相等的情况是i+arr[i]!=j+arr[j],进行递归即可。
 class Main {
    static int resultCount = 0;

    private static boolean place(int[] arr, int s) {
        for(int i = 0; i < s; i++) {
            if((arr[i] == arr[s]) || (Math.abs(i-s) == Math.abs(arr[i]-arr[s]))) {
                return false;
            }
        }

        return true;
    }

    public static void tria(int[] arr, int i, int n) {
        if(i >= n) {
            // 打印出来
            for(int j=0;j<8;j++){
                for(int k=0;k<8;k++){
                    if(arr[j]==k){
                        System.out.print("*");
                    }else{
                        System.out.print(".");
                    }
                }
                System.out.println();

            }
            System.out.println("------------------------");
            ++resultCount;
        } else {
            for(int j = 0; j < n; j++) {
                arr[i] = j;
                if(place(arr, i)) {
                    tria(arr, i+1, n);
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] queen = new int[8];
        tria(queen, 0, 8);

        System.out.println(resultCount);
    }
}

15,Collections常用的工具方法

排序操作

void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面

 查找替换

int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素
int frequency(Collection c, Object o)//统计元素出现次数
int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target)
boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素

 安全(Collections可以将不安全的容易变为安全的)

synchronizedCollection(Collection<T>  c) //返回指定 collection 支持的同步(线程安全的)collection。
synchronizedList(List<T> list)//返回指定列表支持的同步(线程安全的)List。
synchronizedMap(Map<K,V> m) //返回由指定映射支持的同步(线程安全的)Map。
synchronizedSet(Set<T> s) //返回指定 set 支持的同步(线程安全的)set。

 16,Arrays.asList(new int[]{}

数组转换成集合,直接转换成的是Array&ArrayList,并不是真正的ArrayList,在在一个new ArrayList<>(Arrays.asList(new int[]{}));

17,LinkedHashMap

应用:LRU

构造方法:public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

accessOrder=false表示插入顺序,true表示访问顺序,且HashMap并不能保持插入顺序,LinkedHashMap的子类从写removeEldestEntry()方法可以实现LRU固定缓冲区大小置换的功能。

18,拓扑排序

多任务存在先后,找出合法的任务执行序列,应用(课程表中的先修问题-解决存在环的问题)

思想:可以将入度为0的结点加入队列,然后进行出队为key,将其余入度为key的结点入度数减一,并将入度为0的加入队列,总的出队数等于总的结点数的话则表明存在拓扑排序。

19,数组中第k大的数-面试题

排序+找下标

小根堆,将数组一步一步加入根堆,节点数量超出k范围则剔除,直到末尾。

快速标记法-基于快速排序实,partition不变,增加quickSelect方法

quickSelect(int l,int r,int k,int []nums){
    if(l>r)return;
    while(l<r){
        int p=partition(l,r,nums);
        if(p==k)return nums[p];
        else if(p>k) r=p-1;
        else l=p+1;
}
}

剑指offer(第二版)

剑指 Offer 03 数组中重复的数字

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

方法:

  1. 排序+遍历
  2. 用Hashset容器进行去重
  3. 临时数组tep,用于记录出现的次数
class Solution {
    public int findRepeatNumber(int[] nums) {
        int tep[]=new int[nums.length];
        int n=nums.length;
        for (int i=0;i<nums.length;i++)
        {
           tep[nums[i]]+=n;
        }
        for (int i=0;i<nums.length;i++)
        {
            if(tep[nums[i]]>n)
            {
                return nums[i];
            }
        }
        return 0;
    }
}

剑指 offer 04 二维数组中查找

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

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

给定 target = 5,返回 true

给定 target = 20,返回 false

 方法:

  1. 二分查找,对每行进行二分查找。
class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        for (int i=0;i<matrix.length;i++)
        {
           if(binSerach(0,matrix[0].length-1,target,i,matrix))
           {
               return true;
           }
        }
        return false;
    }
    public boolean binSerach(int l,int r,int tar,int index,int [][]matrix){
        if (l>r){
            return false;
        }
        int m=(l+r)/2;
        if(matrix[index][m]==tar)
        {
            return true;
        }else if(matrix[index][m]>tar)
        {
             return binSerach(l,m-1,tar,index,matrix);
        }else{
             return binSerach(m+1,r,tar,index,matrix);
        }
      
    }
}

剑指 Offer 05 替换空格

难度简单224

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."
class Solution {
    public String replaceSpace(String s) {
        return s.replace(" ","%20");
    }
}

剑指 Offer 06 从尾到头打印链表

难度简单247

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

限制:

0 <= 链表长度 <= 10000

 方法:

将list转换成int []

  1. 先存入list在添加到int[]
  2. 直接通过list.stream().mapToInt(Integer::valueOf).toArray();进行转换
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        LinkedList<Integer> res=new LinkedList<>();
        while(head!=null)
        {
            res.addFirst(head.val);
            head=head.next;
        }
        // int [] res1=new int[res.size()];
        // for(int i=0;i<res.size();i++)
        // {
        //     res1[i]=res.get(i);
        // }
        // return res1;
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

 剑指 Offer 07 重建二叉树

难度中等685

输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。

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

 方法:

  1. 以前序遍历为模板,找出前序遍历的根、左、右进行划开,对应中序遍历,找出前序遍历除根节点外左子树的长度,和右子树的常数,递归进行创造建树。
class Solution {
    Map<Integer,Integer> mymap;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        mymap=new HashMap<>();
        int len=preorder.length;
        // 将中序遍历按照键(节点值)值(索引位置)
        for(int i=0;i<len;i++)
        {
            mymap.put(inorder[i],i);
        }
        return  creatTree(preorder,inorder,0,len-1,0,len-1);
    }
    public TreeNode creatTree(int[] preorder,int [] inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right){
        if(preorder_left>preorder_right)return null;
        //创建根节点
        TreeNode root=new TreeNode(preorder[preorder_left]);
        // 查找根节点在中序遍历的位置
        int root_index=mymap.get(preorder[preorder_left]);
        int left_length=root_index-inorder_left;
        //得到根的左子树
        root.left=creatTree(preorder,inorder,preorder_left+1,preorder_left+left_length,inorder_left,root_index-1);
        //得到根的右子树
        root.right=creatTree(preorder,inorder,preorder_left+left_length+1,preorder_right,root_index+1,inorder_right);
        return root;
    }
}

剑指 Offer 09 用两个栈实现队列

难度简单457

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

方法:

  1. 直接用LinkedList中的方法
  2. 用两个栈的方法
方法一
class CQueue {

    LinkedList<Integer> res;
    public CQueue() {
        res=new LinkedList<>();
    }
    
    public void appendTail(int value) {
        res.addLast(value);
    }
    
    public int deleteHead() {
        if(res.size()==0){
            return -1;
        }
        int t=res.getFirst();
        res.removeFirst();
        return t;
    }
}
方法二

class CQueue {

    Stack <Integer> a=new Stack<>();
    Stack <Integer> b=new Stack<>();
    public CQueue() {
       
    }
    
    public void appendTail(int value) {
        a.push(value);
    }
    
    public int deleteHead() {
        if(a.size()==0){
            return -1;
        }

        while(!a.isEmpty()){
            b.push(a.peek());
            a.pop();
        }

        int res=b.peek();
        b.pop();

        while(!b.isEmpty()){
            a.push(b.peek());
            b.pop();
        }

        return res;
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

 剑指 Offer 10 斐波那契数列

难度简单307

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

简单的迭代思想

class Solution {
    public int fib(int n) {
     if(n==0)return 0;
     if (n==1)return 1;
      int t1=0;
      int t2=1;
      int t3=0;
       for(int i=2;i<=n;i++)
       {
           t3=t1+t2;
           if(t3>1000000007){
               t3=t3%1000000007;
           }
           t1=t2;
           t2=t3;
       }
       return t3;
    }
}

剑指 Offer 10 青蛙跳台阶问题

难度简单246

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

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

 方法:

  1. 动态规划:// dp[i]表示青蛙跳到第i个台阶共需要多少种跳法
class Solution {
    public int numWays(int n) {
        // dp[i]表示青蛙跳到第i个台阶共需要多少种跳法
        int dp[]=new int[101];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-2]+dp[i-1];
            if (dp[i]>1000000007){
                dp[i]%=1000000007;
            }
        }
        return dp[n];
    }
}

剑指 Offer 11 旋转数组的最小数字

难度简单542

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。  

class Solution {
    public int minArray(int[] numbers) {
        Arrays.sort(numbers);
        return numbers[0];
    }
}

 剑指 Offer 12 矩阵中的路径

难度中等529

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。

 方法

  1. 采用DFS方法对路径进行遍历剪枝。
class Solution {
    private boolean res=false;
    public boolean exist(char[][] board, String word) {
        boolean [][]visited=new boolean[board.length][board[0].length];
        for(int i=0;i<board.length;i++)
        {
            for(int j=0;j<board[0].length;j++){
                if(board[i][j]==word.charAt(0))
                {
                    visited[i][j]=true;
                    dfs(i,j,board,1,word,visited);
                    visited[i][j]=false;
                }
            }
        }
        return res;
    }
    public void dfs(int x,int y,char[][] board,int index,String word,boolean [][]visited){
        int m=board.length;
        int n=board[0].length;
        if(index==word.length()){
            res=true;
            return;
        }
        for(int i=0;i<4;i++)
        {
             if(res){
                return;
            }
            switch(i){
                case 0:
                if(x-1>=0&&visited[x-1][y]==false&&word.charAt(index)==board[x-1][y])
                {
                    visited[x-1][y]=true;
                    dfs(x-1,y,board,index+1,word,visited);
                    visited[x-1][y]=false;
                }
                break;
                case 1:
                if(x+1<m&&visited[x+1][y]==false&&word.charAt(index)==board[x+1][y])
                {
                    visited[x+1][y]=true;
                    dfs(x+1,y,board,index+1,word,visited);
                    visited[x+1][y]=false;
                }
                break;

                case 2:
                if(y+1<n&&visited[x][y+1]==false&&word.charAt(index)==board[x][y+1])
                {
                    visited[x][y+1]=true;
                    dfs(x,y+1,board,index+1,word,visited);
                    visited[x][y+1]=false;
                }
                 case 3:
                if(y-1>=0&&visited[x][y-1]==false&&word.charAt(index)==board[x][y-1])
                {
                    visited[x][y-1]=true;
                    dfs(x,y-1,board,index+1,word,visited);
                    visited[x][y-1]=false;
                }
                break;
            }
        }

    }
}

 剑指 Offer 13 机器人的运动范围

难度中等452

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

方法:

  1. dfs思路
class Solution {
    int res=0;
    public int movingCount(int m, int n, int k) {
        boolean [][] visited=new boolean[m][n];
        dfs(0,0,m,n,k,visited);
        return res;
    }
    public void dfs(int x,int y,int m,int n,int k,boolean[][] visited)
    {
        // 判断
        if(getSum(x)+getSum(y)<=k){
            visited[x][y]=true;
            res++;
        }else{
            return;
        }
        for(int i=0;i<4;i++){
            switch(i){
                case 0:
                    if(x-1>=0&&visited[x-1][y]==false){
                        dfs(x-1,y,m,n,k,visited);
                    }
                break;
                 case 1:
                    if(x+1<m&&visited[x+1][y]==false){
                        dfs(x+1,y,m,n,k,visited);
                    }
                break;
                case 2:
                    if(y-1>=0&&visited[x][y-1]==false){
                        dfs(x,y-1,m,n,k,visited);
                    }
                break;
                case 3:
                    if(y+1<n&&visited[x][y+1]==false){
                        dfs(x,y+1,m,n,k,visited);
                    }
                break;
            }
        }
    }
    public int getSum(int x){
        int r=0;
        while(x!=0){
            r+=(x%10);
            x/=10;
        }
        return r;
    }
}

 剑指 Offer 14-1 剪绳子

难度中等374

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

思路:

  1. 对绳子进行分段的关键在于在满足分割了一次的前提下面,不能出现长度为1的绳子,尽量为长度为3的绳子,可以为长度为2的绳子,其余为长度为4的绳子。
class Solution {
    public int cuttingRope(int n) {
        int res=1;
        boolean j=true;
        // 从3开始
        while(n>=2){
           if(n>=5){
               res*=3;
               n-=3;
               j=false;
           }else{
               if(j){//长度为2-4,表示最少要一次分割
                   j=false;
                   //必须进行划分
                   int tep=n/2;
                   res=(n-tep)*tep;
                   n=0;
               }else{//长度为4,表示为末尾不分割
                 res*=n;
                 n=0;
               }
           }
        }
        return res;
    }
}

 剑指 Offer 15 二进制中1的个数

难度简单222

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 '1' 的个数(也被称为 汉明重量).)。

思想:

  1. int为32位,遍历进行判断统计。
  2. 采用n&(n-1)可以将二进制最右边的1转换成0。

常见的&操作的快捷方法

  • n&(-n)表示求出二进制最边的1位起始的值的大小。
  • n&(n-1)可以将二进制最右边的1转换成0.
public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
        int res=0;
        for(int i=0;i<32;i++)
        {
            int tep=n>>i;
            if((tep&1)==1){
                res++;
            }
        }
        return res;
    }
}

剑指 Offer 16 数值的整数次方 

难度中等261

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。

提示:

  • -100.0 < x < 100.0
  • -231 <= n <= 231-1
  • -104 <= xn <= 104

思路:

  1. 很容易漏掉几种特殊情况,如果没有测试用例的提示,很难判断出来,需要看到x和n的特殊情况,x=1或者-1的时候,n为-23-1和0的时候,这都不要进行判断。
class Solution {
    public double myPow(double x, int n) {
      if(n==0||x==1)return 1;
      if(x==-1){
          if(n%2==0)return 1;
          return -1;
      }
      if(n==-2147483648){
          return 0;
      }
      double res=1;
      if(n>0){
          for(int i=1;i<=n;i++){
              res*=x;
              if(res==0.0)return 0;
          }
          return res;
      }else{
          for(int i=1;i<=-n;i++){
              res*=(1/x);
              if(res==0.0)return 0;
          }
          return res;
      }
    }
}

 剑指 Offer 17 打印从1到最大的n位数

难度简单194

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
class Solution {
    public int[] printNumbers(int n) {
        int max=0;
        for(int i=0;i<n;i++){
            max=(max*10)+9;
        }
        LinkedList<Integer> res=new LinkedList<>();
        for(int i=1;i<=max;i++){
            res.addLast(i);
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

 剑指 Offer 18 删除链表的节点

难度简单196

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        ListNode h1=new ListNode();h1.next=head;
        ListNode p1=h1,p2=head;
        while(p2!=null&&p2.val!=val){
            p1=p1.next;
            p2=p2.next;
        }
        p1.next=p2.next;
        return h1.next;
    }
}

 剑指Offer 20 表示数值的字符串

难度中等302

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。

数值(按顺序)可以分成以下几个部分:

  1. 若干空格
  2. 一个 小数 或者 整数
  3. (可选)一个 'e' 或 'E' ,后面跟着一个 整数
  4. 若干空格

小数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-'
  2. 下述格式之一:
    1. 至少一位数字,后面跟着一个点 '.'
    2. 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字
    3. 一个点 '.' ,后面跟着至少一位数字

整数(按顺序)可以分成以下几个部分:

  1. (可选)一个符号字符('+' 或 '-'
  2. 至少一位数字

部分数值列举如下:

  • ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"]

部分非数值列举如下:

  • ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"]

思想:

  1. 用Double.parseDouble()进行内置转换,对不能进行转换的进行捕获,在进行排除特殊情况。
class Solution {
    public boolean isNumber(String s) {
        s=s.trim();
        if(s.length()==0)return false;//去掉空格之后可能会空串
        try{
            Double.parseDouble(s);
        }catch(Exception e){
            return false;
        }
        // 排除八进制和十六进制的可能性,d、D,f、F等
        char flag=s.charAt(s.length()-1);
        if((flag>='0'&&flag<='9')||flag=='.'){
            return true;
        }
        return false;
    }
}

 剑指 Offer 21 调整数组顺序使奇数位于偶数前面

难度简单201

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

提示:

  1. 0 <= nums.length <= 50000
  2. 0 <= nums[i] <= 10000
class Solution {
    public int[] exchange(int[] nums) {
        LinkedList<Integer> res=new LinkedList<>();
        for(int i:nums){
            if(i%2==0){
                res.addLast(i);
            }else{
                res.addFirst(i);
            }
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

 剑指 Offer 22 链表中倒数第k个节点

难度简单330

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode h1=head;
        if(head==null)return null;
        for(int i=0;i<k;i++){
            h1=h1.next;
        }
        while(h1!=null){
            h1=h1.next;
            head=head.next;
        }
        return head;
    }
}

剑指 Offer 24 反转链表

难度简单385

定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null)return head;
        ListNode h1=new ListNode();
        h1.next=null;
        while(head!=null){
            ListNode tep=head;
            head=head.next;
            tep.next=h1.next;
            h1.next=tep;
        }
        return h1.next;
    }
}

 剑指 Offer 25 合并两个升序的链表

难度简单217

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

示例1:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode h1=new ListNode(),tail;
        h1.next=null;
        tail=h1;
        while(l1!=null&&l2!=null)
        {
            ListNode tep;
            if(l1.val>l2.val)
            {
                tep=l2;
                l2=l2.next;
                tep.next=null;
                tail.next=tep;
                tail=tep;
            }else{
                tep=l1;
                l1=l1.next;
                tep.next=null;
                tail.next=tep;
                tail=tep;
            }
        }
        if(l1!=null){
            tail.next=l1;
        }
         if(l2!=null){
            tail.next=l2;
        }
        return h1.next;
    }
}

 剑指 Offer 26 树的子结构

难度中等476

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

例如:
给定的树 A:

     3
    / \
   4   5
  / \
 1   2

给定的树 B:

   4 
  /
 1

返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。

思想:

可以根据先序遍历,然后对每个子结构进行匹配验证。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private boolean flag=false;
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(B==null)return false;
        dfs1(A,B);
        return flag;
    }
    void dfs1(TreeNode A,TreeNode B){
        if(A==null){
            return;
        }
        flag=dfs(A,B);
        if(flag)return;
        dfs1(A.left,B);
        if(flag)return;
        dfs1(A.right,B);
    }
    boolean dfs(TreeNode A,TreeNode B)
    {
        if(B==null){
            // 表示为真
            return true;
        }
        if(A==null&&B!=null){
            return false;
        }
        if(A.val!=B.val){
            return false;
        }
        boolean cleft=dfs(A.left,B.left);
        boolean cright=dfs(A.right,B.right);
        return cleft&&cright;
    }
}

 剑指 Offer 27 二叉树的镜像

难度简单218

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

镜像输出:

     4
   /   \
  7     2
 / \   / \

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        dfs(root);
        return root;
    }
    void dfs(TreeNode root){
        if(root==null){
            return;
        }
        TreeNode tep=root.left;
        root.left=root.right;
        root.right=tep;

        dfs(root.left);
        dfs(root.right);
    }
}

剑指 Offer 28 对称的二叉树

难度简单288

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return dfs(root,root);
    }
    boolean dfs(TreeNode r1,TreeNode r2){
        if(r1==null&&r2==null){
            return true;
        }
        if(r1==null||r2==null){
            return false;
        }
        if(r1.val!=r2.val){
            return false;
        }
        return dfs(r1.right,r2.left)&&dfs(r1.left,r2.right);
    }
}

 剑指 Offer 29 顺时针打印矩阵

难度简单368

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

思路:

  1. 采用DFS进行遍历打印,注意边界值
class Solution {
    private ArrayList<Integer> res=new ArrayList<>();
    public int[] spiralOrder(int[][] matrix) {
        if (matrix.length==0){
            return res.stream().mapToInt(Integer::valueOf).toArray();
        }
        boolean [][]visited=new boolean[matrix.length][matrix[0].length];
        visited[0][0]=true;
        res.add(matrix[0][0]);
        dfs(0,0,matrix.length,matrix[0].length,0,matrix,visited);
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
    void dfs(int x,int y,int m,int n,int dir,int[][] matrix,boolean[][] visited){
        switch(dir){
            case 0:
            //右走
            int i=y;
            for(i++;i<n;i++){
                if(visited[x][i]){
                    break;
                }else{
                    visited[x][i]=true;
                    res.add(matrix[x][i]);
                }
            }
           if(i!=y+1)
            dfs(x,i-1,m,n,1,matrix,visited);
            if(y+1==1){// 表示为单列
                 dfs(x,i-1,m,n,1,matrix,visited);
            }
            break;
            case 1:
            //下走
             i=x;
            for(i++;i<m;i++){
                if(visited[i][y]){
                    break;
                }else{
                    visited[i][y]=true;
                    res.add(matrix[i][y]);
                }
            } 
            if(i!=x+1)
            dfs(i-1,y,m,n,2,matrix,visited);
            break;
            case 2:
             i=y;
            for(i--;i>=0;i--){
                 if(visited[x][i]){
                    break;
                }else{
                    visited[x][i]=true;
                    res.add(matrix[x][i]);
                }
            }

            if(i!=y-1)
            dfs(x,i+1,m,n,3,matrix,visited);
            break;

            case 3:
             i=x;
            for(i--;i>=0;i--){
                if(visited[i][y]){
                    break;
                }else{
                    visited[i][y]=true;
                    res.add(matrix[i][y]);
                }
            }
            if(i!=x-1)
            dfs(i+1,y,m,n,0,matrix,visited);
            break;
        }
    }
}

 剑指 Offer 30 包含min函数的栈

难度简单293

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.
class MinStack {

    /** initialize your data structure here. */
    Stack<Integer> s=new Stack<>();
    TreeMap<Integer,Integer> t=new TreeMap<>();

    public MinStack() {
        
    }
    
    public void push(int x) {
        s.push(x);
        Integer v=t.get(x);
        if(v==null){
            t.put(x,1);
        }else{
            t.put(x,v+1);
        }
    }
    
    public void pop() {
        int x=s.peek();
        s.pop();
        Integer v=t.get(x);
        if(v==1){
            t.remove(x);
        }else{
            t.put(x,v-1);
        }
    }
    
    public int top() {
        return s.peek();
    }
    
    public int min() {
        return t.firstKey();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

剑指 Offer 31 栈的压入、弹出序列

难度中等288

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> s=new Stack<>();
        int index=0;
        for(int i=0;i<pushed.length;i++){
            if(pushed[i]==popped[index])
            {
                index++;
                //是否可以继续弹栈
                while(!s.isEmpty()&&s.peek()==popped[index])
                {
                    index++;
                    s.pop();
                }
            }else{
                s.push(pushed[i]);
            }
        }
        while(!s.isEmpty()&&s.peek()==popped[index])
        {
            index++;
            s.pop();
        }
        if(index==pushed.length){
            return true;
        }
        return false;
    }
}

剑指 Offer 32 从上到下打印二叉树

难度简单184

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private List<List<Integer>> res=new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        LinkedList<TreeNode> q=new LinkedList<>();
        if(root==null)return res;
        LinkedList<Integer> c=new LinkedList<>();
        int clen=1;
        int cindex=0;
        int nextlen=0;
        q.addLast(root);
        while(!q.isEmpty()){
            TreeNode cval=q.getFirst();
            c.add(cval.val);
            cindex++;
            q.pop();

            if(cval.left!=null){
                nextlen++;
                q.addLast(cval.left);
            }
            if(cval.right!=null){
                nextlen++;
                q.addLast(cval.right);
            }

            if(cindex==clen){
                cindex=0;
                clen=nextlen;
                nextlen=0;
                res.add(new ArrayList<>(c));
                c.clear();
            }
        }
        return res;
    }
}

剑指 Offer 33 二叉搜索树的后序遍历序列

难度中等445

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:

     5
    / \
   2   6
  / \
 1   3

示例 1:

输入: [1,6,3,2,5]
输出: false

思想:

  1. 二叉搜索树的性质:左<根<右,后序遍历的性质:左,右,根,最右边的是根,根据根可以判断出左子树和右子树,对左子树和右子树继续递归进行判断,直至判断所有的子树都满足要求。
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return judge(postorder,0,postorder.length-1);
    }
    boolean judge(int []postorder,int left,int right){
        if(left>=right)return true;
        // 找到以postorder[right]为根右子树最边上的index
        int tep=left;
        while(left<=right&&postorder[left]<postorder[right]){
            left++;
        }
        left--;
        // 对右子树进行判断
        for(int i=left+1;i<right;i++){
            if(postorder[i]<postorder[right]){
                return false;
            }
        }
        return judge(postorder,tep,left)&&judge(postorder,left+1,right-1);
    }
}

剑指 Offer 34 二叉树中和为某一值的路径

难度中等297

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点 是指没有子节点的节点。

思路:

  1. 用DFS的思想去统计从根节点到叶子节点的路径之和,可以用ArrayList存储某一条路径,用add和remove(new Integer())会出现结点值相同,而删除错误位置的结点,所以用LinkedList去处理边界山的值,addLast(),removeLast()。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private List<List<Integer>> res=new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        // 根节点表示左右根节点都为null
        dfs(root,0,target,new LinkedList<>());
        return res;
    }
    void dfs(TreeNode root,int csum,int target,LinkedList<Integer> cval){
        if(root==null)return;

        cval.addLast(root.val);
        csum+=root.val;

        if(root.left==null&&root.right==null){
            if(csum==target)
            res.add(new ArrayList<>(cval));
            cval.removeLast();
            return;
        }

        dfs(root.left,csum,target,cval);
        dfs(root.right,csum,target,cval);

        cval.removeLast();
    }
}

剑指 Offer 35 复杂链表的复制

难度中等444

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    public Node copyRandomList(Node head) {
        Node h1=new Node(-1);
        Node tail=h1;
        Node h2=head;
        HashMap<Node,Node> LToL=new HashMap<>();
        while(h2!=null){
            Node tep=new Node(h2.val);
            tep.next=null;
            tep.random=null;
            tail.next=tep;
            tail=tep;
            LToL.put(h2,tep);
            h2=h2.next;
        }
        h2=h1.next;
        while(h2!=null&head!=null){
            h2.random=LToL.get(head.random);
            head=head.next;
            h2=h2.next;
        }
        return h1.next;      
    }
}

剑指 Offer 36 二叉搜索树与双链表

难度中等416

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

为了让您更好地理解问题,以下面的二叉搜索树为例:

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。

特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。

class Solution {
    LinkedList<Node> q=new LinkedList<>();
    public Node treeToDoublyList(Node root) {
       if(root==null)return root;
       dfs(root);
       Node h1=q.getFirst();
       q.removeFirst();
       root=h1;
        h1.right=h1;
        h1.left=h1;
      while(!q.isEmpty()){
        Node tt=q.getFirst();
        q.removeFirst();
        h1.right=tt;
        tt.left=h1;
        h1=tt;
      }
      h1.right=root;
      root.left=h1;
      return root; 
    }
    void dfs(Node root){
        if(root==null){
            return;
        }
        dfs(root.left);
        q.addLast(root);
        dfs(root.right);
    }
}

剑指 Offer 37 序列化二叉树

难度困难269

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

你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。

提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Codec {

    private TreeNode res1;
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
       res1=root;
       return null;
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
       return res1;
    }
}

// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));

剑指 Offer 38 字符串的排列

难度中等506

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

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

限制:

1 <= s 的长度 <= 8

通过次数186,068提交次数320,465

思路:

  1. dfs进行全排列,但是去重是一个老大难的问题,设置一个HashSet[]进行去重复判断,但是时间超出了限制,于是想到了HashSet也存在toArray()方法,可以直接进行转化为String[],因为对返回的全排列的顺序没有要求,因此可以将全排列直接全部插入set中,会自动去重复。
class Solution {
    HashSet<String> ss=new HashSet<>();
    public String[] permutation(String s) {
        dfs(0,"",s,new boolean[s.length()]);
        return ss.toArray(new String[0]);
    }
    void dfs(int index,String cv,String s,boolean []visited){
        if(index==s.length()){
            ss.add(cv);
            return ;
        }
        for(int i=0;i<s.length();i++){
            if(!visited[i]){
                visited[i]=true;
                dfs(index+1,cv+s.charAt(i),s,visited);
                visited[i]=false;
            }
        }
    }
}

剑指 Offer 39 数组中出现次数超过一半的数字

难度简单251

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2
class Solution {
    public int majorityElement(int[] nums) {
        int res=nums[0];
        int n=1;
        for(int i=1;i<nums.length;i++){
            if(n>=1){
                if(nums[i]==res){
                    n++;
                }else{
                    n--;
                }
            }else{
                res=nums[i];
                n=1;
            }
        }
        return res;
    }
}

 剑指 Offer 40 最小的k个数

难度简单387

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        Arrays.sort(arr);
        int []res=new int[k];
        res=Arrays.copyOf(arr,k);
        return res;
    }
}

 剑指 Offer 41 数据流中的中位数

难度困难263

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。

思想:

 comparator比较器的写法:

1,new Comparator<T>(){

int comare(T o1,T o2){

return 1;

}

}

2,o1,o2->o1>o2?-1:1

3,(o1,o2)->{return o1>o2?-1:1;}

还有另一种解法时间复杂度较小,可以采用大小根堆区避免每次都要进行排序。

class MedianFinder {

    /** initialize your data structure here. */
    private ArrayList<Double> res;
    public MedianFinder() {
        res=new ArrayList<>();
    }
    
    public void addNum(int num) {
        res.add((double)num);
        // res.sort(new Comparator<Double>(){
        //     @Override
        //     public int compare(Double o1, Double o2) {
        //         if(o1>o2){
        //             return -1;
        //         }else 
        //         return 1;
        //     }
        // });
        // res.sort((x,y)-> {return x-y>0? -1:1;});
        res.sort((x,y)-> x-y>0? -1:1);
    }
    
    public double findMedian() {
        if(res.size()%2==0){
            return (res.get(res.size()/2)+res.get(res.size()/2-1))/2;
        }else{
            return res.get(res.size()/2);
        }
    }
}

// 大小根堆的方式求解
// 建设数组排序后,后半部分较大的放在小根堆,前半部分放在大根堆,这就满足小根堆的顶部和大根堆的顶部都是靠中间的。
class MedianFinder {
    Queue<Integer> A, B;
    public MedianFinder() {
        A = new PriorityQueue<>(); // 小顶堆,保存较大的一半
        B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半
    }
    public void addNum(int num) {
        if(A.size() != B.size()) {
            A.add(num);
            B.add(A.poll());
        } else {
            B.add(num);
            A.add(B.poll());
        }
    }
    public double findMedian() {
        return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
    }
}

剑指 Offer 42 连续子数组的最大和

难度简单474

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

class Solution {
    public int maxSubArray(int[] nums) {
        int res=Integer.MIN_VALUE;
        int cv=0;

        for(int i:nums){
            // 进行比较 另开一个 还是连续的干
            if(cv+i<0||cv+i<i){
                cv=i;
                res=Integer.max(cv,res);
            }else{
                cv+=i;
                res=Integer.max(cv,res);
            }
        }
        return res;
    }
}

剑指 Offer 43 1-n整数中1出现的次数

难度困难288

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例 1:

输入:n = 12
输出:5

示例 2:

输入:n = 13
输出:6

思路:

进行逐位进行统计,对于一个数字n,先统计最低位置,个位,然后在看十位和以上的高位,个位为1,高位则可以随便变换,个位可能为0-9,当为0的时候,

class Solution {
    public int countDigitOne(int n) {
        int res=0;
        String s = String.valueOf(n);
        int len=s.length();
        for(int i=len-1;i>=0;i--)//按照十进制展开 
        {
            int digit= (int) Math.pow(10,len-1-i);
           // 从右向左 i表示位置,digit也表示位置,digit=1表示个位,digit=10表示十位
            // 
            if(s.charAt(i)=='0')
            {
                int temp=0;
                for(int j=0;j<i;j++)
                {
                    temp*=10;
                    temp+=s.charAt(j)-'0';
                }
                // 因为i位置不为1,所以00-temp-1总共有temp,再有位数为digit,所以
                res+=(digit*temp);
            }
            else if(s.charAt(i)=='1')
            {
                // 高位
                int temp=0;
                for(int j=0;j<i;j++)
                {
                    temp*=10;
                    temp+=s.charAt(j)-'0';
                }
                res+=(digit*temp);
                // 低位
                temp=0;
                for(int j=i+1;j<len;j++)
                {
                    temp*=10;
                    temp+=s.charAt(j)-'0';
                }
                temp++;
                res+=temp;
            }
            else{// 当前位为2-9
                int temp=0;
                for(int j=0;j<i;j++)
                {
                    temp*=10;
                    temp+=s.charAt(j)-'0';
                }
                temp++;
                res+=(digit*temp);
            }
        }
        return res;
    }
    }

剑指 Offer 44 数字序列中某一位的数字

难度中等207

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

示例 1:

输入:n = 3
输出:3

思路:

  1. 对1-n进行逐位添加,存在规律,首先是1-9,其次是10-99,在100-99,,,可以按照这个规律将n位置找出来。
class Solution {
    public int findNthDigit(int n) {
        int digit = 1;
        long start = 1;
        long count = 9;
        while (n > count) { // 1.
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        }
        long num = start + (n - 1) / digit; // 2.
        return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.
    }
}

剑指 Offer 45 把数组排成最小的数

难度中等390

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

思路:

  1. 对于一个数组进行全排列得到最小的数,可考虑用Arrays.sort()中的比较器comparator(),将两个数转换成字符串进行拼接,a+b和b+a的形式,然后进行判断即可得出目标要求。
class Solution {
    public String minNumber(int[] nums) {
         String res="";
        Integer [] temp=new Integer[nums.length];
        for(int i=0;i<nums.length;i++)
            temp[i]=nums[i];
        Arrays.sort(temp,(one,two)->{
            return (one.toString()+two.toString()).compareTo(two.toString()+one.toString());
        });
        for(Integer i:temp)
            res=res+i.toString();
        return res;
    }
}

剑指 Offer 46 把数字翻译成字符串

难度中等382

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

示例 1:

输入: 12258
输出: 5
解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"

思想:

  1. 采用DFS思想进行遍历,对满足进行set去重即可。
class Solution {
    private HashSet<String> res;
    public int translateNum(int num) {
        res=new HashSet<>();
        String s=String.valueOf(num);
        dfs(0,"",s);
        return res.size();
    }
    void dfs(int index,String cv,String s){
        if(index==s.length()){
            res.add(cv);
            return ;
        }
        int num=s.charAt(index)-'0';
        dfs(index+1,cv+String.valueOf('a'+num),s);
        // 如果index index+1合法的话
        if(index+1==s.length())return;
        if(num==0||num>2)return;
        if(num==1){
            num=10+s.charAt(index+1)-'0';
            dfs(index+2,cv+String.valueOf('a'+num),s);
        }
        if(num==2){
            num=20+s.charAt(index+1)-'0';
            if(num>=26)return;
            dfs(index+2,cv+String.valueOf('a'+num),s);
        }
    }
}

剑指 Offer 47 礼物的最大价值

难度中等255

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

示例 1:

输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物
class Solution {
    public int maxValue(int[][] grid) {
        // 很明显的dp题目,dp[i][j]表示
        if(grid.length==0)return 0;
        int [][]dp=new int[grid.length][grid[0].length];
        int csum=0;
        for(int i=0;i<grid.length;i++){
            csum+=grid[i][0];
            dp[i][0]=csum;
        }
        csum=0;
        for(int i=0;i<grid[0].length;i++){
            csum+=grid[0][i];
            dp[0][i]=csum;
        }
        for(int i=1;i<grid.length;i++){
            for(int j=1;j<grid[0].length;j++){
                dp[i][j]=Integer.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
            }
        }
        return dp[grid.length-1][grid[0].length-1];
    }
}

剑指 Offer 48 最长不含重复字符的子串字符串

难度中等386

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res=0;
        String tep="";
        for(int i=0;i<s.length();i++){
            String t=""+s.charAt(i);
            int index=tep.indexOf(t);
            if(index==-1){
                tep=tep+t;
            }else{
                tep=tep.substring(index+1,tep.length());
                tep=tep+t;
            }
            res=Integer.max(res,tep.length());
        }
        return res;
    }
}

剑指 Offer 49 丑数

难度中等292

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

示例:

输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

方法一,超时

class Solution {
    public int nthUglyNumber(int n) {
        int index=0;
        int count=1;
        while(true){
            if(isUgly(count)){
                index++;
                if(index==n){
                    return count;
                }
            }
            count++;
        }
        
    }
    boolean isUgly(int n){
        while(n%5==0)n/=5;
        while(n%3==0)n/=3;
        while(n%2==0)n/=2;
        return n==1?true:false;
    }
}

方法二:

思想:

丑数为2,3,5的倍数,故此,可以对丑数x进行2x、3x、5x进行相乘且也为丑数,去重并排序后,在慢慢进行下去,直至到达n即可。

class Solution {
    public int nthUglyNumber(int n) {
        int[] factors = {2, 3, 5};
        Set<Long> seen = new HashSet<Long>();
        PriorityQueue<Long> heap = new PriorityQueue<Long>();
        seen.add(1L);
        heap.offer(1L);
        int ugly = 0;
        for (int i = 0; i < n; i++) {
            long curr = heap.poll();
            ugly = (int) curr;
            for (int factor : factors) {
                long next = curr * factor;
                if (seen.add(next)) {
                    heap.offer(next);
                }
            }
        }
        return ugly;
    }
}

面试题 50 第一只出现一次的字符

难度简单190

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

示例 1:

输入:s = "abaccdeff"
输出:'b'

思路:

LinkedHashMap,保持顺序的map,且map的遍历,Map.Entry<T,T> entry:map.entrySet();,然后通过entry.getKey()和entry.getValue();

class Solution {
    public char firstUniqChar(String s) {
        if(s.length()==0)
        return ' ';
        LinkedHashMap<Character,Integer> map=new LinkedHashMap<>();
        for(int j=0;j<s.length();j++){
            char i=s.charAt(j);
            Integer f= map.get(i);
            if(f==null){
                map.put(i,1);
            }else{
                map.put(i,f+1);
            }
        }
        for(Map.Entry<Character,Integer> entry:map.entrySet()){
            if(entry.getValue()==1){
                return entry.getKey();
            }
        }
       return ' ';
    }
}

剑指 Offer 51 数组中的逆序对

难度困难638

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5
class Solution {
    public int reversePairs(int[] nums) {
        // 离散化
        int temp[]=Arrays.copyOf(nums,nums.length);
        Arrays.sort(temp);
        for(int i=0;i<nums.length;i++){
            nums[i]=Arrays.binarySearch(temp,nums[i])+1;
        }
        BIT t=new BIT(nums.length);
        int res=0;
        for(int i=0;i<nums.length;i++){
            t.add(nums[i]);
            res=res+i-t.query(nums[i])+1;
            // i表示当前加入bit中的数的总数,query(index)表示小于等于index数的总数
        }
        return res;
    }
}
class BIT{
    int tree[];
    int n;
    public BIT(int n){
        tree=new int[n+1];
        this.n=n;
    }
    int lowbit(int x){
        return x&(-x);
    }
    void add(int index){
        while(index<=n){
            tree[index]++;
            index+=lowbit(index);
        }
    }
    int query(int index){
        int res=0;
        while(index!=0){
            res+=tree[index];
            index-=lowbit(index);
        }
        return res;
    }
}

剑指 Offer 52 两个链表的第一个公共节点

难度简单451

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

如下面的两个链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int len1=0;
        int len2=0;
        ListNode h1=headA,h2=headB;
        while(h1!=null){len1++;h1=h1.next;}
        while(h2!=null){len2++;h2=h2.next;}
        int t=len1-len2;
        if(t>0){
            while(t!=0){t--;headA=headA.next;}
        }else{
            t=len2-len1;
            while(t!=0){t--;headB=headB.next;}
        }
        while(headA!=headB){
            headA=headA.next;
            headB=headB.next;
        }
        return headA;
    }
}

剑指 Offer 53 在排序数组中查找数字

难度简单278

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

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
class Solution {
    public int search(int[] nums, int target) {
        int res=0;
        for(int i:nums){
            if(res!=0&&target!=i)return res;
            if(i==target)res++;
        }
        return res;
    }
}

 剑指 Offer 53 0-n-1中缺失的数字

难度简单240

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例 1:

输入: [0,1,3]
输出: 2
class Solution {
    public int missingNumber(int[] nums) {
        int sum=0;
       for(int i:nums)sum+=i;
       return (nums.length+0)*(nums.length+1)/2-sum;
    }
}

剑指 Offer 54 二叉搜索数的第k大节点

难度简单261

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

示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 4
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private int res=0;
    int count=0;
    public int kthLargest(TreeNode root, int k) {
        dfs(root,k);
        return res;
    }
    void dfs(TreeNode root,int k){
        if(root==null)return;
        
        dfs(root.right,k);
        count++;
        if(count==k){
            res=root.val;
            return;
        }
        dfs(root.left,k);
        
    }
}

剑指 Offer 55 二叉树的深度

难度简单167

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

例如:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int res=0;
    public int maxDepth(TreeNode root) {
        dfs(root,0);
        return res;
    }
    void dfs(TreeNode root,int d){
        if(root==null)return;

        res=Integer.max(res,d+1);

        dfs(root.left,d+1);

        dfs(root.right,d+1);
    }
}

剑指Offer 55 平衡二叉树

难度简单237

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private boolean flag=true;
    public boolean isBalanced(TreeNode root) {
        deepth(root);
        return flag;
    }
    int deepth(TreeNode root){
        if(root==null)return 0;
        int cl=deepth(root.left);
        int cr=deepth(root.right);
        // 进行左右对比
        if(Math.abs(cl-cr)>=2){
            flag=false;
        }
        return Integer.max(cl,cr)+1;
    }
}

剑指 Offer 56 数组中数字出现的次数-1

难度中等557

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

思路:

  1. 两个数字出现一次,其余出现两次,采用分组异或即可满足条件,那怎样进行分组呢???先可以让nms[]数组全部进行异或,得到的是目标a、b的异或,然后得到a^b的异或二进制位置为1的位置进行分组,然后遍历nums[]进行分组异或即可得到a和b。
class Solution {
    public int[] singleNumbers(int[] nums) {
        int ret = 0;
        for (int n : nums) {
            ret ^= n;
        }
        int div = 1;
        while ((div & ret) == 0) {
            div <<= 1;
        }
        int a = 0, b = 0;
        for (int n : nums) {
            if ((div & n) != 0) {
                a ^= n;
            } else {
                b ^= n;
            }
        }
        return new int[]{a, b};
    }
}

剑指 Offer 56 数组中出现的次数-2

难度中等292

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:

输入:nums = [3,4,3,3]
class Solution {
    public int singleNumber(int[] nums) {
        HashMap<Integer,Integer> r=new HashMap<>();
        for(int i:nums){
            Integer t=r.get(i);
            if(t==null){
                r.put(i,1);
            }else{
                r.put(i,t+1);
            }
        }
        for(Map.Entry<Integer,Integer> entry:r.entrySet()){
            if(entry.getValue()==1){
                return entry.getKey();
            }
        }
        return 0;
    }
}

剑指 Offer 57 和为s的两个数字

难度简单168

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
class Solution {
    public int[] twoSum(int[] nums, int target) {

       ArrayList<Integer> res=new ArrayList<>();
        for(int i=0;i<nums.length;i++){
            int t=target-nums[i];
            int flag=Arrays.binarySearch(nums,i+1,nums.length,t);
            if(flag>=0){
                res.add(t);
                res.add(target-t);
                break;
            }
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

剑指 Offer 57 和为s的连续正数序列

难度简单389

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9
输出:[[2,3,4],[4,5]]

思想:

  1. 滑动窗口
class Solution {
    public int[][] findContinuousSequence(int target) {
        ArrayList<int []> res=new ArrayList<>();

        int l=1;
        int r=2;
        while(l<=target/2){
            if(sum(l,r)==target){
                int []t=new int[r-l+1];
                int v=l;
                for(int i=0;i<r-l+1;i++){
                    t[i]=v;
                    v++;
                }
                res.add(t);
                l++;
                r++;
            }else if(sum(l,r)>target){
                l++;
            }else{
                r++;
            }
        }
        return res.toArray(new int[0][]);
    }
    int sum(int l,int r){
        return (l+r)*(r-l+1)/2;
    }
}

剑指 Offer 58 翻转单词顺序

难度简单178

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. ",则输出"student. a am I"。

示例 1:

输入: "the sky is blue"
输出: "blue is sky the"
class Solution {
    public String reverseWords(String s) {
       String res="";
       String t="";
       for(int i=0;i<s.length();i++){
           if(s.charAt(i)==' '){
               if(t.length()!=0){
                   if(res.length()==0){
                       res=t+"";
                   }else{
                       res=t+" "+res;
                   }
                   t="";
               }
           }else{
               t=t+String.valueOf(s.charAt(i));
               if(i==s.length()-1){
                    if(res.length()==0){
                       res=t+"";
                   }else{
                       res=t+" "+res;
                   }
               }
           }
       }
        
        return res;
    }
}

剑指 Offer 58 左旋转字符串

难度简单203

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n,s.length())+s.substring(0,n);
    }
}

 剑指 Offer 59 滑动窗口的最大值

难度困难399

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {

        ArrayList<Integer> res=new ArrayList<>();
        if(nums.length==0)return res.stream().mapToInt(Integer::valueOf).toArray();
        PriorityQueue<Integer> q=new PriorityQueue<>(new Comparator<Integer>(){
            public int compare(Integer a1,Integer a2){
                if(a1>a2)return -1;
                else return 1;
            }
        });
        int l=0;
        int r=k-1;
        for(int i=0;i<k-1;i++){
            q.add(nums[i]);
        }
        while(r<nums.length){
            q.add(nums[r]);
            res.add(q.peek());
            
            // res.add(sum[r]-sum[l-1]);
            q.remove(new Integer(nums[l]));
            r++;
            l++;
            
        }
        return res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

剑指 Offer 59 队列中的最大值

难度中等327

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_valuepush_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入: 
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
class MaxQueue {
    PriorityQueue<Integer> q=new PriorityQueue<>(new Comparator<Integer>(){
        public int compare(Integer a1,Integer a2){
            if(a1>a2)return -1;
            else return 1;
        }
    });
    LinkedList<Integer> l=new LinkedList<>();

    public MaxQueue() {

    }
    
    public int max_value() {
        if(l.size()==0)return -1;
        return q.peek();
    }
    
    public void push_back(int value) {
        l.addLast(value);
        q.add(value);
    }
    
    public int pop_front() {
        if(l.size()==0)return -1;
        int res=l.getFirst();
        l.removeFirst();
        q.remove(new Integer(res));
        return res;
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

剑指 Offer 60 n个骰子的点数

难度中等374

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。

你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。

思路:

  1. 根据骰子的个数,由少到多并进行统计,根据上一层的点数去求得下一层的点数。
class Solution {
    public double[] dicesProbability(int n) {
        double[] dp = new double[6];
        Arrays.fill(dp, 1.0 / 6.0);
        for (int i = 2; i <= n; i++) {// 骰子数
            double[] tmp = new double[5 * i + 1];// 下一层的骰子
            for (int j = 0; j < dp.length; j++) {// 当前层的骰子数
                for (int k = 0; k < 6; k++) {//从当前层跳到下一层有六种情况
                    tmp[j + k] += dp[j] / 6.0;// 每种情况进行累加
                }
            }
            dp = tmp;
        }
        return dp;
    }
}

剑指 Offer 61 扑克牌中的顺子

难度简单211

若干副扑克牌中随机抽 5 张牌,判断是不是一个顺子,即这5张牌是不是连续的。2~10为数字本身,A为1,J为11,Q为12,K为13,而大、小王为 0 ,可以看成任意数字。A 不能视为 14。

class Solution {
    public boolean isStraight(int[] nums) {
        Arrays.sort(nums);
        int count_0=0;
        int l=-1;
        for(int i:nums){
            if(i==0){
                count_0++;
            }else{
                if(l==-1){
                    l=i;
                }else{
                    if(i-l==1){
                        l=i;
                    }else if(i-l==0){
                        return false;
                    }else{
                        int tep=i-l-1;
                        if(tep>count_0){
                            return false;
                        }else{
                            l=i;
                            count_0-=tep;
                        }
                    }
                }
            }
        }
        return true;
    }
}

剑指 Offer 62 圆圈中最后剩下的数字

难度简单554

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

思路:

约瑟夫环问题,可以采用list去模拟删除操作,index=(index+m-1)%n,n为变化的。

class Solution {
    public int lastRemaining(int n, int m) {
        ArrayList<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int idx = 0;
        while (n > 1) {
            idx = (idx + m - 1) % n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

剑指 Offer 63 股票的最大利润

难度中等223

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格

思路:

  1. 要找到最低点在前,最高点在后的两点,采用单调栈的方式记录栈低就能满足要求。
class Solution {
    public int maxProfit(int[] prices) {
       
        int res=0;
        int low=0;
        Stack<Integer> s=new Stack<>();
        for(int i:prices){
            if(s.isEmpty()){
                s.push(i);
                low=i;
            }else if(s.peek()<i){
                s.push(i);
                res=Integer.max(res,i-low);
            }else{
                while(!s.isEmpty()&&s.peek()>i){
                    s.pop();
                }
                // if(s.isEmpty())low=0;
                s.push(i);
                low=Integer.min(low,i);
                if(s.size()>=2){
                    res=Integer.max(res,i-low);
                }


            }

        }
        return res;
    }
}

剑指 Offer 64 求1+2+...+n

难度中等448

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

class Solution {
    public int sumNums(int n) {
        if(n==1)return 1;
        return n+sumNums(n-1);
    }
}

剑指 Offer 65 不用加减乘除做加法

难度简单269

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

class Solution {
    public int add(int a, int b) {
        int res=0;
        int jin=0;
        for(int i=0;i<32;i++){
            
            int a1=(a>>i)&1;
            int b1=(b>>i)&1;
            if(jin==1){
                if(a1==1&&b1==1){
                    jin=1;
                    res+=(1<<i);
                }else if(a1==1||b1==1){
                    jin=1;

                }else{
                    jin=0;
                    res+=(1<<i);
                }
            }else{
                if(a1==1&&b1==1){
                    jin=1;
                }else if(a1==1||b1==1){
                    jin=0;
                    res+=(1<<i);
                }else{
            }
        }
    }
    return res;
    }
}

剑指 Offer 66 构建乘积数组

难度中等205

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:

输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
class Solution {
    public int[] constructArr(int[] a) {
        if(a.length<=1)return a;
        int []t1=new int[a.length];
        int []t2=new int[a.length];
        Arrays.fill(t1,1);
        Arrays.fill(t2,1);
        int temp=1;
        for(int i=0;i<a.length;i++){
            temp*=a[i];
            t1[i]=temp;
        }
        temp=1;
        for(int i=a.length-1;i>=0;i--){
            temp*=a[i];
            t2[i]=temp;
        }
        int []res=new int[a.length];
        for(int i=0;i<res.length;i++){
            if(i==0){
                res[i]=t2[1];
            }else if(i==res.length-1){
                res[i]=t1[res.length-2];
            }else{
                res[i]=t1[i-1]*t2[i+1];
            }
        }
        return res;
    }
}

剑指 Offer 67 把字符串转换成整数

难度中等146

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。

首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。

该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。

注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。

在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:

假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231,  231 − 1]。如果数值超过这个范围,请返回  INT_MAX (231 − 1) 或 INT_MIN (−231) 。

class Solution {
         public int strToInt(String str) {
             str=str.trim();
             if(str.length()==0)return 0;
             if(str.charAt(0)!='+'&&str.charAt(0)!='-'&&str.charAt(0)<'0'&&str.charAt(0)>'9')return 0;
             Long res=0L;
             boolean flag=true;
             for(int i=0;i<str.length();i++){
                 if(i==0&&(str.charAt(i)=='-'))
                 {
                     flag=false;
                     continue;
                 }
                 if(i==0&&(str.charAt(i)=='+'))
                 {
                     continue;
                 }
                 if(str.charAt(i)<'0'||str.charAt(i)>'9'){
                     break;
                 }else{
                     res=res*10+(str.charAt(i)-'0');
                 }
                 if(!flag){
                     if(-res<=Integer.MIN_VALUE){
                         return Integer.MIN_VALUE;
                     }
                 }
                 else
                 if(res>=Integer.MAX_VALUE){
                     return Integer.MAX_VALUE;
                 }
             }
             if(!flag){
                 if(-res<=Integer.MIN_VALUE){
                     return Integer.MIN_VALUE;
                 }else{
                     return -res.intValue();
                 }
             }
             if(res>=Integer.MAX_VALUE){
                 return Integer.MAX_VALUE;
             }else{
                 return res.intValue();
             }
             // return Integer.parseInt(String.valueOf(res));
         }
     }

剑指 Offer 68 二叉树的最近公共祖先

难度简单388

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树:

思路:

  1. 可以用dfs去寻找单个目标值,找到返回true,当左右子树都找到目标值的时候,此时的根就为目标节点。
class Solution {
public:
    TreeNode* ans;
    bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr) return false;
        bool lson = dfs(root->left, p, q);
        bool rson = dfs(root->right, p, q);
        if ((lson && rson) || ((root->val == p->val || root->val == q->val) && (lson || rson)))// 左右子树存在目标值
        {
            ans = root;
        }
        return lson || rson || (root->val == p->val || root->val == q->val);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        dfs(root, p, q);
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿联爱学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值