你需要实现一个模拟单标签页浏览器历史记录的 BrowserHistory 类,核心要求是高效处理访问新页面(清空前进记录)、后退、前进 三个操作。最适合的方案是用 ArrayList 存储历史记录,配合当前索引定位,所有操作的时间复杂度均为 O(1)(visit 截断操作最坏 O(n),但题目约束下完全够用),且逻辑直观、易于理解。
解题思路
-
数据结构选择 :
ArrayList<String>存储浏览历史(按访问顺序排列),支持随机访问和快速截断;int current记录当前所在页面的索引(初始为0,对应首页)。
-
核心操作逻辑 :
操作 实现方式 初始化 将首页加入历史列表, current设为0。visit(url) 截断 current之后的所有历史记录(清空前进记录),添加新url,current移到新索引。back(steps) 计算新索引 = Math.max(0, current - steps)(不超过历史起点),更新current并返回对应url。forward(steps) 计算新索引 = Math.min(history.size()-1, current + steps)(不超过历史终点),更新current并返回对应url。
完整Java代码(AC级性能,匹配示例)
java
import java.util.ArrayList;
import java.util.List;
public class BrowserHistory {
// 存储浏览历史记录(按访问顺序)
private final List<String> history;
// 当前页面在history中的索引
private int current;
// 初始化:首页作为初始历史记录
public BrowserHistory(String homepage) {
history = new ArrayList<>();
history.add(homepage);
current = 0;
}
// 访问新url,清空前进记录
public void visit(String url) {
// 步骤1:删除current之后的所有记录(前进记录清空)
// 比如当前在索引2,历史有[0,1,2,3,4],则删除3、4,保留[0,1,2]
if (current < history.size() - 1) {
history.subList(current + 1, history.size()).clear();
}
// 步骤2:添加新url,current移到新索引
history.add(url);
current = history.size() - 1;
}
// 后退steps步,返回最终页面url
public String back(int steps) {
// 计算后退后的索引(不能小于0)
current = Math.max(0, current - steps);
return history.get(current);
}
// 前进steps步,返回最终页面url
public String forward(int steps) {
// 计算前进后的索引(不能超过历史记录最后一位)
current = Math.min(history.size() - 1, current + steps);
return history.get(current);
}
// 测试用例(匹配题目示例)
public static void main(String[] args) {
BrowserHistory browserHistory = new BrowserHistory("leetcode.com");
browserHistory.visit("google.com"); // history: [leetcode, google], current=1
browserHistory.visit("facebook.com"); // history: [leetcode, google, facebook], current=2
browserHistory.visit("youtube.com"); // history: [leetcode, google, facebook, youtube], current=3
System.out.println(browserHistory.back(1)); // 3-1=2 → facebook.com
System.out.println(browserHistory.back(1)); // 2-1=1 → google.com
System.out.println(browserHistory.forward(1)); // 1+1=2 → facebook.com
browserHistory.visit("linkedin.com"); // 截断current=2之后的youtube,添加linkedin → history: [leetcode, google, facebook, linkedin], current=3
System.out.println(browserHistory.forward(2)); // 3+2=5,但最大是3 → linkedin.com
System.out.println(browserHistory.back(2)); // 3-2=1 → google.com
System.out.println(browserHistory.back(7)); // 1-7=-6 → 0 → leetcode.com
}
}
代码关键部分解释
- visit方法的截断逻辑 :
history.subList(current + 1, history.size()).clear():subList返回的是原列表的视图,clear()会直接删除原列表中对应范围的元素,高效清空前进记录;- 例如当前在索引2(facebook),访问linkedin时,会删除索引3(youtube),再添加linkedin,确保前进记录被清空。
- 索引边界处理 :
back时用Math.max(0, ...)确保不后退到首页之前;forward时用Math.min(history.size()-1, ...)确保不前进到未访问的页面。
- 性能保障 :
ArrayList的get是 O(1) 随机访问,subList().clear()是 O(n)(n为截断的元素数),但题目中最多调用5000次方法,且单次截断的元素数有限,实际运行无性能问题。
测试用例执行结果(匹配题目输出)
facebook.com
google.com
facebook.com
linkedin.com
google.com
leetcode.com
进阶优化(可选,针对极端场景)
如果需要支持百万级历史记录 且 visit 频繁截断,可改用 LinkedList + 迭代器,但会牺牲随机访问性能。对于题目给定的约束(最多5000次调用),ArrayList 是最优选择。
总结
- 核心数据结构:
ArrayList存储历史记录 + 索引定位当前页面,兼顾随机访问和截断效率; - 关键逻辑:
visit时截断前进记录,back/forward时严格控制索引边界; - 性能:所有操作均为高效操作,完全满足题目5000次调用的约束。