题解 | #MP3光标位置#
MP3光标位置
https://www.nowcoder.com/practice/eaf5b886bd6645dd9cfb5406f3753e15
这道题应该可以不用迭代器的,但我最开始用了迭代器,就想着把它做对了,结果就遇到迭代器指针的坑,然后调了好长时间,好累,一度想要放弃,但最终总算好了,也是这个机会是把迭代器指针了取数了相关的搞明白了。
总结:
1.ListIterator中的previous()返回值是当前迭代器的cursor左边的值,next()方法返回值是cursor右边的值
2.迭代器的使用场景,应该是迭代趋势不变的情况下,例如一直向next取值,或者一直向previous取值,如果一会儿next一会儿previous的话,会很混乱,最后把自己搞死,当然如果对迭代器足够了解的话,也可以,就是伤脑筋。
3.如果用cursor右边的值表示当前选中歌曲,那么在接到"D"指令时,应该先用next()把cursor移到下一首歌曲之前,然后再调一次next()取值,取完值之后,为了保证下一次迭代时cursor在当前选中歌曲之前,就得调一次previous把cursor恢复:
接到D指令之前:|歌曲1 歌曲2 歌曲3 歌曲4 , 当前选中歌曲为歌曲1,此时若用next()取到的不是下一首歌,而是歌曲1
所以要先next()一下把指针移到歌曲2之前:歌曲1 |歌曲2 歌曲3 歌曲4 ,这时候为了取到歌曲2就得再调一次next()取它的返回值,调完之后指针变成这样了:歌曲1 歌曲2 |歌曲3 歌曲4,这会导致下一次迭代时,认为当前选中歌曲是歌曲3,实际上我们是歌曲2,为了避免如此,就调一个previous()把指针恢复到歌曲2。
总之可以这么说:为了取到指针右边的值以便将其设置为当前选中歌曲,但是又不改变指针位置(不影响下一次迭代),就每次取完值之后再调一次previous恢复指针位置。见如下代码中66,67,68三行。
4.若有关联迭代器,最好不要再用ArrayList的remove和add方法修改ArrayList中的值,这很可能导致的报错。原因如下:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
int a = Integer.parseInt(in.nextLine());
String b = in.nextLine();
new Main().MP3iter(a, b);
}
}
//思路:建立两个列表及两个各自的迭代器,一个是歌曲总列表有n个值(用来记录当前选中的是哪首歌),一个是显示列表只有最多4个值(用来记录当前显示列表),每次取一个命令,根据命令是U或者D来处理两个迭代器
void MP3iter(int n, String cmd) {
ArrayList<Integer> arr = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
arr.add(i, i + 1);
}
ListIterator<Integer> arri = arr.listIterator(0);//所有歌曲迭代器,默认选中为第一首歌
ArrayList<Integer> out = new ArrayList<>();
Arrays.stream(new int[] {1, 2, 3, 4}).forEach(o->out.add(o));
ListIterator<Integer> outi = out.listIterator(0);//当前显示列表迭代器
int now = 1;//表示当前选中歌曲,默认第一首歌,此时不要用next()取now的初始值,否则会把指针移到第二首歌那里,一定要用的话,就再用previous回退一下
while (cmd.length() > 0) {
char nowcmd = cmd.charAt(0);
int nowp = now;
if (nowcmd == 'U') {
//当命令为U时取now值
if (arri.previousIndex() == -1) {//若执行U前选中的是第一首歌
arri = arr.listIterator(n);//手动把歌曲迭代器的指针设置到最后一首歌的右边
now = arri.previous().intValue();//执行previous后指针在最后一首歌左边,使我们想要的选中该歌的状态
} else {
now = arri.previous().intValue();//若执行U前选中的不是歌曲1,则直接前移指针
}
//当命令为U时取显示列表
if (outi.previousIndex() == -1) {//若执行U前选中的是显示列表的第一条
if (nowp == 1) {//若该条还是第一首歌,则显示列表全部替换为最后一页
out.set(3, n);
out.set(2, n - 1);
out.set(1, n - 2);
out.set(0, n - 3);
outi = out.listIterator(3);//显示列表指针设置为选中第四行
} else {//若该条不是第一首歌,则显示列表第一条设置为now,最后一条去掉,显示列表指针位置不变
int out0 = out.get(0);
int out1 = out.get(1);
int out2 = out.get(2);
out.set(0, now);
out.set(1, out0);
out.set(2, out1);
out.set(3, out2);
}
} else {//若执行U前选中的不是显示列表的第一条
outi.previous();
}
}
if (nowcmd == 'D') {
//当命令为D时取now值:
if (arri.nextIndex() == arr.size() - 1) {//若执行D命令前选中的是最后一首歌
arri = arr.listIterator(0);//手动把指针放到第一首歌,手动设置now值
now = 1;
} else {//若执行D命令前选中的不是最后一首歌
arri.next();//把指针移到下一首歌之前
now = arri.next().intValue();//取下一首歌的值
arri.previous();//取完值后恢复指针
}
//当命令为D时取显示列表:
if (outi.nextIndex() == 3) {//若执行D前选中的是显示列表的第四条
if (nowp == n) {//若执行D前选中的是最后一页最后一首歌,显示列表全部替换为首页
outi = out.listIterator(0);//手动将选中歌曲设置为列表第一条
out.set(0, 1);
out.set(1, 2);
out.set(2, 3);
out.set(3, 4);
} else {//若执行D前选中的不是最后一页最后一首歌,列表选中项不变,列表内容去头加尾
int out1 = out.get(1);
int out2 = out.get(2);
int out3 = out.get(3);
out.set(0, out1);
out.set(1, out2);
out.set(2, out3);
out.set(3, now);
}
} else {//若执行D前选中的不是显示列表的第四条
outi.next();
}
}
if (cmd.length() == 1) {//为防止取substring时数组越界,发现执行完最后一个命令后,直接退出循环
break;
}
cmd = cmd.substring(1);
}
if (n < 4) {//当歌曲总数小于4时,显示列表是固定的1->n
for (int cf = 1; cf <= n; cf++) {
System.out.printf("%d ", cf);
}
System.out.println();
} else {
out.forEach(ff->System.out.printf("%d ", ff));
System.out.println();
}
System.out.println(now);
}
}

