一些难用的数据结构…
一瓶酒一支烟一道力扣敲一天…
目录
- 149.直线上最多的数
- 752.打开转盘锁——广度优先搜索
- 815.公交路线——优化建图+广度优先搜索
- 剑指 Offer 37. 序列化二叉树——DFS,二叉树序列化和反序列化
- LCP 07.传递信息——有向图,DFS,BFS,DP
- 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。
记录斜率
- 如果只是单纯 Δ x Δ y \frac{\Delta x}{\Delta y} ΔyΔx,可能因为精度不够而无法足够精确地表示每一个斜率。所以可以用一个二元组来表示斜率 ( Δ x , Δ y ) (\Delta x,\Delta y) (Δx,Δy)。
- 考虑如: 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)
- 考虑如: 1 − 2 = − 1 2 \frac{1}{-2}=\frac{-1}{2} −21=2−1这种情况,所以我们可以规定分子必须非负。如果 m x < 0 mx<0 mx<0令 m x = − m x , m y = − m y mx=-mx,my=-my mx=−mx,my=−my
- 考虑直线是垂直的和水平的情况,即: 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
- 在题解里面他是直接 v a l = m y + m x ∗ 20001 val=my+mx*20001 val=my+mx∗20001,通过比较val来判定斜率是否一致的。这种方法我想不到我也不能理解,但是最后是看题解才敲出来的代码所以…
- 这里还有一种思路,那就是把mx,my直接打造成一个字符串"mx_my"通过比较字符串来衡量斜率是否一致。
一些优化
- 在点的总数量小于等于2的情况下,我们总可以用一条直线将所有点串联,此时我们直接返回点的总数量即可;
- 其实只要考虑下标大于i的,因为i之前的点和i的关系已经被考虑过了
- 当我们找到一条直线经过了图中超过半数的点时,我们即可以确定该直线即为经过最多点的直线;
- 当我们枚举到点 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。
一些小细节
- 如果target在deadends中那么直接返回-1;
- 如果target是0000直接返回0;
- 如果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。
解题思路
- DFS
使用深度优先搜索计算方案数。从节点 0 出发做深度优先搜索,每一步记录当前所在的节点以及经过的轮数,当经过 k轮时,如果位于节点 n−1,则将方案数加 1。搜索结束之后,即可得到总的方案数。
定义一个List<List>存储每一条连通路径,然后使用递归or循环,寻找终点是n-1,且只经过k个轮次的路径。 - BFS
从节点 0出发做广度优先搜索,当遍历到 k层时,如果位于节点 n−1,则将方案数加 1。搜索结束之后,即可得到总的方案数。 - 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[n−1][j]=0,dp[i][1]=和n−1连通的所以路径数
对于路径 [ 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][j−1]
代码
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);
}
应用
- 队列+哈希表一般用来做广度优先搜索(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);
}
}
}
}
- 也能做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);
}
}
}