剑指offer简单题汇总

1 剑指 Offer 03. 数组中重复的数字
找出数组中重复的数字。限制:2 <= n <= 100000
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

题解:

import java.util.*;
class Solution {
    public int findRepeatNumber(int[] nums) {
        //1.暴力解法,勉强通过
       /* for(int i=0;i<nums.length;i++)
        {
            for(int j=i+1;j<nums.length;j++)
            {
                if(nums[i]==nums[j])return nums[i];
            }
        }
        return nums[0];
    }*/
    //2.先排序在一次遍历,稍微比暴力解法好一点
    /*Arrays.sort(nums);//sort方法的源码是快排,快排的时间复杂度O(nlogn)
    for(int i=1;i<nums.length;i++)
    {
        if(nums[i-1]==nums[i])return nums[i];
    }
    return -1;*/
    //3.集合 勉强能通过
    //HashSet<Integer> set = new HashSet<Integer>();
    //LinkedList<Integer> set=new LinkedList<Integer>();//出现超出时间
        /*for(int num:nums){
           if(set.contains(num)) return num;
             set.add(num);
         }
         return -1;*/
    //
     //**4.****在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内,不会越界,把数组中的数当作索引,遍历数组。**
     //这个方法较快,没有修改原数据
     /*int[] arr=new int[nums.length]; //java 默认每个数组元素的值为0
     for(int num:nums)
     {
         arr[num]++;
         //因为出现的元素值 < nums.size(); 所以我们可以将见到的元素 放到索引的位置
         //每次遍历就加1后赋值给该索引的位置数组元素的值,如果大于1,就表示有重复元素
         if(arr[num]>1)return num;
         
     }
     return -1;  */
       //5.利用索引与数字的关系,修改了原数据
       /* for(int i = 0 ; i < nums.length;i++){
            //如果该数字不和他的索引相等
            while(nums[i]!=i){
                //重复返回
                if(nums[i]==nums[nums[i]]){
                    return nums[i];
                }
                //不重复交换
                int temp = nums[nums[i]];
                nums[nums[i]] = nums[i];
                nums[i] = temp;
            }
        }
        return -1; */
        
        
        }
    


}

2 剑指 Offer 05. 替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:

输入:s = "We are happy."
输出:"We%20are%20happy."

题解:

class Solution {
    public String replaceSpace(String s) {
        //1.使用StringBuffer或StringBuilder容器
       /* char[] arr=s.toCharArray();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<arr.length;i++)
        {
            if(arr[i]==' ')
            sb=sb.append("%20");
            else
            sb=sb.append(arr[i]);
        }
        return sb.toString();*/
        //2.直接调用类String下的public String replace(char oldChar,char newChar)返回一个新的字符串,
        //它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 
       /* String s1=  s.replace(" ","%20");
        return s1;*/
        
    }
}

3剑指 Offer 06. 从尾到头打印链表
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:

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

题解:

import java.util.*;
class Solution {
    public int[] reversePrint(ListNode head) {
      /* ListNode node=head;
        int count=0;
        while(node!=null)
        {
            count++;
            node=node.next;
        }
        int[] arr=new int[count];
        for(int i=count-1;i>=0;i--)
        {
          arr[i]=head.val;
         head=head.next;
        }
        return arr;
     */
        //2.使用栈
       /* LinkedList<ListNode> stack = new LinkedList<ListNode>();
        ListNode node = head;
        while(node != null){
            stack.push(node);
            node = node.next;
        }
        int[] arr = new int[stack.size()];
        int n=stack.size();
        for(int i = 0; i < n; i++){
            arr[i] = stack.pop().val;
        }
        return arr;*/  
        
    }
}

4 剑指 Offer 24. 反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点
示例:

输入: 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) {
        //双指针
       /* ListNode cur=null,node=head;
        while(node!=null)
        {
            ListNode temp=node.next;
            node.next=cur;
            cur=node;
           node=temp;
        }
        return cur;*/
        //递归
       /*if(head==null||head.next==null)
        {
            return head;
        }
        ListNode cur=reverseList(head.next);
        head.next.next=head;
        head.next=null;
        return cur;*/
    }
}

5 剑指 Offer 09. 用两个栈实现队列
示例 1:

输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
输入:
["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

提示:

1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用

题解:

class CQueue {
        LinkedList<Integer> stack1;
        LinkedList<Integer> stack2;
    public CQueue() {
         stack1=new LinkedList<Integer>();
         stack2=new LinkedList<Integer>();
    }
    
    public void appendTail(int value) {
        stack1.push(value);
    }
    
    public int deleteHead() {
        if(stack2.isEmpty())
        {
            while(!stack1.isEmpty())
            {
                stack2.push(stack1.pop());
            }
        }
        if(stack2.isEmpty())
        {
            return -1;
        }
        else
        {
            return stack2.pop();
        }
    }
}

6 剑指 Offer 10- I. 斐波那契数列
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 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) {
        //1.递归超时
      /* if(n==0)return 0;
        if(n==1)return 1;
        return fib(n-1)+fib(n-2)%1000000007*/;
        //2.数组存储叠加的值
       /*int[] arr=new int[n+1];
        if(n==0)return 0;
        if(n==1)return 1;
        arr[0]=0;
        arr[1]=1;
        int i=2;
        for(i=2;i<=n;i++)
        {
            arr[i]=(arr[i-1]+arr[i-2])%1000000007;
        }
        return arr[n];*/
        //3.直接迭代,动态规划
      /*  if(n==0)return 0;
        if(n==1)return 1;
        int a1=0;
        int a2=1;
        int sum=0;
        for(int i=2;i<=n;i++)
        {
            sum=(a1+a2)%1000000007;
            a1=a2;
            a2=sum;
        }
        return sum;*/
        //4.再简化一下
        if(n==0||n==1)
        return n;
        int a1=0,a2=1;
        for(int i=2;i<=n;i++)
        {
            a2=a1+a2;
            a1=a2-a1;
            a2 %=1000000007;
        }
        return a2;
    }
}

7 剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
题解:

class Solution {
    public int numWays(int n) {
      if(n==0)return 1;
      if(n==1)return 1;
      int f1=1,f2=1;
      int sum=0;
      for(int i=2;i<=n;i++)
      {
          sum=(f1+f2)%1000000007;
          f1=f2;
          f2=sum;
      }
      return sum;
    }
}

8 剑指 Offer 11. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:

输入:[3,4,5,1,2]
输出:1

示例 2:

输入:[2,2,2,0,1]
输出:0

题解:
二分法

import java.util.*;
class Solution {
    public int minArray(int[] numbers) {
        /*1.判断突然递减的点,通过效果都在50%以下
        for(int i=1;i<numbers.length;i++)
        {
            if(numbers[i]<numbers[i-1])
            return numbers[i];
        }
        return numbers[0];
        */
        /*2.逆序遍历判断突然递增的点,通过时,0 ms, 在所有 Java 提交中击败了100.00%的用户,
        内存消耗较高
        for(int j=numbers.length-2;j>=0;j--)
        {
            if(numbers[j+1]<numbers[j])return numbers[j+1];
        }
        return numbers[0];
        */
        /*3.和第一个数比较,比第一个数小时就是输出结果
        for(int i=1;i<numbers.length;i++)
        {
            if(numbers[i]<numbers[0])
            return numbers[i];
        }
         return numbers[0];
         */
        /*4.暴力解法
        Arrays.sort(numbers);
        return numbers[0];*/
        /*5.二分法,目前觉得最优
        int left=0,right=numbers.length-1;
        while(left<right)
        {
            int mid=(right+left)>>1;
            if(numbers[right]>numbers[mid])
            right=mid;
            else if(numbers[right]<numbers[mid])
            left=mid+1;
            else right--;//消除重复项
        }
        return numbers[left];
        */
        /*6.自带顺序的集合TreeSet
        TreeSet<Integer> set=new TreeSet<Integer>();
        for(int num:numbers)
        {
            set.add(num);
        }
        Iterator<Integer> it=set.iterator();
        return  (int)it.next();*/

        
    }
}

9 剑指 Offer 15. 二进制中1的个数
请实现一个函数,输入一个整数(以二进制串形式),输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
示例 1:

输入:00000000000000000000000000001011
输出:3
解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。

示例 2:

输入:00000000000000000000000010000000
输出:1
解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。

示例 3:

输入:11111111111111111111111111111101
输出:31
解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。

题解:使用 n=n&(n-1),每一次操作都会使n的二进制中的1少一个,依次循环下去,原数n的二进制中的1 的个数从右到左依次减少1,直到n为0;

public class Solution {
    // you need to treat n as an unsigned value
    public int hammingWeight(int n) {
       //1.首先调用Integer类中的toBinaryString()方法把整数n变成二进制形式的字符串,然后循环判断记录字符1的个位数。
        
       /* String str=Integer.toBinaryString(n);
        int count=0;
        for(int i=0;i<str.length();i++)
        {
            if(str.charAt(i)=='1')
            count++;
        }
        return count;*/
        //2.直接调用Integer 中的public static int bitCount(int i),返回指定 int 值的二进制补码表示形式的 1 位的数量。
       // return Integer.bitCount(n);
       //3.使用  n=n&(n-1),每一次操作都会使n的二进制中的1少一个,依次循环下去,原数n的二进制中的1 的个数从右到左依次减少1,直到n为0;
     int count=0;
       while(n!=0)
       {
           count++;
           n=n&(n-1);
       }
       return count;
       //运行超时
      /* int count = 0;
        while(n != 0){
            //count += n&1;
            if((n&1)==1)count++;
            n = n >> 1;
        }
        return count;*/
        


    }
}

10 剑指 Offer 17. 打印从1到最大的n位数
输入数字 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 num=1;
        //int num = (int) Math.pow(10, n) - 1;
        for(int i=1;i<=n;i++)
        {
            num*=10;
        }
        int[] arr=new int[num-1];
        for(int i=0;i<num-1;i++)
        {
            arr[i]=i+1;
        }
        return arr;
    }
}

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

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

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

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

题解:

/**
 * 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 node=head;
       //如果第一个节点就是要删除的节点,就直接返回下一节点
       if(head.val==val)return head.next;
       while(head!=null&&head.next!=null)
       {
           //从第二个节点开始判断
           if(head.next.val==val)
           {
               head.next=head.next.next;
               break;
           }
           head=head.next;
       } 
       return node;
       //从第一个节点开始判断
      /* ListNode node=new ListNode(0);
       node.next=head;
       ListNode cur=node;
       while(cur!=null&&cur.next!=null)
       {
           //从第二个节点开始判断
           if(cur.next.val==val)
           {
              cur.next=cur.next.next;
               break;
           }
          cur=cur.next;
       } 
       return node.next;*/
    }
}

12 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
示例:

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

题解:

public int[] exchange(int[] nums) {
        //1.通过双指针,把所给数组的奇数和偶数分别存在另一个数组的前半部分和后半部分
        int[] arr=new int[nums.length];
         int i=0,j=nums.length-1;
        for(int k=0;k<nums.length;k++)
        { 
           if(i<=j)  
           {
              if(nums[k]%2==0)
               {
                arr[j]=nums[k];
                 j--;
               }
               else
               {
                   arr[i]=nums[k];
                    i++;
               }
           }   
       
        }
        return arr;
       //2.还是双指针法,类似快排寻找基准点的思路
       //先从左寻找一个偶数的位置,然后从右寻找一个奇数的位置,当left<right时就交换位置,直到left==right循环停止。
       /* int left = 0;
        int right = nums.length - 1;
        while (left < right) {
            // 当找到一个偶数时,就跳出循环。
            // (这里有个求奇偶数的小技巧,就是当一个数是奇数时,它的二进制表示的最           后一位肯定是1
            while (left < right && (nums[left] %2 ) == 1) {
                left++;
            }
            // 当找到一个奇数时,就跳出循环
            while (left < right && (nums[right] %2) == 0) {
                right--;
            }
            // 如果两个指针还没有碰到一起时,说明找到了需要交换的位置
            if (left < right) {
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
            }
        }
        return nums;*/
       // int[] arr=new int[nums.length];



    }
}

13剑指 Offer 22. 链表中倒数第k个节点
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

题解:

/**
 * 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) {
   //1.首先遍历链表得到链表长度count,然后指针到count-k时停止跳出循环
   /* ListNode node=new ListNode(0);
    node=head;
    int count1=0,count2=0;
    while(node!=null)
    {
        count1++;
        node=node.next;
    }
    if(k==count1)return head;
    while(head!=null)
    {
        count2++;
        head=head.next;
        if(count2==count1-k)break;
    }
    return head;*/
    //2.双指针
    ListNode fast=new ListNode(0);
    fast=head;
    while(fast!=null)
    {
        fast=fast.next;
        if(k==0)head=head.next;
       else
        k--;

    }
    return head;
    }
}

14 剑指 Offer 25. 合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例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 head =new ListNode(0),node=head;
    while(l1!=null&&l2!=null)
    {
        if(l1.val>=l2.val)
        {
            head.next=l2;
            l2=l2.next;
        }
        else
        {
            head.next=l1;
            l1=l1.next;
        }
        head=head.next;
    }
    head.next=l1!=null?l1:l2;
    return node.next;*/
    //递归
          if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        if (l1.val <= l2.val) {
            l1.next=  mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

15 剑指 Offer 27. 二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。

例如输入:

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

镜像输出:

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

示例 1:

输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]

题解:
前序遍历或后续遍历,左右节点交换

/**
 * 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) {
        //前序遍历
       /* if(root==null)return root;
        TreeNode temp=root.right;
        root.right=root.left;
        root.left=temp;
        mirrorTree(root.left);
        mirrorTree(root.right);
        return root;*/
        //后序遍历
       /* if(root==null)return root;
        mirrorTree(root.left);
        mirrorTree(root.right);
        TreeNode temp=root.right;
        root.right=root.left;
        root.left=temp;
        return root;*/
        
    }
}

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

例如,二叉树 [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

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

题解:
判断左右子树的左右节点是否相同

/**
 * 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) {
     if(root==null)return true;
     return helper(root.left,root.right);

    }
    public boolean helper(TreeNode r1,TreeNode r2)
    {
        if(r1==null&&r2==null)
        return true;
        if(r1==null||r2==null)
        return false;
        return helper(r1.right,r2.left)&&r1.val==r2.val&&helper(r2.right,r1.left);
    }
}

17 剑指 Offer 29. 顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
示例 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]

题解:
定义上下左右4个标记位置,以顺时针的顺序遍历把值赋给一个一维数组。当++top > low,–right<left,–low<top,++left>right时会抛出异常,通过try{},finally{}进行处理,正好在要抛出异常时数组遍历完,也可以判断一下。

class Solution {
    public int[] spiralOrder(int[][] matrix) {
        int n=matrix.length;
         if (n == 0) return new int[0];
          int m= matrix[0].length;
        int left=0,right=matrix[0].length-1,top=0,low=matrix.length-1;int[] arr=new int[n*m];
        int count=0;
        //当++top > low,--right<left,--low<top,++left>right时会抛出异常,通过try{},finally{}进行处理,正好在要抛出异常时数组遍历完
       /* try{
             while(count<n*m)
       {
            
            for(int i=left;i<=right;i++)
            {
                arr[count++]=matrix[top][i];
            }
            top++;
            for(int i=top;i<=low;i++)
            {
                arr[count++]=matrix[i][right];
            }
            right--;
            for(int i=right;i>=left;i--)
            {
                  arr[count++]=matrix[low][i];
            }
            low--;
            for(int i=low;i>=top;i--)
            {
                arr[count++]=matrix[i][left];
            }
            left++;
        }
        }
       finally{
            return arr;
       }*/

       while(count<n*m)
       {
            
            for(int i=left;i<=right;i++)
            {
                arr[count++]=matrix[top][i];
            }
            if(++top>low)break;
            for(int i=top;i<=low;i++)
            {
                arr[count++]=matrix[i][right];
            }
            if(--right<left)break;
            for(int i=right;i>=left;i--)
            {
                  arr[count++]=matrix[low][i];
            }
            if(--low<top)break;
            for(int i=low;i>=top;i--)
            {
                arr[count++]=matrix[i][left];
            }
            if(++left>right)break;

       }
       return arr;






        /*
            while(count<m*n)
            {
              
                while(j<m)
                {
                    
                    
                     if(matrix[i][j]!=-1)
                    arr[count++]=matrix[i][j];
                    matrix[i][j]=-1;
                    j++;

                  
                }
               --j;    
                while(i<n)
                {
                    if(matrix[i][j]!=-1)
                    arr[count++]=matrix[i][j];
                    matrix[i][j]=-1;
                    i++;
                 
                }
                i--;
                while(j>=0)
                {
                     if(matrix[i][j]!=-1)
                     arr[count++]=matrix[i][j];
                     matrix[i][j]=-1;
                     j--;
                   
                }
                j++;
                while(i>=0)
                {
                    if(matrix[i][j]!=-1)
                    arr[count++]=matrix[i][j];
                    matrix[i][j]=-1;  
                    i--;
                }
                i++;
               
              
            }
       
     
       return arr; */
               
           
        
    }
}

18 剑指 Offer 30. 包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 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.

题解:使用双栈,第一个栈正常的实现push,pop,top函数的功能,第二个栈保持栈顶最小。

class MinStack {
//1.双栈
  Stack<Integer>stack1,stack2;
    /** initialize your data structure here. */
    public MinStack() {
        stack1=new Stack<Integer>();
        stack2=new Stack<Integer>();
    }
    
    public void push(int x) {
        stack1.push(x);
        if(stack2.size()==0||stack2.peek()>=x)
        stack2.push(x);
    }
    
    public void pop() {
    //如果出栈的数恰好等于第二个栈的栈顶元素,则栈二需要出栈,保持栈顶最小。
        if(stack1.pop().equals(stack2.peek()))
        stack2.pop();
    }
    
    public int top() {
      return   stack1.peek();
    }
    public int min() {
     return   stack2.peek();
    }
}

/**
 * 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();
 */

19 剑指 Offer 32 - II. 从上到下打印二叉树 II
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

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

    3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

题解:
BFS DFS两种解法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
      //1.DFS
       /* List<List<Integer>>list=new ArrayList<>();
        levelHelper(list,root,0);
        return list;*/
        List<List<Integer>>list=new ArrayList<>();
        Queue<TreeNode>queue=new LinkedList<>();
        if(root != null)
        queue.add(root);
        while(!queue.isEmpty())
        {
        //初始化每一层的list
            List<Integer>tempList=new ArrayList<>();
            //queue.size()记录每一层的数量
            for(int i=queue.size();i>0;i--)
            {
              //出队
                TreeNode node=queue.poll();
                //添加数值到list
                tempList.add(node.val);
                if(node.left!=null)queue.add(node.left);
                if(node.right!=null)queue.add(node.right);
            }
            //添加每一层的list
            list.add(tempList);
        }
        return list;
    }
   /* public void levelHelper(List<List<Integer>>list,TreeNode root,int height)
    {
        if(root==null)return;
        //level表示的是层数,如果level >= list.size(),说明到下一层了,所以
    //要先把下一层的list初始化,防止下面add的时候出现空指针异常
        if(height>=list.size())
        {
            list.add(new LinkedList<>());
        }
        //遍历到那一层,就把数值添加到那一层的list中
        list.get(height).add(root.val);
        levelHelper(list,root.left,height+1);
        levelHelper(list,root.right,height+1);
    }*/
}

20 剑指 Offer 39. 数组中出现次数超过一半的数字
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

题解:
本题常见的三种解法:

哈希表统计法: 遍历数组 nums ,用 HashMap 统计各数字的数量,即可找出 众数 。此方法时间和空间复杂度均为 O(N) 。
数组排序法: 将数组 nums 排序,数组中点的元素 一定为众数。
摩尔投票法: 核心理念O(N) 和 O(1) ,为本题的最佳解法。
当 票数 votes 等于 0 ,则假设当前数字 num 是众数;
当 num ==x 时,票数 votes 自增 1 ;当 num != x 时,票数 votes 自减 1 ;

class Solution {
    public int majorityElement(int[] nums) {
       //1.因为重复数超过了一半,排序后中间那个数就是重复数最多的数
       /* Arrays.sort(nums);
        int mid=nums.length/2;
        return nums[mid];*/
        //2.HashMap
       /* HashMap<Integer,Integer> map=new HashMap<>();
        int mid=nums.length/2;
        int count=1;
        for(int i=0;i<nums.length;i++)
        {
            if(map.containsKey(nums[i]))
            {
                 count=map.get(nums[i])+1;
                 if(count>mid)
                     return nums[i];
             //  map.remove(nums[i]);
                 map.put(nums[i],count);
            }
            else
            {
                map.put(nums[i],1);
            }
           
           
        }
        return nums[0];*/
        //3.投票法 前提也是重复数超过一半,否者可能不成立
        int votes=0, x=0;
        for(int num:nums)
        {
            if(votes==0)x=num;
            votes+=x==num?1:-1;
        }
        return x;

    }
}

21 剑指 Offer 68 - II. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

在这里插入图片描述

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

22 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

在这里插入图片描述

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

23 剑指 Offer 65. 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
示例:

输入: a = 1, b = 1
输出: 2
class Solution {
    public int add(int a, int b) {
    //拆分为无进位(异或)+进位(与后左移)
   /* while(b!=0)
    {
        int c=(a&b)<<1;
        a^=b;
        b=c;
    }
    return a;*/
   if(b==0)return a;
    return add(a^b,(a&b)<<1);
    }
}

24剑指 Offer 62. 圆圈中最后剩下的数字
0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

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

输入: n = 5, m = 3
输出: 3

示例 2:

输入: n = 10, m = 17
输出: 2

题解:
我们将上述问题建模为函数 f(n, m),该函数的返回值为最终留下的元素的序号。

首先,长度为 n 的序列会先删除第 m % n 个元素,然后剩下一个长度为 n - 1 的序列。那么,我们可以递归地求解 f(n - 1, m),就可以知道对于剩下的 n - 1 个元素,最终会留下第几个元素,我们设答案为 x = f(n - 1, m)

由于我们删除了第 m % n 个元素,将序列的长度变为 n - 1。当我们知道了 f(n - 1, m) 对应的答案 x 之后,我们也就可以知道,长度为 n 的序列最后一个删除的元素,应当是从 m % n 开始数的第 x 个元素。因此有 f(n, m) = (m % n + x) % n = (m + x) % n。

class Solution {
    public int lastRemaining(int n, int m) {
        //约瑟夫环问题
             int res=0;
               //f(n,m)=f(m+f(n-1,m)),递推开始为发f(2,m)=f(m+f(1,m))%2,所以i从2开始
               for(int i=2;i<=n;i++)
               {
                   res=(res+m)%i;
               }  
               return res;           
         //return   f(n,m);  
    }
   /* public int f(int n,int m){
        if(n==1)return 0;
        return (m+f(n-1,m))%n;
    }*/


                               
}

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

输入: [1,2,3,4,5]
输出: True

示例 2:

输入: [0,0,1,2,5]
输出: True
class Solution {
    public boolean isStraight(int[] nums) {
        //只需要满足两个条件:不包含重复元素;max-min<5
        //1.set,不排序
       /*  int min=14,max=0;
         Set<Integer> set=new HashSet();
        for(int num:nums)
        {
            if(num==0)continue;
            max=Math.max(num,max);
            min=Math.min(num,min);
            if(set.contains(num))return false;
            set.add(num);
        }
        return max-min<5;*/
        //2.先排序,记录大小王的个数index,然后用最大数-nums[index]
        Arrays.sort(nums);
        int index=0;
        for(int i=0;i<4;i++)
        {
            if(nums[i]==0)index++;
            else
            {
                if(nums[i]==nums[i+1])return false;
            }
        }
        return nums[4]-nums[index]<5;
       }
}

26 剑指 Offer 59 - I. 滑动窗口的最大值
给定一个数组 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) {
      //1.暴力解法
      /*if(nums==null||k==0)return new int[0];//如果数组为空或者k为零返回空数组
        int[] arr=new int[nums.length-k+1];
        for(int i=0;i<=nums.length-k;i++)
        {
            int max=i;
            for(int j=i+1;j<i+k;j++)
            {
                if(nums[j]>nums[max])
                {
                    max=j;
                }
            }
            arr[i]=nums[max];
        }
        return arr;  */
      //2.单调队列
      Deque<Integer> queue=new LinkedList<>();
      if(nums==null||k==0)return new int[0];//如果数组为空或者k为零返回空数组
      int[] arr=new int[nums.length-k+1];
      int index=0;
      for(int i=0;i<nums.length;i++)
      {   // 删除小于或等于nums[i]的队列尾部的数,然后添加到后面。
        // nums[i]小于队列尾部数直接添加在后面,保证队首下标对应的值最大
          while(queue.size()!=0&&nums[queue.peekLast()]<=nums[i])
          {
              queue.pollLast();
          }
       
          queue.addLast(i);
          if(queue.peekFirst()==(i-k))//当最大数的下标不在新的滑动窗口时,就把队首删除
          {
              queue.pollFirst();
          }
          if(i>=(k-1))//形成滑动窗口后,直接取队首下标对应的元素值
          {
              arr[index]=nums[queue.peekFirst()];
              index++;
          }
      }
         
    return arr;

    }
    
}

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

输入: s = "abcdefg", k = 2
输出: "cdefgab"

示例 2:

输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
class Solution {
    public String reverseLeftWords(String s, int n) {
       //方法一:字符串切片
        /*String str1=s.substring(0,n);
        String str2=s.substring(n,s.length());
        return str2+str1;*/
        //列表遍历拼接
       /* StringBuilder sb=new StringBuilder();
        for(int i=n;i<s.length();i++)
        {
            sb.append(s.charAt(i));
        }
        for(int i=0;i<n;i++)
        {
             sb.append(s.charAt(i));
        }
        return sb.toString();*/
        //利用求余运算简化代码
       /* StringBuilder sb=new StringBuilder();
        for(int i=n;i<s.length()+n;i++)
        {
            sb.append(s.charAt(i%s.length()));
        }
         return sb.toString();*/
        //字符串遍历拼接
         String str="";
        for(int i=n;i<s.length()+n;i++)
        {
            str+=s.charAt(i%s.length());
        }
         return str;
    }
}

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

输入: "the sky is blue"
输出: "blue is sky the"

示例 2:

输入: "  hello world!  "
输出: "world! hello"

解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:

输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
class Solution {
    public String reverseWords(String s) {
     //1.切割
      /*String[] str=s.trim().split(" ");
      StringBuilder sb=new StringBuilder();
      for(int i=str.length-1;i>=0;i--)
      {
          if(str[i].equals(""))continue;//如果为空字符就跳过执行
          sb.append(str[i]+" ");
      }
       return sb.toString().trim(); */
       //2.
       /*
       class Solution {
   		 public String reverseWords(String s) {
      	  s = s.trim(); // 删除首尾空格
        int j = s.length() - 1, i = j;
       	 StringBuilder res = new StringBuilder();
        while(i >= 0) {
            while(i >= 0 && s.charAt(i) != ' ') i--; // 搜索首个空格
            res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
            while(i >= 0 && s.charAt(i) == ' ') i--; // 跳过单词间空格
            j = i; // j 指向下个单词的尾字符
        }
        return res.toString().trim(); // 转化为字符串并返回
    }
}*/


}

29 剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

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

示例 2:

输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
在这里插入代码片

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

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]

示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]
class Solution {
    public int[] twoSum(int[] nums, int target) {
       //二分法,内循环中查找另一个数
       /* for(int i=0;i<nums.length;i++)
        {
            int left=i+1,right=nums.length-1,t=target-nums[i];
            while(left<=right)
            {
                int mid=(right+left)/2;
                if(nums[mid]>t)
                right=mid-1;
                else if(nums[mid]<t)
                left=mid+1;
                else
                return new int[]{nums[i],nums[mid]};
            }
        }
        return new int[]{};*/
        //HashSet
     /*  HashSet<Integer> set = new HashSet<>();
        for(int num : nums){
            set.add(num);
        }
        for(int num : nums){
            int e = target - num;
            if(set.contains(e)){
                return new int[]{num,e};
            }
        }
        return new int[]{};*/


        //双指针
     /* int i=0,j=nums.length-1;
        int res=0;
        while(i<j)
        {
            res=nums[i]+nums[j];
            if(res>target)j--;
            else if(res<target)
            i++;
            else 
            return  new int[]{nums[i],nums[j]};
        }
        return new int[]{};*/
        
    }
}

31 剑指 Offer 55 - II. 平衡二叉树
示例 1:

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

3

/
9 20
/
15 7
返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

     	1
      / \
     2   2
    / \
   3   3
  / \
 4   4
在这里插入代码片

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

例如:

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

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。
class Solution {
    public int maxDepth(TreeNode root) {
      //1.后序遍历(DFS)
      //树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现,这里使用递归实现。
      /*  if(root==null)
        return 0;
      
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;*/
    //2.层序遍历(BFS)
    //树的层序遍历 / 广度优先搜索往往利用 队列 实现。
    if(root == null) return 0;
        LinkedList<TreeNode> queue = new LinkedList<>() , tmp;
        int res = 0;
        queue.add(root);
      /*  while(!queue.isEmpty()) {
            tmp = new LinkedList<>();
            for(TreeNode node : queue) {
                if(node.left != null) tmp.add(node.left);
                if(node.right != null) tmp.add(node.right);
            }
            queue = tmp;
            res++;
        }*/
        while (!queue.isEmpty()) {
            res++;
            int n = queue.size();
            for (int i = 0; i < n; i++) {
                TreeNode node = queue.poll();
                if (node.left != null) queue.add(node.left);
                if (node.right != null) queue.add(node.right);
            }
        }
        return res;
    }
}

33 剑指 Offer 54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1

   3
  / \
 1   4
  \
   2
输出: 4

示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3

       5
      / \
     3   6
    / \
   2   4
  /
 1
输出: 4
在这里插入代码片

34 剑指 Offer 53 - II. 0~n-1中缺失的数字
示例 1:
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

输入: [0,1,3]
输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8
class Solution {
    public int missingNumber(int[] nums) {
       //nums[i]=i为右子数组缺数,nums[i]!=i为左子数组缺数
        int left=0,right=nums.length-1;
        while(right>=left)
        {
            int  mid=(left+right)/2;
            if(nums[mid]!=mid)
            right=mid-1;
            else
            left=mid+1;

        }
        return left;
    }
}

35 剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

class Solution {
    public int search(int[] nums, int target) {
        /*int count=0;
        for(int i=0;i<nums.length;i++)
        {
            if(nums[i]==target)count++;
        }
        return count;*/
        int left=0,right=nums.length-1,count=0;
        while(left<=right)
        {
            int mid=(left+right)/2;
            if(nums[mid]>=target)//等号放右边,找出来的值是重复数的第一个
            right=mid-1;
            if(nums[mid]<target)
            left=mid+1;
        }
        for(int i=left;i<nums.length&&nums[i]==target;i++)
        {

            count++;
        }
        return count;
        
    }
}

36 剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在这里插入图片描述
在节点 c1 开始相交。
示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

在这里插入图片描述

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

在这里插入图片描述

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。
/**
 * 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) {
       /* HashSet<ListNode> set = new HashSet<>();
        ListNode curA = headA;
        ListNode curB = headB;
        while (curA != null) {
            set.add(curA);
            curA = curA.next;
        }
        while (curB != null) {
            if (set.contains(curB)) return curB;
            curB = curB.next;
        }
        return null; */
        //走自己的路然后在走别人的路,别人也是如此,然后在某一时刻相遇。
        ListNode node1=headA,node2=headB;
        while(node1!=node2)
        {
              node1 = node1 == null ? headB : node1.next;
              node2= node2 == null ? headA : node2.next;           
        }
       return node1;
      /* Stack<ListNode> s1=new Stack<>();
       Stack<ListNode> s2=new Stack<>();
        while(headA!=null)
        {
            s1.push(headA);
            headA=headA.next;
        }
         while(headB!=null)
        {
            s1.push(headB);
            headB=headB.next;
        }
        while(s1.size()!=0&&s2.size()!=0)
        {
          s1.pop();
          s2.pop();
            if(s1.peek()!=s2.peek())return s1.peek().next;
            
        }
        return null;*/
      }
}

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

示例:

s = "abaccdeff"
返回 "b"

s = "" 
返回 " "
class Solution {
    public char firstUniqChar(String s) {
      
      /* for (int i = 0; i < s.length(); i++) {
           char ch=s.charAt(i);
           //首次出现的位置是当前位置,且后面没有再出现这个字符
            if(s.indexOf(ch)==i&&s.indexOf(ch,i+1)==-1)
                return s.charAt(i);
        }
        return ' ';*/
        //2.使用HashMap,遍历字符数组,把字符当作键值,如果字符重复对应的值就是false,否则为true
        /* HashMap<Character,Boolean> map=new LinkedHashMap<>();
         char[] arr=s.toCharArray();
         for(char ch:arr)
         {
             map.put(ch,!map.containsKey(ch));
         }
         for(char c:arr)
         {
             if(map.get(c))return c;
         }
         return ' ';*/
        //这个方法和接最多重复整数的题类似
         HashMap<Character, Integer> map = new LinkedHashMap<>();
         for(int i=0;i<s.length();i++)
         {
             if(map.containsKey(s.charAt(i)))
             {
            
                  map.put(s.charAt(i), map.get(s.charAt(i))+1);

             }
             else
             {
                 map.put(s.charAt(i),1);
             }
            

         }
         for(char c:map.keySet())//使用 LinkedHashMap保证顺序性
         {
             if(map.get(c)==1)
             return c;
         }
         /*
         使用没顺序的HashMap,遍历字符数组
         for(int i = 0;i < s.length();i++){
            if(map.get(s.charAt(i)) == 1){
                return s.charAt(i);
            }
        }
         */
         return ' ';
         //字典查找法
         /*int[] arr=new int [26];
         for(int i=0;i<s.length();i++)
         {
             arr[s.charAt(i)-'a']++;
         }
         for(int j=0;j<s.length();j++)
         {
            if(arr[s.charAt(j)-'a']==1)return s.charAt(j);
         }
         return ' ';*/
    }
}

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

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

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
class Solution {
    public int maxSubArray(int[] nums) {
        /*1.
        假设sum<=0,那么后面的子序列肯定不包含目前的子序列,只会越加越小
        如果sum > 0对于后面的子序列是有好处的,
        res = Math.max(res, sum)保证可以找到最大的子序和。
        */
      /*int res = nums[0];
        int sum = 0;
        for (int num : nums) {
            if (sum > 0)
                sum += num;
            else
                sum = num;
            res = Math.max(res, sum);
        }
        return res;*/
       // 2.动态规划
       /*int sum=0,maxsum=nums[0];
       for(int num:nums)
       {
           sum=Math.max(sum+num,num);
           maxsum=Math.max(sum,maxsum);
       }
       return maxsum; */
       int thisSum=0,maxSum=nums[0];
       for(int i=0;i<nums.length;i++)
       {
           if(thisSum>0)
           {
               thisSum+=nums[i];
           }
           else{
               thisSum=nums[i];
           }
           if(thisSum>maxSum)
           {
               maxSum=thisSum;
           }
       }
       return maxSum;
    }
}

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

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
       //1.先排序,然后把前k个数赋值给另一个新的数组
        /*Arrays.sort(arr);
        int[] arr1=new int[k];
        for(int i=0;i<k;i++)
        {
            arr1[i]=arr[i];
        }
        return arr1;*/
        //2.选择排序
     /*    int[] arr1=new int[k];
        for(int i=0;i<k;i++)
        {
            int min=i;
            for(int j=i+1;j<arr.length;j++)
            {
                if(arr[min]>arr[j])
                {
                    min=j;
                }
            }
            if(min!=i)
            {
                int temp=arr[i];
                arr[i]=arr[min];
                arr[min]=temp;
            }
           arr1[i]=arr[i];
        }
        return arr1;*/
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值