且
在日常生活中, 经常会遇到条件执行. 通常是往 for
或者 while
的参数中写一个条件控制循环.
有时候, 条件可能不止 1 个, 这时候就会使用多个判断表达式作为条件, 并用 &&
连接.
条件判断的时候, 计算机就会从左往后先后执行判断表达式, 当判断表达式都满足时, 进行指定的条件执行.
这个时候, 当条件判断中有加减法的时候, 就需要注意了.
因为看上去, 就是符合条件执行, 不符合条件就不执行离开执行后面的, 仅此而已.
但是计算机, 它真的是会去计算的.
拿一个循环举例, 现在要将最后一个位置的数据往前插入到第一个比它小的位置.
循环因子 in
从 n 为起点, 往前循环, 直到 n>0
n 递减.
现在要比较前一个元素, 如果遇到比 temp
值小的时候, 便移动元素.
temp, 是原来 in
位置元素的拷贝.
在插入数组元素中, 这是常见的操作.
显然, 这里的条件需要另外插入一个 a[n-1]>=temp
程序
public void insert() {
int in = a.length-1;
long temp = a[in];
while (in > 0 && a[in-1] >= temp) {
a[in] = a[in-1]; // 向后移动: 小坐标给大坐标, 左大右小
in--;
}
//首次抵达比自己小的元素的后面
a[in] = temp;
}
}
程序执行, a[length-1]
顺利地来到第一个比它小的元素后边, 站在它的后边.
原来在写这个时候, 是在实现插入排序的.
插入排序的简单思路复现:
假设前半部分总是有序的, 实际上, 第一个位置的元素相对于 1 个元素的序列来说, 它就是有序的(虽然听上去有些牵强, 但是你不得不承认它是事实)
从第 1 个开始, 这个位置就是有序部分和无序部分中的中间点, 无序序列的起点, 后面的插入排序都将从它开始.
这个时候, 控制这个部分, 还需要另一个循环因子控制下一次循环的起点.
因为每次将当前位插入到有序序列中去后, 前面部分就部分有序了, 下一次循环将从有序序列的后一位开始.
即前面有序序列不需要另外比较了.
于是, 有了 out 和 in 2 个循环因子控制.
先说思路, 主循环中以 in 为起点, 往前遍历, 在没到 0 之前以及遇到比自己更小的元素之前, 元素都需要后移并递减.
实现就是这样:
public void insertSort() {
int in;
int out;
for (out = 1; out < nElems; out++) {
in = out;
long temp = a[in];
while (in > 0 && a[in-1] >= temp) {
a[in] = a[in-1]; // 向后移动: 小坐标给大坐标, 左大右小
in--;
}
//首次抵达比自己小的元素的后面
a[in] = temp;
}
}
原来是这样, 我以为就结束了, 直到我从自己对插入排序的认识出发手写一遍的时候, 执行没有完成, 还抛出了越界的异常 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 10
每个点都确认是基于插入排序写出来的, 也确认无误, 和参考答案一一比对. 接着调试,
以 [77 99 44 55 22 88 11 0 66 33 ]
为例
直到 in 等于 2, 之前的 99 后移, 77 后移也都符合预期, 直到下一步 n = 0 的时候, F6
按下去后, 调试窗口跳出了 ArrayIndexOutOfBoundsException
的异常.
后面的排序自然没有成功.
这是当时的程序
public void insertSort() {
int in;
int out;
for (out = 1; out < nElems; out++) {
in = out;
long temp = a[in];
while ( a[in-1] >= temp && in > 0 ) {
a[in] = a[in-1]; // 向后移动: 小坐标给大坐标, 左大右小
in--;
}
//首次抵达比自己小的元素的后面
a[in] = temp;
}
}
发现问题:
原来在写主循环的时候, 我私以为 a[in]-1 > temp
判断前一个是不是比旗帜位更大才是更重要的, 至于循环终点 n>0
控制终点只是其次.
问题就出现在这里.
当旗帜位指向 44 的时候, 前面正常比对并交换, 眼看着 44 就要插入到 n = 0 的位置晋升第一位最小元素的时候, 数组越界了.
原因在于 这时候计算机不是判断不符合就走了的, 条件里的有数组的引用, 有加减.
当走 while 条件的时候, 它会拿着等于 0 的 n 值先后 a[in-1] >= temp
做完减法后做引用去引用数组元素.
做到减法的时候, 完了, 遇到 a[-1]
这样的项, 数组怎么可能有那样的项嘛.
感悟
难怪, se 中会强调写那些有关条件且或非多条件的执行过程哪.
如经常看到这样的写, 执行完第一个就不会继续往后执行后面的执行.
int n = 0;
while (n < 0 || xxx) {
}
判断条件的先后决定计算机执行的先后, 看上去的判断对计算机来说有时候除了判断还有还是执行.
平时遇到的一个小问题, 衍生出来的篇幅有点拉, 自行阅读哈 hh.