[MOOC笔记]第三章 列表(数据结构)

来源:互联网 发布:淘宝图片空间免费领取 编辑:程序博客网 时间:2024/05/21 11:11

1.列表的介绍

列表是采用动态储存策略的典型结构,它的基本单位是节点。各节点通过引用或者指针彼此连接,在逻辑上构成一个线性序列。相邻节点彼此互称为前驱(predecessor)和后继(successor),如果前驱或后继存在则必然唯一,没有前驱(后继)的节点被称为首(末)节点(在有些列表的实际实现中,首(末)节点是拥有前驱(后继)的,它们被称为头(尾)节点,这两个节点并没有实际作用,也不会对外暴露,只是为了方便某些操作而产生的。)。

与向量不同的是,列表在物理上并不是一段连续的内存空间,所以无法通过秩直接访问其中的元素。因此列表更常用的是寻位置访问,也就是通过节点中的互相引用找到每个元素的位置。


2.列表的接口

作为组成列表的基本单位,节点至少需要这些接口:

操作功能pred()当前节点前驱节点的位置succ()当前节点后继节点的位置data()当前节点所存放的数据对象insertAsPred(e)插入前驱节点,存入被引用对象einsertAsSucc(e)插入后继节点,存入被引用对象e


由节点所组成的列表,则需要如下接口:

操作功能适用对象size()报告列表当前的规模(节点总数)列表first()、last()返回首、末节点的位置列表insertAsFirst(e)、insertAsLast(e)将e作为列表的首、末节点插入列表insertBefore(p, e)、insertAfter(p, e)将e作为节点p的前驱、后继插入列表remove(p)删除节点p列表disordered()判断所有节点是否已按照非降序排列列表sort()调整各节点的位置,使之按非降序排列列表find(e)查找目标元素e列表search(e)查找不大于e且秩最大的节点有序列表deduplicate()删除重复节点列表uniquify()删除重复节点有序列表traverse()遍历列表并统一处理所有节点,处理方法由函数对象指定列表

3.列表的寻秩访问、插入和删除操作
/** * 列表的寻秩访问,时间复杂度O(n) * @param r 待访问元素的秩 * @return 对应秩的节点 */public ListNode get(int r) {//判断秩是否合法if (r >= this.size || r < 0) {throw new ArrayIndexOutOfBoundsException("下标越界");}//得到列表的首节点ListNode node = this.first();//从首节点出发,依次指向后继节点,递推r次则得到秩为r的元素while (r-- > 0) {node = node.succ();}return node;}/** * 列表的前驱插入操作(后继插入操作正好相反),时间复杂度O(1) * @param node * @param e * @return */public ListNode insertBefore(ListNode node, T e) {//增加列表的规模this.size++;//构造一个新节点,新节点的前驱是该节点的前驱,新节点的后继是该节点。ListNode<T> newNode = new ListNode(e, node.getPred(), node);//将该节点前驱的后继设为新节点node.getPred().setSucc(node);//将该节点的前驱设为新节点node.setPred(node);//返回新节点return newNode;}/** * 删除一个合法位置的节点,时间复杂度O(1) * @param node 待删除的节点 * @return 该节点的元素 */public T remove(ListNode<T> node) {//减少列表的规模this.size--;//备份节点中的元素T e = node.getDate();//将该节点前驱的后继节点设为该节点的后继node.getPred().setSucc(node.getSucc());//将该节点后继的前驱节点设为该节点的前驱node.getSucc().setPred(node.getPred());//删除该节点的引用node = null;//返回该节点的元素return e;}

4.有序列表

有序列表同有序向量一样,再去重上会有更高的性能。列表和有序列表的去重操作与向量和有序向量类似,时间复杂度也相当:

/** * 列表的去重操作 时间复杂度O(n^2) * @return 删除的元素总数 */public int deduplicate() {//记录去重之前的规模int oldSize = this.size;//从首节点开始遍历ListNode node = this.first();//记录当前的位置int r = 1;//依次遍历直到尾节点while ((node = node.getSucc()) != this.trailer) {//在node的r个前驱中寻找是否有与它内容相同的节点ListNode temp = this.find(node.getDate(), r, node);//如果存在相同的节点 删除该节点,否则增加前驱范围if (temp != null) {this.remove(temp);} else {r++;}}//返回规模变化量return oldSize - this.size;}/** * 有序列表的去重操作 时间复杂度O(n) * @return 删除的元素总数 */public int uniquify() {//记录每个区间的第一个元素和下个区间的第一个元素的位置int oldSize = this.size;//从首节点开始遍历ListNode node = this.first();//记录下一个节点ListNode next;//依次遍历直到尾节点while ((next = node.getSucc()) != this.trailer) {//删除每一个相同节点if (node.getDate().equals(next.getDate())) {this.remove(next);} else {node = next;}}//返回规模变化量return oldSize - this.size;}


但是由于列表是寻位置访问而不是寻秩访问,导致有序列表并不能在查找中产生更高的效率。所以有序列表的查找时间复杂度和普通列表一样都是O(n)


注:本课为清华大学邓俊辉老师在学堂在线的Mooc课程《数据结构》,下次开课时间是2015年春季。如果感兴趣的同学可以去看看,个人觉得这门课无论广度还是深度都高于其他数据结构课。




0 0
原创粉丝点击