题解 | #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); } }