热题HOT 100(31-40)

31.给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符

输入: word1 = "horse", word2 = "ros"
输出: 3
解释: 
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

编辑距离详解

package wg;
public class Solution {
    public static int minthree(int a,int b,int c){
        return Math.min(Math.min(a,b),c);
    }
    public static int minDistance(String s1,String s2){
        int m = s1.length();
        int n = s2.length();
        int[][] dp = new int[m+1][n+1];
        for(int i=1;i<=m;i++){
            dp[i][0]=i;
        }
        for(int j=1;j<=n;j++){
            dp[0][j]=j;
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(s1.charAt(i-1)==s2.charAt(j-1))
                    dp[i][j]=dp[i-1][j-1];
                else{
//分别对应 插入 删除 替换
                    dp[i][j]=minthree(dp[i][j-1]+1,dp[i-1][j]+1,dp[i-1][j-1]+1);
                }

            }
        }
        return dp[m][n];
    }
    public static void main(String[] args) {
        System.out.println(minDistance("horse","ros"));
        //3
    }
}

32.给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
注意:
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
不能使用代码库中的排序函数来解决这道题。

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

用三个指针(p0, p2curr)来分别追踪0的最右边界2的最左边界当前考虑的元素。沿着数组移动 curr指针,若nums[curr] = 0,则将其与 nums[p0]互换;若nums[curr] = 2 ,则与 nums[p2]互换。

算法

1.初始化0的最右边界:p0 = 0。在整个算法执行过程中 nums[idx < p0] = 0.

2.初始化2的最左边界 :p2 = n - 1。在整个算法执行过程中 nums[idx > p2] = 2.

3.初始化当前考虑的元素序号 :curr = 0.

4.While curr <= p2 :

5.若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针**都**向右移。

6.若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。

7.若 nums[curr] = 1 :将指针curr右移。
package wg;
import java.util.Arrays;
public class Solution {
    public static void sortColors(int[] nums) {
        // 对于所有 idx < i : nums[idx < i] = 0
        // j是当前考虑元素的下标
        int p0 = 0, curr = 0;
        // 对于所有 idx > k : nums[idx > k] = 2
        int p2 = nums.length - 1;
        int tmp;
        while (curr <= p2) {
            if (nums[curr] == 0) {
                // 交换第 p0个和第curr个元素
                // i++,j++
                tmp = nums[p0];
                nums[p0++] = nums[curr];
                nums[curr++] = tmp;
            }
            else if (nums[curr] == 2) {
                // 交换第k个和第curr个元素
                // p2--
                tmp = nums[curr];
                nums[curr] = nums[p2];
                nums[p2--] = tmp;
            }
            else curr++;
        }
    }
    public static void main(String[] args) {
        int[] nums = {2,0,2,1,1,0};
        sortColors(nums);
        System.out.println(Arrays.toString(nums));
        //[0, 0, 1, 1, 2, 2]
    }
}

33.给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

滑动窗口算法通用思想

package wg;
public class Solution {
    public static String minWindow(String s, String t) {
        int m=s.length(),n=t.length();
        if(m<n){
            return "";
        }
        //转化成数组,提升查找速度
        char[] S=s.toCharArray();
        char[] T=t.toCharArray();
        int[] map=new int[256];//利用ASSII码做映射,比hashmap效率高
        for(int i=0;i<n;i++){
            map[T[i]]++;
        }
        int start=-1;
        int L=0,R=0;//滑动窗口[L,R]
        int count=0;//保存窗口里已经找到了多少个字符
        int min=m+1;
        //左边不能越过m-n  不然字符串短于查找的 右边不能越过m
        while(L<=m-n&&R<m){
            //找到一个T中的
            map[S[R]]--;
            //减减过后>=0 说明开始这个位置有加加  说明T中有这个字母
            if(map[S[R]]>=0){
                count++;
            }
            //当找到总字符等于T的长度时
            if(count==n){
                //L左移一个相当于取消一个字符 不能=是因为里面会进行L++
                //当遇到map[S[L]]>=0时,说明这个字符是T中的,因为不是T中的不会一开始的判断就是>=0,都是负数,前面减的 首先明确map[S[L]]肯定是前面map[S[R]]操作过的数
                while(map[S[L]]<0){//L尽量往左移动
                    map[S[L]]++;
                    L++;
                }
                //取消字符后是否需要更新结果
                if(R-L<min){//记录位置
                    min=R-L;//因为L位于的位置是刚好在T中的字符  
//比如s= abcde t=cd;    L位于2 R位于3   3-2=1;所以结果在return要+1
                    start=L;
                }
                map[S[L]]++;//L继续右移一位,且此时S[L]是需要的那个字符(在T中)进行下次寻找
                L++;
                count--;
            }
            R++;
        }
        if(min<m+1){
            return s.substring(start,start+min+1);
        }
        return "";
    }

    public static void main(String[] args) {
         System.out.println(minWindow("aasdfghj","gfd"));
         //dfg
    }
}

34.给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

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

回溯算法:每个数都可以选择放或者不放

package wg;
import java.util.*;
public class Solution {
    private static List<List<Integer>> res = new ArrayList<>();
    public static List<List<Integer>> subsets(int[] nums) {
        List<Integer> list = new ArrayList<>();
        res.add(list);//先把空放进去
        if(nums==null||nums.length==0){
            return res;
        }
        numsziji(0,nums,list);
        return res;
    }
    public static void numsziji(int start, int[] nums, List<Integer> list){
        if(start>=nums.length){
            return;
        }
        //选择放
        list.add(nums[start]);
        res.add(new ArrayList<>(list));//注意这里
        numsziji(start+1,nums,list);
        //选择不妨
        list.remove(list.size()-1);
        numsziji(start+1,nums,list);
    }
    public static void main(String[] args) {
        int[] nums = {1,2,3};
        List<List<Integer>> result = new ArrayList<>();
        result=subsets(nums);
        System.out.println(result);
        //[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]
    }
}

35.给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.

在二维平面上使用回溯法 DFS+状态置换

package wg;
public class Solution {
    private boolean[][] marked;
    //        x-1,y
    // x,y-1  x,y    x,y+1
    //        x+1,y
    private int[][] direction = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}};
    // 盘面上有多少行
    private int m;
    // 盘面上有多少列
    private int n;
    private String word;
    private char[][] board;

    public boolean exist(char[][] board, String word) {
        m = board.length;
        if (m == 0) {
            return false;
        }
        n = board[0].length;
        marked = new boolean[m][n];
        this.word = word;
        this.board = board;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }
    private boolean dfs(int i, int j, int start) {
        if (start == word.length() - 1) {
            //返回最后一位是否相等
            return board[i][j] == word.charAt(start);
        }
        if (board[i][j] == word.charAt(start)) {
            marked[i][j] = true;
            for (int k = 0; k < 4; k++) {
                int newX = i + direction[k][0];
                int newY = j + direction[k][1];
                if (inArea(newX, newY) && !marked[newX][newY]) {
                    if (dfs(newX, newY, start + 1)) {
                        return true;
                    }
                }
            }
            marked[i][j] = false;
        }
        return false;
    }
    private boolean inArea(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
    public static void main(String[] args) {
        char[][] board =
                {
                        {'A', 'B', 'C', 'E'},
                        {'S', 'F', 'C', 'S'},
                        {'A', 'D', 'E', 'E'}
                };
        String word = "ABCCED";
        Solution solution = new Solution();
        boolean exist = solution.exist(board, word);
        System.out.println(exist);
    }
}

36.给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。


以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]

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

我们维护一个栈。一开始,我们把-1(有深意的请看代码) 放进栈的顶部来表示开始。初始化时,按照从左到右的顺序,我们不断将柱子的序号放进栈中,直到遇到相邻柱子呈下降关系,也就是a[i−1]>a[i]。现在,我们开始将栈中的序号弹出,直到遇到 stack.peek()满足a[stack.peek()] <a[i]。每次我们弹出序号时,我们用弹出序号在数组中表示的值作为最大面积高,宽是当前走到的序号stack[top-1],从栈顶开始数的第二个之间的那些柱子。也就是当我们弹出 stack[top] 时,记当前走到的元素在原数组中的下标为 i ,当前弹出元素为高的最大矩形面积为:
(i-stack[top-1]-1) *height[stack[top]]
更进一步,当我们到达数组的尾部时,我们将栈中剩余元素全部弹出栈。
此时每个元素代表的高度都成递增的每弹出每一个元素,我们用下面的式子来求面积:(height.length-stack[top-1]-1) *height[stack[top]],其中,stack[top]表示刚刚被弹出的元素知道遇到弹出的元素为-1停止。因此,我们可以通过每次比较新计算的矩形面积来获得最大的矩形面积

package wg;
import java.util.Stack;
public class Solution {
    public int largestRectangleArea(int[] heights){
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        //这个-1有深意的 被当作+1用的 因为数组下标从0开始  7和5之间的举例是6的宽度 所以是7-5-1 但是2和0之间的举例是0 1  如果是 2-0-1就成了1 所以-1当作+1
        int len = heights.length;
        int area = 0;
        for(int i=0;i<len;i++){
            //满足弹栈的要求
            while(stack.peek()!=-1&&heights[stack.peek()]>=heights[i]){
                area = Math.max(area,heights[stack.pop()]*(i-stack.peek()-1));
            }
            stack.push(i);
        }
        while (stack.peek()!=-1){
            area=Math.max(area,heights[stack.pop()]*(len-stack.peek()-1));
        }
        return area;
    }
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.largestRectangleArea(new int[]{2,1,5,6,2,3}));
//10
    }
}

37.给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

输入:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
输出: 6

化成橙色部分,完全是上一题

注意求高度的时候,以每一行为基准,找到这个每一行的最大高度,如果是以0为底,则为0,有0就可以断了。

package wg;
import java.util.Stack;
public class Solution {
    public int largestRectangleArea(int[] heights){
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        //这个-1有深意的 被当作+1用的 因为数组下标从0开始  7和5之间的举例是6的宽度 所以是7-5-1 但是2和0之间的举例是0 1  如果是 2-0-1就成了1 所以-1当作+1
        int len = heights.length;
        int area = 0;
        for(int i=0;i<len;i++){
            //满足弹栈的要求
            while(stack.peek()!=-1&&heights[stack.peek()]>=heights[i]){
                area = Math.max(area,heights[stack.pop()]*(i-stack.peek()-1));
            }
            stack.push(i);
        }
        while (stack.peek()!=-1){
            area=Math.max(area,heights[stack.pop()]*(len-stack.peek()-1));
        }
        return area;
    }
    public int maximalRectangle(char[][] matrix) {
        int m = matrix.length;
        if(m==0)
            return 0;
        int n = matrix[0].length;
        int[] heights = new int[n];//整数类型数组的默认值为0
        int res = 0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]=='1'){
                    heights[j]+=1;
                }else{
                    heights[j]=0;
                }
            }
            res = Math.max(res,largestRectangleArea(heights));
        }
        return res;
    }
    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.maximalRectangle(new char[][]{{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}}));
//6
    }
}

38.给定一个二叉树,返回它的中序 遍历。

输入: [1,null,2,3]
   1
    \
     2
    /
   3
输出: [1,3,2]

递归遍历就不说了。
基于栈的中序遍历

//递归
      public class TreeNode {
          int val;
          TreeNode left;
          TreeNode right;
          TreeNode(int x) { val = x; }
          TreeNode(){}
     }
     private List<Integer> res = new ArrayList<>();
    public List<Integer> inorderTraversal(TreeNode root) {
        if(root==null){
            return  res;
        }
        else{
            helper(root,res);
        }
        return res;
    }
    public void helper(TreeNode treeNode,List<Integer> list){
        if(treeNode!=null){
            if(treeNode.left!=null){
                helper(treeNode.left,list);
            }
            list.add(treeNode.val);
            if(treeNode.right!=null){
                helper(treeNode.right,list);
            }
        }
    }

基于栈的中序遍历

 public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            TreeNode cur = root;
            while (cur != null || !stack.isEmpty()) {
                if (cur != null) {
                    stack.push(cur);
                    cur = cur.left;
                } else {
                    cur = stack.pop();
                    list.add(cur.val);
                    cur = cur.right;
                }
            }
            return list;
        }

39.给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

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

动态规划
假设n个节点存在二叉排序树的个数是G(n),令f(i)为以i为根的二叉搜索树的个数
即有:G(n) = f(1) + f(2) + f(3) + f(4) + ... + f(n)
n为节点总数,当i为根节点时,其左子树节点个数为[1,2,3,...,i-1],右子树节点个数为[i+1,i+2,...n],所以当i为根节点时,其左子树节点个数为i-1个,右子树节点为n-i,即f(i) = G(i-1)*G(n-i),
上面两式可得:G(n) = G(0)*G(n-1)+G(1)*(n-2)+...+G(n-1)*G(0)

public static int numTrees(int n) {
        int[] dp = new int[n+2];//这里注意了 +2防止dp[2]=2;越界
        dp[0]=1;//这里注意了
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    }

40.给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

中序遍历二叉树,遍历结果如果按照从小到大的顺序排列,则表明是二叉搜索树,否则不是二叉搜索树。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
       private double small = - Double.MAX_VALUE;//这样子更小 int 会出错
    public boolean isValidBST(TreeNode root) {
        Stack<TreeNode> stack = new Stack();
        while (!stack.isEmpty() || root != null) {
            while (root != null) {
                stack.push(root);//栈是先进后出哦
                root = root.left;
            }
            //此时已经到达最最最左边的节点
            root = stack.pop();
            if (root.val <= small) return false;
            small = root.val;
            root = root.right;//再换右边
        }
        return true;
    }
}

文章参考
https://leetcode-cn.com/problemset/hot-100/

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,648评论 0 13
  • 一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要使用到,那...
    24K男阅读 6,726评论 5 14
  • 介绍 二叉树的结构 二叉树常考的原因有如下几点1、它可以结合链表、栈、队列和字符串等数据结构出题2、需要熟练掌握图...
    雨住多一横阅读 439评论 0 1
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,729评论 0 38
  • 了解孩子的比较策略 比较策略就是看孩子的关注点是求同的还是求异的。家长可以通过问孩子,你发现了什么,它...
    在水一方_y阅读 76评论 0 1