力扣刷题日常(二)

一些难用的数据结构…

一瓶酒一支烟一道力扣敲一天…

目录

  1. 149.直线上最多的数
  2. 752.打开转盘锁——广度优先搜索
  3. 815.公交路线——优化建图+广度优先搜索
  4. 剑指 Offer 37. 序列化二叉树——DFS,二叉树序列化和反序列化
  5. LCP 07.传递信息——有向图,DFS,BFS,DP
  6. 726.原子的数量——哈希表+栈

2.1 直线上最多的数

题目描述
给你一个数组 points ,其中 points[i] = [xi , yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。
输入
points = [[1,1],[2,2],[3,3]]
输出
3

解题思路
就是暴力穷举嘛,枚举经过点i和另外一个点的所有直线。如果直线同时经过另外两个不同的点j和k,如果 k k k表示斜率,那么: k i j = k i k k_{ij} = k_{ik} kij=kik
统计所有除i点外的点和点i之间的斜率,出现次数最多的斜率就是经过点最多的直线,经过的点数就是次数+1。

记录斜率

  1. 如果只是单纯 Δ x Δ y \frac{\Delta x}{\Delta y} ΔyΔx,可能因为精度不够而无法足够精确地表示每一个斜率。所以可以用一个二元组来表示斜率 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)
  2. 考虑如: 1 2 = 2 4 \frac{1}{2}=\frac{2}{4} 21=42,这样的情况,我们可以把 Δ x , Δ y \Delta x,\Delta y Δx,Δy除一个他们的最大公约数 g c d ( ∣ Δ x ∣ , ∣ Δ y ∣ ) gcd(|\Delta x|,|\Delta y|) gcd(Δx,Δy)那么就会得到另一个二元组: ( m x , m y ) = ( Δ x g c d ( ∣ Δ x ∣ , ∣ Δ y ∣ ) , Δ y g c d ( ∣ Δ x ∣ , ∣ Δ y ∣ ) ) (mx,my)=(\frac{\Delta x}{gcd(|\Delta x|,|\Delta y|)},\frac{\Delta y}{gcd(|\Delta x|,|\Delta y|)}) (mx,my)=(gcd(Δx,Δy)Δx,gcd(Δx,Δy)Δy)
  3. 考虑如: 1 − 2 = − 1 2 \frac{1}{-2}=\frac{-1}{2} 21=21这种情况,所以我们可以规定分子必须非负。如果 m x < 0 mx<0 mx<0 m x = − m x , m y = − m y mx=-mx,my=-my mx=mx,my=my
  4. 考虑直线是垂直的和水平的情况,即: m x = 0 mx=0 mx=0时我们设 m y = 1 my=1 my=1 m y = 0 my=0 my=0时我们设 m x = 1 mx=1 mx=1
  5. 在题解里面他是直接 v a l = m y + m x ∗ 20001 val=my+mx*20001 val=my+mx20001,通过比较val来判定斜率是否一致的。这种方法我想不到我也不能理解,但是最后是看题解才敲出来的代码所以…
  6. 这里还有一种思路,那就是把mx,my直接打造成一个字符串"mx_my"通过比较字符串来衡量斜率是否一致。

一些优化

  1. 在点的总数量小于等于2的情况下,我们总可以用一条直线将所有点串联,此时我们直接返回点的总数量即可;
  2. 其实只要考虑下标大于i的,因为i之前的点和i的关系已经被考虑过了
  3. 当我们找到一条直线经过了图中超过半数的点时,我们即可以确定该直线即为经过最多点的直线;
  4. 当我们枚举到点 i(假设编号从 0开始)时,我们至多只能找到n-i个点共线。假设此前找到的共线的点的数量的最大值为k,如果有 k≥n−i,那么此时我们即可停止枚举,因为不可能再找到更大的答案了。

代码

class Solution {
    public int maxPoints(int[][] points) {
        int n = points.length;
        if(n <=2)//优化1
        {
            return n;
        }
        int maxCount = 0;
        for(int i=0;i<n;i++){
            if(maxCount>=n-i||maxCount>n/2){//优化3,4
                break;
            }
            HashMap<Integer,Float> slope = new HashMap<Integer,Float>();
            for(int j=i+1;j<n;j++){//优化2
                slope.put(j,slopecompute(points[i],points[j]));//算i点与其余各点的斜率
            }
            int maxCounttemp = 0;
            HashMap<Float,Integer> slopecount = new HashMap<Float,Integer>();
            for(Map.Entry<Integer,Float> Entry:slope.entrySet()){//遍历键值对
                if(slopecount.containsKey(Entry.getValue())){
                    slopecount.put(Entry.getValue(),slopecount.get(Entry.getValue())+1);//slopecount的key是slope的value,value是slope中相同value的个数
                }
                else{
                    slopecount.put(Entry.getValue(),1);
                }
            }
            for(Integer nums:slopecount.values()){
                maxCounttemp = Math.max(nums,maxCounttemp);
            }
            maxCount = Math.max(maxCount,maxCounttemp+1);
        }
        return maxCount;

    }
    public float slopecompute(int[] a,int[] b){
        int deltx = a[0]-b[0];
        int delty = a[1] - b[1];
        float slope;
        if(deltx==0){//考虑4.中的情况
            delty = 1;
        }
        else if(delty==0){
            deltx = 1;
        }
        else{
            if(delty<0){//考虑3.中说的情况
                deltx = -deltx;
                delty = -delty;
            }
        }
        //求a,b的最大公约数
        int gcd = gcd(Math.abs(deltx),Math.abs(delty));
        float mx = deltx/gcd;
        //考虑2.中的情况,把deltx,delty化成最简形式。if deltx = 6,delty=9.则mx=2,my=3
        float my = delty/gcd;
        slope = my+mx*20001;
        return slope;

    }
    public int gcd(int a,int b){
        return b!=0?gcd(b,a%b):a;
    }
}

2.2 打开转盘锁

你好意思说这是中等题?
题目描述
你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’ 。每个拨轮可以自由旋转:例如把 ‘9’ 变为 ‘0’,‘0’ 变为 ‘9’ 。每次旋转都只能旋转一个拨轮的一位数字。

锁的初始数字为 ‘0000’ ,一个代表四个拨轮的数字的字符串。

列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。

字符串 target 代表可以解锁的数字,你需要给出解锁需要的最小旋转次数,如果无论如何不能解锁,返回 -1 。
输入

deadends = ["0201","0101","0102","1212","2002"], target = "0202"

输出
6
解释
可能的移动序列为 “0000” -> “1000” -> “1100” -> “1200” -> “1201” -> “1202” -> “0202”。
注意 “0000” -> “0001” -> “0002” -> “0102” -> “0202” 这样的序列是不能解锁的,
因为当拨动到 “0102” 时这个锁就会被锁定。

解题思路
广度优先搜索
将 (0000, 0)加入队列,设当前搜索到的数字为status,旋转次数为step,枚举status通过一次旋转得到的所有数字。判断这些数字中有没有target,如果没有就判断这些数字有没有在deadends里面,把不在deadends里的这些数字都加到队列中,且step+1,如果搜索到了target就返回step。

为了方便判断可以用哈希集存储deadends中的所有元素,同时用一个哈希集存储搜索到的所有状态,避免重复搜索。

如果搜到最后都没有搜到,说明无法解锁返回-1。
一些小细节

  1. 如果target在deadends中那么直接返回-1;
  2. 如果target是0000直接返回0;
  3. 如果0000在deadends里面直接返回-1。

代码

class Solution {
    public int openLock(String[] deadends, String target) {
    //一些小细节考虑
        if(target.equals("0000")){
            return 0;
        }
        if(Arrays.asList(deadends).contains(target)||Arrays.asList(deadends).contains("0000")){
            return -1;
        }
        Set<String> deadmap = new HashSet<String>();//存储deadends
        for(int i=0;i<deadends.length;i++){
            deadmap.add(deadends[i]);
        }
        Set<String> status = new HashSet<String>();//存储所有得到的状态
        Queue<String> queue = new LinkedList<String>();//队列存储状态的变化
        queue.offer("0000");
        status.add("0000");
        int step = 0;
        //枚举当前状态下转一下得到的所有状态
        while(!queue.isEmpty()){
            step++;
            int size =  queue.size();
            for(int i=0;i<size;i++){
                String st = queue.poll();//取出元素
                List<String> allst = new ArrayList<String>();
                char[] charst = st.toCharArray();
                //枚举st转一下得到的所有状态
                for(int j=0;j<4;j++){
                    //char[] newStBackward = charst; 不能这么搞!charst也会变的!
                    char[] newStBackward = (char[]) charst.clone();
                    //向后转动一次得到的所有数字
                    if(newStBackward[j]=='0'){
                        newStBackward[j]='9';
                    }
                    else{
                        newStBackward[j] = (char)(newStBackward[j]-1);
                    }
                    //allst.add(newStBackward.toString());也不能这么搞!会转成一个地址的!
                    allst.add(String.valueOf(newStBackward));
                    //向前转动一次得到的数字
                    char[] newStForward = (char[]) charst.clone();
                    if(newStForward[j]=='9'){
                        newStForward[j]='0';
                    }
                    else{
                        newStForward[j] = (char)(newStForward[j]+1);
                    }
                    allst.add(String.valueOf(newStForward));//为了避免重复搜索

                }
                //查看所有状态里有没有target
                for(String nextstr:allst){
                    if(!status.contains(nextstr)&&!deadmap.contains(nextstr)){
                        if(nextstr.equals(target)){//别用==!!!!
                            return step;
                        }
                        queue.offer(nextstr);
                        status.add(nextstr);
                    }
                }
            }

        }
        return -1;


    }
}

一些小错误

char[] charst = {'a','b','c','d'}
//关于字符数组复制
char[] newSt= charst;//这么搞newSt改变charst也会变的
//正确打开方式
char[] newSt=Arrays.copyOf(charst, charst.length);
char[] newSt = (char[]) charst.clone();

//关于字符想加一
newStForward[j] = newStForward[j]++;//没用不行
//正确打开方式
newStForward[j] = (char)(newStForward[j]+1);

2.3 815. 公交路线

为啥我每天的力扣题都这么难啊…
题目描述
给你一个数组 routes ,表示一系列公交线路,其中每个 routes[i] 表示一条公交线路,第 i 辆公交车将会在上面循环行驶。
例如,路线 routes[0] = [1, 5, 7] 表示第 0 辆公交车会一直按序列 1 -> 5 -> 7 -> 1 -> 5 -> 7 -> 1 -> … 这样的车站路线行驶。
现在从 source 车站出发(初始时不在公交车上),要前往 target 车站。 期间仅可乘坐公交车。
求出最少乘坐的公交车数量 。如果不可能到达终点车站,返回 -1 。
输入
routes = [[1,2,7],[3,6,7]], source = 1, target = 6
输出
2
解释
最优策略是先乘坐第一辆公交车到达车站 7 , 然后换乘第二辆公交车到车站 6 。
解题思路
优化构图+广度优先搜索
以每个站点为一个节点,如果站点之间有公交可达到就加一条长度为1的边。这样就能看出每个节点通过搭一辆公交所能到的所有站点,存储这些站点然后遍历,继续搜索每个站点通过只搭乘一辆公交所能到达的站点,如果站点中有target就返回所乘公交数,没有就继续搜索。(看代码吧容易明白一些)
代码

class Solution {
    public int numBusesToDestination(int[][] routes, int source, int target) {
        if(source==target){
            return 0;
        }
        //记录每一站station,有哪些bus经过它
        HashMap<Integer,List<Integer>> stnumsbus = new HashMap<>();
        for(int i=0;i<routes.length;i++){
            for(int station:routes[i]){
                int b = i;
                if(!stnumsbus.containsKey(station)){
                    stnumsbus.put(station,new ArrayList(){
                        {add(b);}
                    });
                }
                else{
                    stnumsbus.get(station).add(i);
                }
            }
        }
        //记录已经坐了哪些公交车
        boolean[] memorybus = new boolean[routes.length]; 
        Queue<Integer> q = new LinkedList<>();//记录每次poll出的车站所能辐射到的所有车站
        q.offer(source);//先把起始点加到队列中
        int count = 0;//记录坐的公交车数
        //bfs
        while(!q.isEmpty()){
            int size = q.size();
            count++;
            while(size-->0){
               int sitenow = q.poll();//当前所在车站
               //遍历当前所在车站经过的bus
                for(int bus : stnumsbus.get(sitenow)){
                    if(memorybus[bus]){//如果这bus坐过了就继续循环
                        continue;
                    }
                    memorybus[bus] = true;//标记该公交车已经坐过
                    for(int s:routes[bus]){//遍历该公交车经过的站
                        if(s==target){//如果能直接到目标站就返回count
                            return count;
                        }
                        if(s==sitenow){
                            continue;
                        }
                        q.offer(s);//否则就把能坐到的站加入队列
                    }
                } 
            }
        }
        return -1;


    }
}

2.4 剑指 Offer 37. 序列化二叉树

我是个菜鸡,这都不会QAQ
题目描述
请实现两个函数,分别用来序列化和反序列化二叉树。所谓序列化,就是给定一个二叉树,依次输出二叉树节点值,也就是层序遍历。反序列化就是根据一串数值初始化一个二叉树,是序列化的逆过程。
输入
root = [1,2,3,null,null,4,5]
输出
[1,2,3,null,null,4,5]
解释
LeetCode调用方式是:

Codec codec = new Codec();
codec.deserialize(codec.serialize(root));

解题思路和代码

  • 序列化
    使用层序遍历实现,有两种实现方式,递归法和非递归法。
  • 递归法
    先序遍历二叉树,遇到空子树的时候序列化成 None,否则继续递归序列化,先递归左子树再递归右子树。
    public String serialize(TreeNode root) {
        if(root==null) {
            res+="null,";
        }
        else{
            res += root.val;
            res+=",";
            serialize(root.left);
            serialize(root.right);
        }
        return res;
    }
  • 非递归法
    现将root加入队列,并开始层序遍历,让一个节点出队,打印value,再将他的左右子节点加入队列。如果该节点为空,打印“null"。
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        if(root==null) return "[]";// 若 root 为空,则直接返回空列表 "[]" 
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        String res = "[";
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node!=null){
                res += node.val+",";
                queue.offer(node.left);
                queue.offer(node.right);
            }
            else{
                res+="null,";
            }
        }
        res.substring(0,res.length()-1);//结尾会多一个,
        res += "]";
        return res;
    }
  • 反序列化
  • 递归法
    根据”,“把原先的序列分割开来得到先序遍历的元素列表,然后从左向右遍历这个序列:如果当前的元素为 None,则当前为空树;否则先解析这棵树的左子树,再解析它的右子树。
// Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        String[] split = data.split(",");
        System.out.print(Arrays.toString(split));
        List<String> datalist = new LinkedList<String>(Arrays.asList(split));//string[]转list
        return deserialized(datalist);

    }
    public TreeNode deserialized(List<String> datalist){
        if(datalist.size()==0){
            return null;
        }
        if(datalist.get(0).equals("null")){
            datalist.remove(0);
            return null;
        }
        TreeNode root = new TreeNode(Integer.valueOf(datalist.get(0)));
        datalist.remove(0);
        root.left = deserialized(datalist);
        root.right = deserialized(datalist);
        return root;
    }
  • 非递归法
    也是用队列按层构建二叉树。先用字符串第一个数字构建root,把root压入队列。然后循环遍历,从队列中取出一个节点node,以此构建他的左节点和右节点。(如果对应的value不为空),然后再把左节点和右节点压入队列。当队列为空时退出循环返回root。
    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
        if(data.length()==0||data.equals("[]")){
            return null;
        }
        String[] splitdata = data.substring(1,data.length()-1).split(",");
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        TreeNode root = new TreeNode(Integer.valueOf(splitdata[0]));
        queue.offer(root);
        int i = 1;
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            if(node!=null){
                if(!splitdata[i].equals("null")){
                    node.left = new TreeNode(Integer.valueOf(splitdata[i]));
                    queue.offer(node.left);
                    i++;
                }
                else{
                    i++;
                }
                if(!splitdata[i].equals("null")){
                    node.right = new TreeNode(Integer.valueOf(splitdata[i]));
                    queue.offer(node.right);
                    i++;
                }
                else{
                    i++;
                }
            }
            else{
                i++;
            }
        }
        return root;

    }

2.5 传递信息

题目描述
小朋友 A 在和 ta 的小伙伴们玩传信息游戏,游戏规则如下:

  • 有 n 名玩家,所有玩家编号分别为 0 ~ n-1,其中小朋友 A 的编号为 0
  • 每个玩家都有固定的若干个可传信息的其他玩家(也可能没有)。传信息的关系是单向的(比如 A 可以向 B 传信息,但 B 不能向 A 传信息)。
  • 每轮信息必须需要传递给另一个人,且信息可重复经过同一个人
    给定总玩家数 n,以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数;若不能到达,返回 0。
    输入
    n = 5, relation = [[0,2],[2,1],[3,4],[2,3],[1,4],[2,0],[0,4]], k = 3
    输出
    3
    解释
    信息从小 A 编号 0 处开始,经 3 轮传递,到达编号 4。共有 3 种方案,分别是 0->2->0->4, 0->2->1->4, 0->2->3->4。
    解题思路
  1. DFS
    使用深度优先搜索计算方案数。从节点 0 出发做深度优先搜索,每一步记录当前所在的节点以及经过的轮数,当经过 k轮时,如果位于节点 n−1,则将方案数加 1。搜索结束之后,即可得到总的方案数。
    定义一个List<List>存储每一条连通路径,然后使用递归or循环,寻找终点是n-1,且只经过k个轮次的路径。
  2. BFS
    从节点 0出发做广度优先搜索,当遍历到 k层时,如果位于节点 n−1,则将方案数加 1。搜索结束之后,即可得到总的方案数。
  3. DP
    从编号0玩家经过k轮次到达编号n-1玩家的路径数等于编号i玩家k-1轮次到达编号n-1玩家的路径数之和,这里存在0-i的路径。由此联想DP。
    写递推公式: d p [ i ] [ j ] dp[i][j] dp[i][j] 是从i到n-1经过j轮可以到达的路径数。就有:
    d p [ i ] [ 0 ] = 0 , d p [ n − 1 ] [ j ] = 0 , d p [ i ] [ 1 ] = 和 n − 1 连 通 的 所 以 路 径 数 dp[i][0] = 0,dp[n-1][j] = 0,dp[i][1]=和n-1连通的所以路径数 dp[i][0]=0,dp[n1][j]=0,dp[i][1]=n1
    对于路径 [ i , d s t ] , d p [ i ] [ j ] = ∑ [ i , d s t ] ∈ r e l e a t i o n d p [ d s t ] [ j − 1 ] [i,dst],\quad dp[i][j] = \sum_{[i,dst] \in releation} dp[dst][j-1] [i,dst],dp[i][j]=[i,dst]releationdp[dst][j1]

代码

class Solution {
    int n,k;
    int ways = 0;
    List<List<Integer>> edge = new ArrayList<List<Integer>>();
    public int numWays(int n, int[][] relation, int k) {
        //DFS 会超时
        // this.n = n;
        // this.k = k;
        // for(int i = 0;i<n;i++){
        //     edge.add(new ArrayList<Integer>());
        // }
        // for(int[] e:relation){
        //     edge.get(e[0]).add(e[1]);
        // }
        // int step = 0;
        // dfs(0,0);
        // return ways;

        //BFS
        // List<List<Integer>> edge = new ArrayList<List<Integer>>();
        // for(int i = 0;i<n;i++){
        //     edge.add(new ArrayList<Integer>());
        // }
        // for(int[] e:relation){
        //     edge.get(e[0]).add(e[1]);
        // }
        // Queue<Integer> queue = new LinkedList<Integer>();
        // queue.offer(0);
        // int ways = 0;
        // int step = 0;
        // while(!queue.isEmpty()&&step < k){
        //     step++;
        //     int size = queue.size();//一定要在循环之前定义出来!不然循环过程中队列会变长度的!
        //     for(int i = 0;i<size;i++){
        //         int node = queue.poll();
        //         List<Integer> e = edge.get(node);
        //         for(int edgenodes:e){
        //             queue.offer(edgenodes);
        //         }  
        //     }
            
        // }
        // if(step==k){
        //     while(!queue.isEmpty()){
        //         if(queue.poll()==n-1){
        //             ways++;
        //         }
        //     }
        // }
        // return ways;


        //DP
        // int  temp = 0;
        // int count = 0;
        // //Set<List<Integer>> route = new HashSet<List<Integer>>();
        // for(int i=0;i<relation.length;i++){
        //     if(relation[i][0]==0){
        //         count = routecount(0,n,relation,k);
        //     }
        // }
        // return count;

    }
    public int routecount(int begin,int n,int[][] relation,int k){
        int count = 0;
        if(k==1){
            for(int i=0;i<relation.length;i++){
                if(relation[i][0]==begin&&relation[i][1]==n-1){
                    count++;
                }
            }
            return count; 
        }
        for(int i=0;i<relation.length;i++){
            if(relation[i][0]==begin){
                count += routecount(relation[i][1],n,relation,k-1);
            }
        }
        return count;
        
        
    }
    public void dfs(int index,int step){
        if(step==k){
            if(index==n-1){
                ways++;
            }
        }
        List<Integer> list = edge.get(index);
        for(int nextindex:list){
            dfs(nextindex,step+1);
        }
    }
}

2.6 726. 原子的数量

力扣可能想难死我…
题目描述
给定一个化学式formula(作为字符串),返回每种原子的数量。

原子总是以一个大写字母开始,接着跟随0个或任意个小写字母,表示原子的名字。

如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。例如,H2O 和 H2O2 是可行的,但 H1O2 这个表达是不可行的。

两个化学式连在一起是新的化学式。例如 H2O2He3Mg4 也是化学式。

一个括号中的化学式和数字(可选择性添加)也是化学式。例如 (H2O2) 和 (H2O2)3 是化学式。

给定一个化学式 formula ,返回所有原子的数量。格式为:第一个(按字典序)原子的名字,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。
输入
formula = “H2O”
输出
“H2O”
解释
原子的数量是 {‘H’: 2, ‘O’: 1}。
解题思路
用栈和哈希表。哈希表用来存储原子以及对应的数量。

  • 如果碰到’('那么就在栈里面压入一个新的哈希表,进入下一层
  • 如果再碰到’)'那么就弹出栈顶哈希表,遍历里面的键值对,更新到上一层哈希表里面。注意如果括号后面有数字要读取数字并乘到这一层对应的原子数上。
  • 如果碰到的是字母或者数字,则读取原子名以及对应的数量,并加到栈顶的哈希表中
  • 要注意数字可能不止一位数,字母可能不止一位字母!
    具体的看官方题解吧,这个属于一看到就自动放弃型…

代码

public class countforAtom {
    int n = 0;
    int i = 0;
    public String countOfAtoms(String formula) {
        this.n = formula.length();
        Stack<HashMap> stack = new Stack<HashMap>();
        stack.push(new HashMap<String,Integer>());
        int nums = 0;
        char value = ' ';
        while(i<n){
            value = formula.charAt(i);
            if(value=='('){
                i++;
                stack.push(new HashMap<String,Integer>());//碰到( 说明上一层的以及遍历完了,要进入下一层也就是读取括号里面的原子数
            }
            else if(value==')'){
                i++;
                nums = getNums(formula);//找到)后面的数字
                HashMap<String,Integer> topmap = stack.pop();//取出并且删掉
                HashMap<String,Integer> map = stack.peek();//取出不删掉
                for(Map.Entry<String,Integer> entry:topmap.entrySet()){//把这一层的原子数信息更新到上一层
                    String autom = entry.getKey();
                    int automnum = entry.getValue();
                    map.put(autom,map.getOrDefault(autom,0)+automnum*nums);//在原始基础上加上括号里面的原子数
                }
            }
            else{
                String autom = getAtom(formula);//得到原子全称
                int num = getNums(formula);//统计原子数量
                HashMap<String,Integer> map = stack.peek();//修改栈顶哈希表
                map.put(autom,map.getOrDefault(autom,0)+num);
            }
        }
        HashMap<String,Integer> res = stack.pop();//最终结果排序
        //哈希表排序
//        TreeMap<String,Integer> treeMap = new TreeMap<String,Integer>(res);
//        StringBuffer sb =  new StringBuffer();
//        for(Map.Entry<String,Integer> entry:treeMap.entrySet()){
//            String atom = entry.getKey();
//            int count = entry.getValue();
//            sb.append(atom);
//            if(count>1){
//                sb.append(count);
//            }
//        }
        StringBuffer sb =  new StringBuffer();
        Set set=res.keySet();
        Object[] arr=set.toArray();
        Arrays.sort(arr);
        for(Object key:arr){
            sb.append(key);
            int count = res.get(key);
            if(count>1){
                sb.append(count);
            }
        }

        return sb.toString();
    }
    public String getAtom(String formula){
        StringBuffer sb = new StringBuffer();
        if(!Character.isLowerCase(formula.charAt(i))){
            sb.append(formula.charAt(i));
            i++;
        }
        while (i<n&&Character.isLowerCase(formula.charAt(i))){//扫描首字母后面所有小写字母
            sb.append(formula.charAt(i));
            i++;
        }

        return sb.toString();
    }
    public int getNums(String formula){
        //如果i是字符串末尾或者不是数字
        if(i==n||!Character.isDigit(formula.charAt(i))){
            return 1;
        }
        int num = 0;
        //数字可能不止一位
        while(i<n&&Character.isDigit(formula.charAt(i))){//扫描数字后面的所有数字
            num = num*10 + formula.charAt(i)-'0';
            i++;
        }
        return num;
    }
}

Tips

1. 关于HashMap

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
声明

 HashMap<Integer, String> Sites = new HashMap<Integer, String>();

增删改查

// 添加键值对
 Sites.put(1, "Google");
//访问元素,通过key
Sites.get(3);
//删除元素
Sites.remove(4);
//删除所有
 Sites.clear();
 //计算大小
 Sites.size()
 //修改
 Sites.put(Sites.get(3),"new");
 

迭代

// 输出 key 和 value
        for (Integer i : Sites.keySet()) {
            System.out.println("key: " + i + " value: " + Sites.get(i));
        }
        // 返回所有 value 值
        for(String value: Sites.values()) {
          // 输出每一个value
          System.out.print(value + ", ");
        }

还有一种迭代方式,我写在2.1的代码里了。用EntrySet的。
关于怎么统计HashMap中相同value出现次数
这个在2.1代码里面也有体现。就是重新创建一个HashMap,key是要统计的HashMap的value值,value是要统计的HashMapz中相同value出现的次数。这个思路也能用在统计List中出现相同value的次数

 HashMap<Float,Integer> slopecount = new HashMap<Float,Integer>();
            for(Map.Entry<Integer,Float> Entry:slope.entrySet()){//遍历slope键值对
                if(slopecount.containsKey(Entry.getValue())){
                    slopecount.put(Entry.getValue(),slopecount.get(Entry.getValue())+1);//slopecount的key是slope的value,value是slope中相同value的个数
                }
                else{
                    slopecount.put(Entry.getValue(),1);
                }
            }

哈希表按Key排序

        Set set=res.keySet();//res是HashMap类型
        Object[] arr=set.toArray();
        Arrays.sort(arr);
        for(Object key:arr){
            System.out.print("key:"+key+", value:"+res.get(key));
        }

2. 队列

队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。
声明

Queue<String> queue = new LinkedList<String>();

增删改查
这里add()和remove()方法失败时会抛异常不推荐。

//添加元素
queue.offer("a");
//返回队列第一个元素并在队列中删除
String str = queue.poll();
//返回第一个元素
queue.element();
queue.peek();
//迭代
for(String q : queue){
            System.out.println(q);
}

应用

  1. 队列+哈希表一般用来做广度优先搜索(BFS),应用示例可以看2.2,2.3。一般流程如下
	public static void bfs(Node node) {
		if (node == null) {
			return;
		}
		Queue<Node> queue = new LinkedList<>();
		HashSet<Node> map = new HashSet<>();//表示该节点在不在队列里
		queue.add(node);
		map.add(node);
		while (!queue.isEmpty()) {
			Node curnode = queue.poll();
			for (Node next : curnode.nexts) {//遍历当前点所能到达的所有点
				if (!map.contains(next)) {//map中不包含这个节点就加入队列和set集合
					map.add(next);
					queue.add(next);
				}
			}
		}
	}
  1. 也能做BFS,应用示例可以看2.4——二叉树序列化的非递归方法。

3. List

list可以用来构建有向图,并进行DFS。应用示例见2.5

class Solution {
    int ways, n, k;
    List<List<Integer>> edges;//构建有向图

    public int numWays(int n, int[][] relation, int k) {
        ways = 0;
        this.n = n;
        this.k = k;
        edges = new ArrayList<List<Integer>>();
        //初始化
        for (int i = 0; i < n; i++) {
            edges.add(new ArrayList<Integer>());
        }
        //添加连通路径
        for (int[] edge : relation) {
            int src = edge[0], dst = edge[1];
            edges.get(src).add(dst);
        }
        dfs(0, 0);
        return ways;
    }

    public void dfs(int index, int steps) {
        if (steps == k) {
            if (index == n - 1) {//如果路径终点是n-1
                ways++;
            }
            return;
        }
        //获取路径
        List<Integer> list = edges.get(index);
        for (int nextIndex : list) {
            dfs(nextIndex, steps + 1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于力扣刷C++常用操作,我可以给你一些常见的操作和技巧: 1. 使用 STL 容器和算法库:STL(Standard Template Library)是 C++ 标准库中的一个重要组成部分,包含了许多常用的容器和算法。在力扣刷中,使用 STL 可以大大提高代码的效率和可读性。例如,vector 可以用来存储动态数组,sort 可以用来排序等等。 2. 使用 auto 关键字:auto 关键字可以自动推导变量类型,可以减少代码量和提高可读性。例如,auto x = 1; 可以自动推导出 x 的类型为 int。 3. 使用 lambda 表达式:lambda 表达式是 C++11 中引入的一种匿名函数,可以方便地定义一些简单的函数对象。在力扣刷中,使用 lambda 表达式可以简化代码,例如在 sort 函数中自定义比较函数。 4. 使用位运算:位运算是一种高效的运算方式,在力扣刷中经常会用到。例如,左移运算符 << 可以用来计算 2 的幂次方,右移运算符 >> 可以用来除以 2 等等。 5. 使用递归:递归是一种常见的算法思想,在力扣刷中也经常会用到。例如,二叉树的遍历、链表的反转等等。 6. 使用 STL 中的 priority_queue:priority_queue 是 STL 中的一个容器,可以用来实现堆。在力扣刷中,使用 priority_queue 可以方便地实现一些需要维护最大值或最小值的算法。 7. 使用 STL 中的 unordered_map:unordered_map 是 STL 中的一个容器,可以用来实现哈希表。在力扣刷中,使用 unordered_map 可以方便地实现一些需要快速查找和插入的算法。 8. 使用 STL 中的 string:string 是 STL 中的一个容器,可以用来存储字符串。在力扣刷中,使用 string 可以方便地处理字符串相关的问。 9. 注意边界条件:在力扣刷中,边界条件往往是解决问的关键。需要仔细分析目,考虑各种边界情况,避免出现错误。 10. 注意时间复杂度:在力扣刷中,时间复杂度往往是评判代码优劣的重要指标。需要仔细分析算法的时间复杂度,并尽可能优化代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值