【日撸 Java 三百行】Day 12(顺序表(二))
目录
Day 12:顺序表(二)
一、顺序表的方法
1. 顺序查找
拓展:顺序查找中的哨兵思想
2. 插入
3. 删除
二、代码及测试
拓展:
小结
Day 12:顺序表(二)
Task:
今天的代码直接在昨天的基础上增加. 只贴出新的部分.
- 查找给定元素所处的位置. 找不到就返回 -1.
- 在给定位置增加元素. 如果线性表已满, 或位置不在已有位置范围之内, 就拒绝增加. 该位置可以是在最后一个元素之后一个.
- 删除定定位置的元素. 要处理给定位置不合法的情况. 该位置必须是已经有数据的.
- 函数 要求同样的输入参数获得同样的输出结果, 但 方法 所依赖的数据既包括参数列表中给出的,也依赖于对象的成员变量. 因此, 面向对象所涉及的参数列表要短些. 例如, locate 方法就有效利用了 length 和 data 这两个成员变量.
一、顺序表的方法
在【数据结构】线性表-CSDN博客 中,我们介绍了如何用 C++ 来实现顺序表的构造,检索,插入以及删除。运用类似的思维模式,我们可以举一反三的实现用 Java 语言操作顺序表。
由于存在相应的专题,所以这里仅仅做代码上的展示,思维简要略过。
今天的代码内容是对昨日顺序表对象的方法扩充,详情可见:【日撸 Java 三百行】Day 11-12(顺序表)-CSDN博客
今天我们的任务是对于顺序表方法的基础扩充,因此就继续沿用昨日的的类,补充几个关键的函数和改写测试用的main函数即可。
1. 顺序查找
顺序查找的含义是:给出任意一个数,在顺序表中查找此元素首次出现时的下标号。
默认的顺序查找方法就是从头到尾遍历数据(什么方向都行),一但发现合适的数据就返回下标即可,如果结束遍历后都没有发现合适元素就返回一个定义内的下标非法的数值(比如-1)。
此算法的执行时间主要体现在元素的比较次数中,即for循环上。最好的情况是第1个元素的值即为 value,此时只需比较一次即可;最差的情况则是表中没有值为 value 的元素,这时需要比较n次;一般情况下,假设 value出现在每个位置的概率相同,均为p=1/n,则平均的比较时间为
代码如下:
/************************ Find the index of the given value. If it appears in multiple positions,* simply return the first one.* * @param paraValue The given value.* @return The position. -1 for not found.**********************/public int indexOf(int paraValue) {int tempPosition = -1;for (int i = 0; i < length; i++) {if (data[i] == paraValue) {tempPosition = i;break;} // Of if} // Of forreturn tempPosition;}// Of indexOf
拓展:顺序查找中的哨兵思想
所谓“哨兵”就是用一个特殊值来作为数组的边界,使用“哨兵”可以少用一条判断语句,所以可以提高程序的效率。
这样就可以将数组的第一个位置作为“哨位”,数据的存储从Data[1]开始。因此线性表的初始化中List是从0开始的,表示表中暂无元素。
那么,这种方法到底好不好呢?
从正面看,"哨兵"的引入确实可以令数组不必判断越界问题,到头时循环自己会退出,而且这种"哨兵"思想确实也能避免很多不必要判断(就不需要诸如i<length这种判断了)。
但是,我们知道对于一个 List 来说,插入和删除一个元素都需要考虑到其他元素的平移存储。对比下来,优化一条判断语句的收益远低于处理平移存储所花费的代价。
而一个 “概念” 的提出,必然尤其优越性。当数据总量足够大时,每轮循环固定一次的越界判断累加上来会是一个十分大的开销。此时,“哨兵” 无需判断边界的优势就体现了。
2. 插入
插入操作将改变顺序表的内容,因此必须满足一定的限制条件:除了涉及被更新的那个元素之外,其他线性关系的相对顺序应该保持不变。为此,需要对顺序表实施一系列的元素移动操作来维护逻辑的和存储的线性关系。
在进行插入前,我们需要考虑以下两点(这是需要再程序中体现的逻辑)
- 顺序表是否满了?
- 插入的位置合理吗?
这两个基本问题的解决是顺序表健壮性的基本保障。
插入的具体操作可以参考下图:
在循环遍历时,这里需要采用反向遍历,这是因为我们在插入新元素的位置后,用前一个元素覆盖下一个元素。这一点正向遍历时做不到的。
public boolean insert(int paraPosition, int paraValue) {if (length == MAX_LENGTH) {System.out.println("List full.");return false;} // Of ifif ((paraPosition < 0) || (paraPosition > length)) {System.out.println("The position " + paraPosition + " is out of bound.");return false;}// From tail to head. The last one is moved to a new position.// Because length < MAX_LENGTH, no exceeding occurs.for (int i = length; i > paraPosition; i--) {data[i] = data[i - 1];} // Of for idata[paraPosition] = paraValue;length++;return true;}// Of insert
3. 删除
删除操作与插入操作有异曲同工之妙,同样的,我们也需要考虑如下两个问题:
- 顺序表是否空了?
- 插入的位置合理吗?
删除操作示意图如下:
与插入操作相反,这里我们需要正向遍历, 因为我们在删除一个元素后,需要用后一个元素来填补前一个元素,而这是反向遍历做不到的。
除此之外,我们还需要对 length 做 -1 的修正。
/************************ Delete a value at a position.* * @param paraPosition The given position.* @return Success or not.**********************/public boolean delete(int paraPosition) {if ((paraPosition < 0) || (paraPosition >= length)) {System.out.println("The position " + paraPosition + " is out of bounds.");return false;} // Of if// From head to tailfor (int i = paraPosition; i < length - 1; i++) {data[i] = data[i + 1];} // Of for ilength--;return true;}// Of delete
二、代码及测试
/************************ The entrance of the program.* * @param args Not used now.**********************/public static void main(String args[]) {int[] tempArray = { 1, 4, 5, 9 };SequentialList tempFirstList = new SequentialList(tempArray);System.out.println("After initialization, the list is: " + tempFirstList.toString());System.out.println("Again, the list is: " + tempFirstList);int tempValue = 4;int tempPosition = tempFirstList.indexOf(tempValue);System.out.println("The position of " + tempValue + " is " + tempPosition);tempValue = 5;tempPosition = tempFirstList.indexOf(tempValue);System.out.println("The position of " + tempValue + " is " + tempPosition);tempPosition = 2;tempValue = 5;tempFirstList.insert(tempPosition, tempValue);System.out.println("After inserting " + tempValue + " to position " + ", the list is: " + tempFirstList);tempPosition = 8;tempValue = 10;tempFirstList.insert(tempPosition, tempValue);System.out.println("After inserting " + tempValue + " to position " + tempPosition + ", the list is: " + tempFirstList);for (int i = 0; i < 8; i++) {tempFirstList.insert(i, i);System.out.println("After inserting " + i + " to position " + i + ", the list is: " + tempFirstList);} // Of for itempFirstList.reset();System.out.println("After reset, the list is: " + tempFirstList);}// Of main
测试说明如下:
基本说明下,首先初始了一个包含元素1、4、5、9的顺序表,输出必要答应,然后分别进行了下述操作:
- 查找数字4、5在表中的位置,并打印信息
- 在下标为2的元素前插入5,在下标为8的元素前插入10(这个越界了)
- 删除下标为3的元素
- 在下标为i的元素前插入元素i (0 < i < 8) (这个在插入过程中会导致顺序表满,后续元素将无法插入)
拓展:
顺序表基本方法:【数据结构】线性表-CSDN博客
类、包和接口:
查找中的 “哨兵思想”:顺序查找与"哨兵"的使用&&二分查找_带哨兵的顺序查找算法-CSDN博客
小结
检索,插入和删除是针对各种数据结构的最基本操作,任何数据结构都比不开这三者。实际上,随着我们学习的深入,会发现我们可以根据自己的需求,去重构这三者。
举个例子,对于插入操作,这里仅仅展示了单个元素的插入,那么扩展一下,我们能不能插入一个数列?这个数列是自然递增,还是说可以我们来自定义?学习了重构后,这是我们自然可以联想到的东西。