力扣(LeetCode)剑指offer刷题笔记(java),已完结!!!

Posted Ferron Zhu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣(LeetCode)剑指offer刷题笔记(java),已完结!!!相关的知识,希望对你有一定的参考价值。

文章目录

3、数组中重复的数字

题目描述

找出数组中重复的数字。

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

示例

输入:

[2, 3, 1, 0, 2, 5, 3]

输出:

2或3

思路分析

思路一:题目说要寻找一个重复的元素,因此可以考虑使用HashSet来解决该问题。集合中不能存在重复的元素,将数组中每个元素都添加到Set中,如果添加失败,则代表该元素是重复元素。使用这种方法相当于以空间换时间,时间复杂度会最小

思路二:原地交换法,题目隐藏了条件就是所有数字都在 0~n-1 的范围内,因此我们可以考虑使用原地交换的方法。遍历数组,如果数组索引等于对应的元素,则跳过,否则;则进行交换,即把数值为n的元素放在数组的第n个索引,当发现第n个索引已经有相同的元素时,则返回重复的元素。

代码

思路一:

class Solution 
    public int findRepeatNumber(int[] nums) 
        Set<Integer> set = new HashSet<>();
        int result = -1;

        for(int num:nums)
            if(!set.add(num))
                result=num;
            
        
        return result;


    

思路二:

class Solution 
    public int findRepeatNumber(int[] nums) 

        for(int i=0; i<nums.length; i++)
            if(i == nums[i])
                continue;
            else
                if(nums[i] == nums[nums[i]]) return nums[i];
                int temp = nums[i];
                nums[i]=nums[nums[i]];
                nums[temp]=temp;      
            
        
        return -1;
    

4、二维数组中的查找

题目描述

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

示例

输入:

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

输出:

true

思路分析

因为这个一个排好序的二维数组,查找时考虑使用二分查找。然而由题目可知,这个二维数组的排序有一定规律,按照二分查找的思路就是选定一个mid点,然而进行查找。因此考虑选择二维数组右上方的元素作为mid,如果target等于mid,则找到;如果target大于mid,就说明target不在第一行,此时接着判断第二行和最后一列,循环即可;如歌targte小于mid,就说明target不在最后一列,此时接着判断第一行和倒数第二列,循环即可。

代码

class Solution 
    public boolean findNumberIn2DArray(int[][] matrix, int target) 
        if(matrix.length == 0 || matrix[0].length == 0) return false;
        int i = 0;
        int j = matrix[0].length-1;
        boolean flag = false;
        while(i<=matrix.length-1 && j>=0)
            if(target == matrix[i][j])
                flag=true;
                break;
            
            else if(target > matrix[i][j])
                i++;
            
            else
                j--;
            
        
        return flag;

    

5、替换空格

题目描述

请实现一个函数,将一个字符串中的每个空格替换成“%20”。

示例

输入:

"We Are Happy"

输出:

"We%20Are%20Happy"

思路分析

题目需要替换字符串,因此考虑使用StringBuffer类来实现。新建一个StringBuffer,遍历原字符串,如果字符为空格,则在sb中添加一个%20,如果字符不为空格,则添加该字符。

代码

public String replaceSpace (String s) 

    StringBuffer sb = new StringBuffer();

    for(int i=0; i<s.length(); i++)
        if(s.charAt(i)==' ')
            sb.append("%20");
        
        else
            sb.append(s.charAt(i));
        
    
    return sb.toString();     

6、从尾到头打印链表

题目描述

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

示例

输入:

head = [1,3,2]

输出:

[2,3,1]

思路分析

思路一:从尾到头打印这个链表,会考虑到使用先进后出的栈来解决。

思路二:另外一个思路也可以首先遍历一遍链表,记录下其长度,然后建立一个相同长度的数组,从后往前赋值即可。

代码

思路一:

class Solution 
    public int[] reversePrint(ListNode head) 
        Stack<ListNode> stack = new Stack<ListNode>();

        for(ListNode node=head; node!=null; node=node.next)
            stack.push(node);
        
        int size = stack.size();  //记录栈长度,否则随着出栈栈长度会变小
        int[] result = new int[size];
        
        for(int i=0; i<size; i++)
            result[i]=stack.pop().val;
        

        return result;

    

思路二:

class Solution 
    public int[] reversePrint(ListNode head) 
        
        ListNode tmp = head;
        int len = 0;
        while(tmp!=null)
            tmp=tmp.next;
            len++;
        

        int[] array = new int[len];
        for(int j=len-1; j>=0; j--)
            array[j] = head.val;
            head=head.next;
        
        return array;
    

7、重建二叉树

题目描述

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

示例

输入:

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

输出:

    3
   / \\
  9  20
    /  \\
   15   7

思路分析

思路一:知道二叉树的前序遍历和中序遍历,可以考虑使用递归来解决。首先通过前序遍历得到该二叉树的root结点,示例中root结点为1。知道root结点后,通过中序遍历知道了root的左子树的前序遍历为[9],中序遍历为[9],右子树前序遍历为[20,15,7][15,20,7],接着分别对root的左子树和右子树进行同样的递归操作即可。

思路二:整个思路跟思路一类似,不同的是为了提高效率,加入HashMap存储中序遍历的值与索引的映射

代码

思路一:

class Solution 
    public TreeNode buildTree(int[] preorder, int[] inorder) 
        if(preorder.length==0 | inorder.length==0)
                return null;
        
            TreeNode root = new TreeNode(preorder[0]);

        for(int i=0; i<inorder.length; i++)
            //从前序遍历中找到中序遍历中的二叉树的根结点
            if(inorder[i] == preorder[0])
                root.left=buildTree(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
                root.right=buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
                break;
            
        
        return root;
    

思路二:

class Solution 

    int[] preorder;
    HashMap<Integer,Integer> map = new HashMap<Integer, Integer>();

    public TreeNode buildTree(int[] preorder, int[] inorder) 

        this.preorder = preorder;
        for(int i=0; i<inorder.length; i++)
            map.put(inorder[i], i);
        
        return recur(0,0,preorder.length-1);

        
    
    TreeNode recur(int root, int left, int right) 
        //递归终止条件
        if(left>right) return null;
        //建立新结点
        TreeNode node = new TreeNode(preorder[root]);
        //查找出preorder[root]在inorder中的索引
        int i = map.get(preorder[root]);
        //左子树:跟结点为root+1,范围为preorder[left]-preorder[i-1]
        node.left=recur(root+1,left,i-1);
        //右子树:跟结点i-left+root+1(根结点索引+左子树长度+1),范围为preorder[i+1]-preorder[right]
        node.right=recur(i-left+root+1,i+1,right);
        return node;
    
        
        

9、两个栈来实现一个队列

题目描述

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

思路分析

考虑使用两个栈来实现一个队列,首先考虑的是该队列只进不出,这样就可以直接使用stack1来存储该“队列”的所有元素。一旦考虑到出列的情况,就相当于要把stack1的所有元素先倒置,即把stack1的元素全部装进stack2,这样stack2中的元素就是stack1中所有元素的倒置,这样出列操作就可以通过stack2的pop方法来完成。还有一些细节就是因为队列是先进先出,因为当需要出列的时候应该先检查stack2是否为空,如果为空就将stack1的元素放入,如果不为空则直接出列即可。

代码

class CQueue 

    Stack<Integer> stack1;
    Stack<Integer> stack2;

    public CQueue() 
        stack1 = new Stack<Integer>();
        stack2 = new Stack<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();
        
 
    


10-1、斐波那契数列

题目描述

写一个函数,输入 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。

示例

输入:

4

输出:

3

思路分析

这题最简单的思路便是递归,但是递归会产生许多的重复计算,因此可以考虑使用动态规划来优化。动态规划的状态转移方程便为斐波那契的通项公式dp[n]=dp[n-1]+dp[n-2]

代码

class Solution 
    public int fib(int n) 
        if(n<=1)
            return n;
        
        int a=0;
        int b=1;
        int sum;
        for(int i=0; i<n; i++)
            sum=(a+b)%1000000007;
            a=b;
            b=sum;
        
        return a;
    

10-2、跳台阶

题目描述

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

思路分析

这道题跟前面的斐波那契数列很类似。我们可以用反向思维来理解,假设青蛙跳到第n阶台阶,那么它的上一步会在哪呢?要么在n-1阶台阶,要么在n-2阶台阶,这样我们就可以得到青蛙跳到第n阶台阶的跳法等于青蛙跳到第n-1阶台阶的跳法与跳到第n-2阶台阶的跳法之和。同样使用动态规划求解。

代码

class Solution 
    public int numWays(int n) 
        int a=1;
        int b=1;
        int sum;
        for(int i=0; i<n; i++)
            sum=(a+b)%1000000007;
            a=b;
            b=sum;
        
        return a;
    

11、旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

示例

输入:

[3,4,5,1,2]

输出:

1

思路分析

这题最开始没有思路,网上比较流行的就是使用二分搜索来完成,但是实现的过程也有一些细微的差异,下面列了一个我觉得较为理想的答案。首先mid大于high时,更新左边界值,而当mid小于high时,则更新右边界值,当两者相等时,很难判断,则让high自减1继续判断。

代码

class Solution 
    public int minArray(int[] numbers) 
        if(numbers.length == 0)
            return 0;
        
        
        int low = 0;
        int high = numbers.length-1;
        while(low<high)
            int mid=(low+high)/2;
            if(numbers[mid]>numbers[high])
                low=mid+1;
            else if(numbers[mid]==numbers[high])
                high=high-1;
            else
                high=mid;
            
        
        return numbers[high];    
    

12、矩阵中的路径

题目描述

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

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

示例

输入:

board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"

输出:

true

思路分析

这道题目用的思想是回溯算法,典型的就是深度优先遍历+剪枝。逐个单元格进行深度优先遍历,一旦判断存在即返回true,否则返回false。

代码

class Solution 
    public boolean exist(char[][] board, String word) 
        char[] array = word.toCharArray();
        for(int i=0; i<board.length; i++)
            for(int j=0; j<board[0].length; j++)
                if(dfs(board,array,i,j,0)) return true;
            
        
        return false;
    

    public boolean dfs(char[][] board, char[] array, int i, int j, int k)
        //上下左右的元素越界或者不等于array[k]时,返回false
        if(i<0 || i>=board.length || j<0 || j>=board[0].length || board[i][j] != array[k])
            return false;
        
        //当k==array.length-1时,还未返回false则返回true
        if(k==array.length-1)
            return LeetCode Java刷题笔记—剑指 Offer 22. 链表中倒数第k个节点

Leetcode刷题Python剑指 Offer 32 - I. 从上到下打印二叉树

剑指Offer018 - 020 刷题笔记 回文 双指针DP中心扩展

Leetcode刷题Python剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

LeetCode Java刷题笔记—剑指 Offer 54. 二叉搜索树的第k大节点

Leetcode刷题Python剑指 Offer 32 - II. 从上到下打印二叉树 II