数据结构学习-线性表的单链表实现
数据结构学习---线性表的单链表实现
在使用链表实现线性表是,既可以选择单链表,也可以选择双向链表,实际中的选择要依据需要实现的基本操作来决定。
在使用单链表实现线性表的时候,为了使程序更加简洁和方便,我们在单链表的最前面添加一个哑元节点,也称为头节点。在头节点中不存储任何实质的数据对象,其next指向线性表中0号元素所在的节点。头节点的引入也使线性表运算中的边界条件更容易一些。带头节点的单链表实现线性表结构图:

通过图可以暗处,对于任何基于序号的插入,删除,以及任何基于数据元素所在的节点的前后插入,删除,在带头节点的单链表中均可转换为在某个特定的节点之后完成节点的插入,删除,而不用考虑操作在连表中的头,中,尾等不同情况。
线性表的单链表实现。
在SLinkedList类中有3个成员变量,其中size表明线性表中数据元素个数,head是带头节点的单链表的首节点引用,strategy为连表中数据元素比较策略。
算法getSize(),isEmpty()时间复杂度为1,因为通过size即可判断。
类中的两个私有方法getPreNode(Object e), getPreNode(int i), 功能是找到数据元素e或线性表中i号数据元素所在的节点的前驱结点。在带头节点的单链表中插入,删除操作均在某个节点之后完成,因此线性表中的一些基于数据元素或序号的插入,删除操作依赖于对应元素所在节点的前驱结点引用。这两个方法平均运行时间n/2.
算法replace(int i, Object e),get(int i)的平均时间复杂度为n/2。由于连表中每个节点在内存中的地址不是连续的,所以链表不具有随机存取特性,这样要对线性表中i号元素进行获取或替换的操作,不可能与使用数组实现线性表那样在常数时间内完成,而是必须从链表的头节点开始沿着链表定位i号元素所在的节点,然后才进行相应的操作,比使用数组实现要慢得多。
算法contains(Object e), indexOf(e)主要是在线性表忠查找某个数据元素,算法平均时间与使用数组一样,都只能从线性表0号元素开始,依次向后查找,因此算法时间为n/2.
算法insert(int i, Object e), remove(int i)在实现的过程中首先需要在连表中定位i号元素所在的节点的前驱结点,然后才能进行插入,删除操作,由于定位方法getPreNode(Object e),getPreNode(int i)的平均时间为n/2,而真正的结点的插入与删除只需要常数时间,因此算法的运行时间为n/2,与数组实现的运行时间相同。
算法inertBefore(Object obj, Object e),insertAfter(Object obj, Object e),remove(Object e)在现实的过程中insertBefore, remove,需要找到对应元素的前驱结点,insertAfter需要找到对应的元素本身,这个定位过程品军时间为n/2,而剩下的插入与删除操作只需要常数时间,因此整个算法平均时间要比数组的n小,所以要由于使用数组。
在使用链表实现线性表是,既可以选择单链表,也可以选择双向链表,实际中的选择要依据需要实现的基本操作来决定。
在使用单链表实现线性表的时候,为了使程序更加简洁和方便,我们在单链表的最前面添加一个哑元节点,也称为头节点。在头节点中不存储任何实质的数据对象,其next指向线性表中0号元素所在的节点。头节点的引入也使线性表运算中的边界条件更容易一些。带头节点的单链表实现线性表结构图:
通过图可以暗处,对于任何基于序号的插入,删除,以及任何基于数据元素所在的节点的前后插入,删除,在带头节点的单链表中均可转换为在某个特定的节点之后完成节点的插入,删除,而不用考虑操作在连表中的头,中,尾等不同情况。
线性表的单链表实现。
package taxus.list; public class ListSLinked implements List { private Strategy strategy; private int size; private SLNode head;//单链表首节点引用 public ListSLinked(){ this(new DefaultStrategy()); } public ListSLinked(Strategy strategy){ this.strategy = strategy; head = new SLNode(); size = 0; } private SLNode getPreNode(Object e){ SLNode p = head; while (p.getNext() != null) { if (strategy.equal(p.getNext().getData(), e)) { return p; } else { p = p.getNext(); } } return null; } private SLNode getPreNode(int i){ SLNode p = head; for (; i > 0; i--) { p = p.getNext(); } return p; } private SLNode getNode(int i){ SLNode p = head.getNext(); for (; i > 0; i--) { p = p.getNext(); } return p; } public int getSize() { return size; } public boolean isEmpty() { return size == 0; } public boolean contains(Object e) { SLNode p = head.getNext(); while (p != null) { if (strategy.equal(p.getData(), e)) { return true; } else { p = p.getNext(); } } return false; } public int indexOf(Object e) { SLNode p = head.getNext(); int index = 0; while (p != null) { if (strategy.equal(p.getData(), e)) { return index; } else { p = p.getNext(); index++; } } return -1; } public void insert(int i, Object e) throws OutOfBoundaryException { if (i < 0 || i > size) { throw new OutOfBoundaryException("指定的插入序号越界,Size:" + size + " Index:" + i); } SLNode p = getPreNode(i); SLNode q = new SLNode(e, p.getNext()); p.setNext(q); size++; return; } public boolean insertBefore(Object obj, Object e) { SLNode p = getPreNode(obj); if (p != null) { SLNode q = new SLNode(e, p.getNext()); p.setNext(q); size++; return true; } return false; } public boolean insertAfter(Object obj, Object e) { SLNode p = head.getNext(); while (p != null) { if (strategy.equal(p.getData(), obj)) { SLNode q = new SLNode(e, p.getNext()); p.setNext(q); size++; return true; } else { p = p.getNext(); } } return false; } public Object remove(int i) throws OutOfBoundaryException { if (i < 0 || i >= size) { throw new OutOfBoundaryException("指定的删除序号越界,Size:" + size + " Index:" + i); } SLNode p = getPreNode(i); Object obj = p.getNext().getData(); p.setNext(p.getNext().getNext()); size--; return obj; } public boolean remove(Object e) { SLNode p = getPreNode(e); if (p != null) { p.setNext(p.getNext().getNext()); size--; return true; } return false; } public Object replace(int i, Object e) throws OutOfBoundaryException { if (i < 0 || i >= size){ throw new OutOfBoundaryException("指定的删除序号越界"); } SLNode p = getNode(i); Object obj = p.getData(); p.setData(e); return obj; } public Object get(int i) throws OutOfBoundaryException { if (i < 0 || i >= size) { throw new OutOfBoundaryException("指定的序号越界"); } SLNode p = getNode(i); return p.getData(); } }
在SLinkedList类中有3个成员变量,其中size表明线性表中数据元素个数,head是带头节点的单链表的首节点引用,strategy为连表中数据元素比较策略。
算法getSize(),isEmpty()时间复杂度为1,因为通过size即可判断。
类中的两个私有方法getPreNode(Object e), getPreNode(int i), 功能是找到数据元素e或线性表中i号数据元素所在的节点的前驱结点。在带头节点的单链表中插入,删除操作均在某个节点之后完成,因此线性表中的一些基于数据元素或序号的插入,删除操作依赖于对应元素所在节点的前驱结点引用。这两个方法平均运行时间n/2.
算法replace(int i, Object e),get(int i)的平均时间复杂度为n/2。由于连表中每个节点在内存中的地址不是连续的,所以链表不具有随机存取特性,这样要对线性表中i号元素进行获取或替换的操作,不可能与使用数组实现线性表那样在常数时间内完成,而是必须从链表的头节点开始沿着链表定位i号元素所在的节点,然后才进行相应的操作,比使用数组实现要慢得多。
算法contains(Object e), indexOf(e)主要是在线性表忠查找某个数据元素,算法平均时间与使用数组一样,都只能从线性表0号元素开始,依次向后查找,因此算法时间为n/2.
算法insert(int i, Object e), remove(int i)在实现的过程中首先需要在连表中定位i号元素所在的节点的前驱结点,然后才能进行插入,删除操作,由于定位方法getPreNode(Object e),getPreNode(int i)的平均时间为n/2,而真正的结点的插入与删除只需要常数时间,因此算法的运行时间为n/2,与数组实现的运行时间相同。
算法inertBefore(Object obj, Object e),insertAfter(Object obj, Object e),remove(Object e)在现实的过程中insertBefore, remove,需要找到对应元素的前驱结点,insertAfter需要找到对应的元素本身,这个定位过程品军时间为n/2,而剩下的插入与删除操作只需要常数时间,因此整个算法平均时间要比数组的n小,所以要由于使用数组。