leetcode总结

leetcode分类

1. DFS

岛屿与湖水

https://labuladong.gitee.io/algo/1/9/

2.标准回溯问题

面试题 08.07. 无重复字符串的排列组合

面试题 08.08. 有重复字符串的排列组合

面试题 17.22. 单词转换

3. 二分法

有序则二分
面试题 10.05. 稀疏数组搜索

4. 动态规划

面试题 16.17. 连续数列

5. 前缀树

面试题 04.12. 求和路径

6. 路径

面试题 08.02. 迷路的机器人

7. 数位中1的个数

题解

233. 数字 1 的个数
数字2的个数

技巧

大小写转化

在这里插入图片描述在这里插入图片描述

原地交换两数

class Solution {
    public int[] swapNumbers(int[] numbers) {
        numbers[1] = numbers[0]^numbers[1];
        numbers[0] = numbers[0]^numbers[1];
        numbers[1] = numbers[0]^numbers[1];
        return numbers;
    }
}

判断是否二次幂


一般有两种做法:方法一的复杂度为 O(logn),方法二为 O(1)。

①第一种是通过不断的相除,看能否除净,由于除法精度问题,一般采用乘法再比较。

boolean check(long x) {
         //方法一
         long cur = 1;
         while (cur < x) {
             cur = cur * 2;
         }
         return cur == x;
}

②与HashMap中静态方法tableSizeFor()类似,找到大于等于此数的第一个2的几次幂,与本数相比较。

java源码:

/**
 * Returns a power of two table size for the given desired capacity.
 * See Hackers Delight, sec 3.2
 */
private static final int tableSizeFor(int c) {
    int n = c - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

判断二次幂代码:

long getVal(long x) {
        long n = x - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return n < 0 ? 1 : n + 1;
}

为什么要x-1?

    为了减少开辟的空间,若原来传进来的是2的幂,能够减少一位的空间。

KMP:最长相等前后缀数组

  1. 找出next矩阵
  2. 匹配两个串
    在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

class Solution {
    // KMP 算法
    // ss: 原串(string)  pp: 匹配串(pattern)
    public int strStr(String ss, String pp) {
        if (pp.isEmpty()) return 0;
        
        // 分别读取原串和匹配串的长度
        int n = ss.length(), m = pp.length();
        // 原串和匹配串前面都加空格,使其下标从 1 开始
        ss = " " + ss;
        pp = " " + pp;

        char[] s = ss.toCharArray();
        char[] p = pp.toCharArray();

        // 构建 next 数组,数组长度为匹配串的长度(next 数组是和匹配串相关的)
        int[] next = new int[m + 1];
        // 构造过程 i = 2,j = 0 开始,i 小于等于匹配串长度 【构造 i 从 2 开始】
        for (int i = 2, j = 0; i <= m; i++) {
            // 匹配不成功的话,j = next(j)
            while (j > 0 && p[i] != p[j + 1]) j = next[j];
            // 匹配成功的话,先让 j++
            if (p[i] == p[j + 1]) j++;
            // 更新 next[i],结束本次循环,i++
            next[i] = j;
        }

        // 匹配过程,i = 1,j = 0 开始,i 小于等于原串长度 【匹配 i 从 1 开始】
        for (int i = 1, j = 0; i <= n; i++) {
            // 匹配不成功 j = next(j)
            while (j > 0 && s[i] != p[j + 1]) j = next[j];
            // 匹配成功的话,先让 j++,结束本次循环后 i++
            if (s[i] == p[j + 1]) j++;
            // 整一段匹配成功,直接返回下标
            if (j == m) return i - m;
        }

        return -1;
    }
}

二叉树DFS与BFS


LeetCode

class Solution {
    Set<Integer> set = new HashSet<>();
    public int findSecondMinimumValue(TreeNode root) {
        dfs(root);
        if (set.size() < 2) return -1;
        int first = Integer.MAX_VALUE, second = Integer.MAX_VALUE;
        for (int i : set) {
            if (i <= first) {
                second = first;
                first = i;
            } else if (i <= second) {
                second = i;
            }
        }
        return second;
    }
    void dfs(TreeNode root) {
        if (root == null) return;
        set.add(root.val);
        dfs(root.left);
        dfs(root.right);
    }
}
class Solution {
    Set<Integer> set = new HashSet<>();
    public int findSecondMinimumValue(TreeNode root) {
        bfs(root);
        if (set.size() < 2) return -1;
        int first = Integer.MAX_VALUE, second = Integer.MAX_VALUE;
        for (int i : set) {
            if (i <= first) {
                second = first;
                first = i;
            } else if (i <= second) {
                second = i;
            }
        }
        return second;
    }
    void bfs(TreeNode root) {
        Deque<TreeNode> d = new ArrayDeque<>();
        d.addLast(root);
        while (!d.isEmpty()) {
            TreeNode poll = d.pollFirst();
            set.add(poll.val);
            if (poll.left != null) d.addLast(poll.left);
            if (poll.right != null) d.addLast(poll.right);
        }
    }
}

二叉树前序中序后序遍历(递归和迭代)

迭代法

  • 前序迭代:我-左-右
    找一个栈,将此根节点入栈,只要这栈不为空,就弹出此节点,然后依次压入其右节点、左节点,由于栈的特殊性,弹出的结果为:左。右。再循环
  • 后续迭代:左右我
    思考。将整个反转,得到:我右左,这是前序的一种,只需将前序的迭代的循环中,压栈顺序改一下,改成先压左再压右,弹出的结果为:右,左。这样产生的结果为:我-右-左,再反转!Collections.reverse(result)
  • 中序迭代:左我右
    依次入栈所有左节点,必须先将当前节点推到最左下方的子节点。随后弹栈,弹出一个记一个,再将当前节点记为当前节点的右节点。
// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right != null){
                stack.push(node.right);
            }
            if (node.left != null){
                stack.push(node.left);
            }
        }
        return result;
    }
}

// 中序遍历顺序: 左-中-右 入栈顺序: 左-右
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        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();
               result.add(cur.val);
               cur = cur.right;
           }
        }
        return result;
    }
}

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.left != null){
                stack.push(node.left);
            }
            if (node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(result);
        return result;
    }
}

递归法

// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
    ArrayList<Integer> preOrderReverse(TreeNode root) {
        ArrayList<Integer> result = new ArrayList<Integer>();
        preOrder(root, result);
        return result;
    }

    void preOrder(TreeNode root, ArrayList<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);           // 注意这一句
        preOrder(root.left, result);
        preOrder(root.right, result);
    }
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);             // 注意这一句
        inorder(root.right, list);
    }
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);             // 注意这一句
    }
}

stream取最大值最小值或者总和

在这里插入图片描述

Arrays.stream(dp).sum();
Arrays.stream(dp).min().getAsInt();

快排

在这里插入图片描述
nlogn

/**
 * 快速排序
 * @param array
 */
public static void quickSort(int[] array) {
    int len;
    if(array == null
            || (len = array.length) == 0
            || len == 1) {
        return ;
    }
    sort(array, 0, len - 1);
}

/**
 * 快排核心算法,递归实现
 * @param array
 * @param left
 * @param right
 */
public static void sort(int[] array, int left, int right) {
    if(left > right) {
        return;
    }
    // base中存放基准数
    int base = array[left];
    int i = left, j = right;
    while(i != j) {
        // 顺序很重要,先从右边开始往左找,直到找到比base值小的数
        while(array[j] >= base && i < j) {
            j--;
        }

        // 再从左往右边找,直到找到比base值大的数
        while(array[i] <= base && i < j) {
            i++;
        }

        // 上面的循环结束表示找到了位置或者(i>=j)了,交换两个数在数组中的位置
        if(i < j) {
            int tmp = array[i];
            array[i] = array[j];
            array[j] = tmp;
        }
    }

    // 将基准数放到中间的位置(基准数归位)
    array[left] = array[i];
    array[i] = base;

    // 递归,继续向基准的左右两边执行和上面同样的操作
    // i的索引处为上面已确定好的基准值的位置,无需再处理
    sort(array, left, i - 1);
    sort(array, i + 1, right);
}

注意顺序很重要,如果先从左往右找就会找到错误结果!

为啥错误:
在这里插入图片描述
每次排序的时候,因为是ij处的元素与base元素进行交换,一定要保障的是我们ij处的元素严格小于base,故我们必须先判断右侧指针!

list转int【】

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
public class Main {
    public static void main(String[] args) {
        int[] data = {4, 5, 3, 6, 2, 5, 1};
 
        // int[] 转 List<Integer>
        List<Integer> list1 = Arrays.stream(data).boxed().collect(Collectors.toList());
        // Arrays.stream(arr) 可以替换成IntStream.of(arr)。
        // 1.使用Arrays.stream将int[]转换成IntStream。
        // 2.使用IntStream中的boxed()装箱。将IntStream转换成Stream<Integer>。
        // 3.使用Stream的collect(),将Stream<T>转换成List<T>,因此正是List<Integer>。
 
        // int[] 转 Integer[]
        Integer[] integers1 = Arrays.stream(data).boxed().toArray(Integer[]::new);
        // 前两步同上,此时是Stream<Integer>。
        // 然后使用Stream的toArray,传入IntFunction<A[]> generator。
        // 这样就可以返回Integer数组。
        // 不然默认是Object[]。
 
        // List<Integer> 转 Integer[]
        Integer[] integers2 = list1.toArray(new Integer[0]);
        //  调用toArray。传入参数T[] a。这种用法是目前推荐的。
        // List<String>转String[]也同理。
 
        // List<Integer> 转 int[]
        int[] arr1 = list1.stream().mapToInt(Integer::valueOf).toArray();
        // 想要转换成int[]类型,就得先转成IntStream。
        // 这里就通过mapToInt()把Stream<Integer>调用Integer::valueOf来转成IntStream
        // 而IntStream中默认toArray()转成int[]。
 
        // Integer[] 转 int[]
        int[] arr2 = Arrays.stream(integers1).mapToInt(Integer::valueOf).toArray();
        // 思路同上。先将Integer[]转成Stream<Integer>,再转成IntStream。
 
        // Integer[] 转 List<Integer>
        List<Integer> list2 = Arrays.asList(integers1);
        // 最简单的方式。String[]转List<String>也同理。
 
        // 同理
        String[] strings1 = {"a", "b", "c"};
        // String[] 转 List<String>
        List<String> list3 = Arrays.asList(strings1);
        // List<String> 转 String[]
        String[] strings2 = list3.toArray(new String[0]);
 
    }
}

二分查找

https://www.cnblogs.com/mxj961116/p/11945444.html

在一个升序序列中找左侧或者右侧终点

class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length;

//找右侧最终点,左闭右开
        while(left<right){
            int mid = left+(right-left)/2;
            if(nums[mid]> target){
                right = mid;
            }else if(nums[mid]<target){
                left = mid+1;
            }else if(nums[mid]==target){
                left = mid+1;
            } 
        }
        int bound1 = left-1;

//找左侧节点头,左闭右开
        left = 0;
        right = nums.length;
        while(left<right){
            int mid = left+(right-left)/2;
            if(nums[mid]> target){
                right = mid;
            }else if(nums[mid]<target){
                left = mid+1;
            }else if(nums[mid]==target){
                right = mid;
            } 
        }
        int bound2 = right;

        return bound1-bound2+1  ;
    }
}

树:DFS

 public static void DFS(TreeNode head){//树的深搜使用回溯
        if (head==null)return;
        check();//满足条件的check();
        if (head.left!=null){
            DFS(head.left);
        }if (head.right!=null){
            DFS(head.right);
        }
    }

树:BFS

 public static void BFS(TreeNode head){//树的宽搜
        if (head==null)return;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(head);
        while (!queue.isEmpty()){
            TreeNode m=queue.poll();
            //System.out.print(m.val+" ");
            //dosomething();
            if (m.left!=null){
                queue.offer(m.left);
            }if (m.right!=null){
                queue.offer(m.right);
            }
            queue.poll();
        }
    }

图算法:DFS

从起始顶点开始,递归访问其所有邻近节点。比如A节点是其第一个邻近节点,而B节点又是A的一个邻近节点,则DFS访问A节点后再访问B节点,如果B节点有未访问的邻近节点的话将继续访问其邻近节点,否则继续访问A的未访问邻近节点,当所有从A节点出去的路径都访问完之后,继续递归访问除A以外未被访问的邻近节点。

	public static void DFS(int v){//图的深搜
        visited[v]=true;
        for (int i = 0; i < a[0].length; i++) {
            if (check()&&visited[i]==false){
                DFS(i);//回溯
            }
        }
	}

图算法:BFS

即由一点逐级往下搜索,以图为例,由顶点v1可以延伸到v2、v3,再由v2可以延伸到v1、v4、v5,被搜索过的顶点不用再次搜索,直到整个图的节点被搜完一遍。

public static void BFS(int v){//图的宽搜
        Queue<Integer> queue=new LinkedList<>();
        Boolean[] visited=new Boolean[a.length*a[0].length];//已被访问节点
        visited[v]=true;
        queue.add(v);
        int []prev=new int[a.length*a[0].length];
        int l=0;
        prev[l]=0;
        while (queue.size()!=0){
            int v2=queue.poll();
            for (int i = 0; i <a[0].length ; i++) {//从v2点开始往下搜
                if (visited[i]==false&&check()){//结点满足条件的check();
                    queue.add(i);
                    prev[++l]=i;
                }
                visited[i]=true;
            }
        }
    }

最短路径

图算法:弗洛伊德算法

lc题目举例:https://leetcode-cn.com/problems/course-schedule-iv/solution/floyed-suan-fa-by-15228207-7r9k/
在这里插入图片描述
简单:就是三重遍历,最外层放中间节点,里面两层为i与j,最中间if判断,如果中继的路径小于加和,就更新。
https://leetcode-cn.com/problems/course-schedule-iv/

图算法:迪杰斯特拉算法

在这里插入图片描述
用于计算从某个源点其他所有节点的最短路径。

图算法:贝尔曼-福特算法

弗洛伊德和迪杰斯特拉算法都不能处理求含负权边的图。

图算法:拓扑排序

内容:【广度优先遍历】+【贪心算法】在有向图中
应用场景:任务调度,一个需要是另一个的前提条件。
作用:输出一个拓扑序+判断是否存在环路

典型题目课程表II:https://leetcode-cn.com/problems/course-schedule-ii/solution/tuo-bu-pai-xu-shen-du-you-xian-bian-li-python-dai-/

在这里插入图片描述

最小生成树

将一个有权图中的 所有顶点 都连接起来,并保证连接的边的 总权重最小,即最小生成树,最小生成树不唯一。
在这里插入图片描述

Prim算法

详解:https://mp.weixin.qq.com/s/bvi0wGdbtB4nkYye0yzmqg
在这里插入图片描述
举例:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package com.company.十种算法.prim;

import java.util.Arrays;

/**
 * Author : zfk
 * Data : 16:37
 * prim算法 - 》 最小生成树
 */
public class PrimAlgorithm {

    //用INF表示两个顶点不能连通
    private static final int INF = 65535;

    public static void main(String[] args) {

        char[] data = new char[]{'A','B','C','D','E','F','G'};
        int verxs = data.length;
        //邻接矩阵
        int[][] weight = new int[][]{
                {INF,5,7,INF,INF,INF,2},
                {5,INF,INF,9,INF,INF,3},
                {7,INF,INF,INF,8,INF,INF},
                {INF,9,INF,INF,INF,4,INF},
                {INF,INF,8,INF,INF,5,4},
                {INF,INF,INF,4,5,INF,6},
                {2,3,INF,INF,4,6,INF}
        };

        //创建MGraph对象
        MGraph mGraph = new MGraph(verxs);
        //创建MinTree
        MinTree minTree = new MinTree();
        minTree.createGraph(mGraph,verxs,data,weight);

        minTree.showGraph(mGraph);
        minTree.prim(mGraph,0);
    }


}

//创建最小生成树
class MinTree{

    /**
     * 创建图的邻接矩阵
     * @param graph 图对象
     * @param verxs 顶点个数
     * @param data 顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph graph,int verxs,char[] data,int[][] weight){

        int i ,j;
        for (i = 0;i < verxs;i++){
            graph.data[i] = data[i];
            for (j = 0;j < verxs;j++){
                graph.weight[i][j] = weight[i][j];
            }
        }

    }

    public void showGraph(MGraph graph){
        for (int[] link : graph.weight){
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 编写prim算法,得到最小生成树
     * @param mGraph 图
     * @param v 生成树的起点
     */
    public void prim(MGraph mGraph,int v){
        //标记已被访问的顶点,初始都为0
        int[] visited = new int[mGraph.verxs];

        //把当前节点标记为已访问
        visited[v] =1;
        //用h1和h2记录两个顶点的下标
        int h1 = -1 , h2 = -1;
        //存储最小权
        int minWeight = 10000;

        for (int k =1;k < mGraph.verxs;k++){
            //i表示访问过的节点,j表示没有访问过的节点
            for (int i = 0;i < mGraph.verxs;i++){
                for (int j =0;j < mGraph.verxs;j++){
                    //当<i,j>的边权值小于最小值,存储两顶点,即找到最小边
                    if (visited[i] == 1 && visited[j] == 0 &&
                            mGraph.weight[i][j] < minWeight){
                        minWeight = mGraph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }

                }
            }
            //找到1条边最小
            System.out.println("边<"+mGraph.data[h1]+","+mGraph.data[h2]+"> 权值:"+minWeight);
            //标记已访问
            visited[h2] =1;
            //重新设置为最大值 10000
            minWeight = 10000;
        }
    }

}


//图
class MGraph{
    //结点个数
    int verxs;
    //存放结点数据
    char[] data;
    //邻接矩阵:存放边
    int[][] weight;

    public MGraph(int verxs){
        this.verxs = verxs;
        data = new char[verxs];
        weight = new int[verxs][verxs];
    }

}

kruskal算法

在这里插入图片描述
可以设置一个终点集合ends(终点为最大的顶点,<A,G>的终点为G),最小生成树每加入一个边,就将树中的每个顶点在最小生成树中的终点更新,如果加入的两个顶点相同,说明构成了回路。

大顶堆小顶堆

第k大的数,小顶堆(堆内都是大于堆顶的数,依次放入k个,如果此数小于堆顶,一定不能是第k大的数)

第k小的数,大顶堆(堆顶是最大数,存小值k个,如果此数大于堆顶,一定不能是第k小的数)
在这里插入图片描述

ACM模式IO

https://blog.csdn.net/qq_42403042/article/details/107533785

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {

//        int[] num;
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        String sr = bf.readLine();
        int num[] = new int[Integer.parseInt(sr)];

        while (bf.ready()) {
            String[] str = bf.readLine().split(" ");
            num = new int[str.length];
            for (int i = 0; i < num.length; i++) {
                num[i] = Integer.parseInt(str[i]);
            }
            System.out.println(Arrays.toString(num));
        }
//写代码逻辑

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值