The existing design of class DList
and DListNode
is taken. The main criteria is to do successive updates in \$O(1)\$ time.
Part III (3 points)
Implement a "lockable" doubly-linked list ADT: a list in which any node can be "locked." A locked node can never be removed from its list. Any attempt to remove a locked node has no effect (not even an error message). Your locked list classes should be in the list package alongside
DList
andDListNode
. First, define aLockDListNode
class that extendsDListNode
and carries information about whether it has been locked.LockDListNode
's are not locked when they are first created. YourLockDListNode
constructor(s) should call aDListNode
constructor to avoid code duplication.Second, define a
LockDList
class that extendsDList
and includes an additional methodpublic void lockNode(DListNode node) { ... }
that permanently locks "node".Your
LockDList
class should override just enough methods to ensure that(1)
LockDListNode
's are always used inLockDList
's (instead ofDListNode
's), and(2) locked nodes cannot be removed from a list.
WARNING: To override a method, you must write a new method in the subclass with EXACTLY the same prototype. You can’t change a parameter’s type to a subclass. Overriding won’t work if you do that.
Your overriding methods should include calls to the overridden superclass methods whenever it makes sense to do so. Unnecessary code duplication will be penalized.
Solution
/* DListNode.java */
package cs61b.homework4;
/**
* A DListNode is a node in a DList (doubly-linked list).
*/
public class DListNode {
/**
* item references the item stored in the current node. prev references the
* previous node in the DList. next references the next node in the DList.
*
* DO NOT CHANGE THE FOLLOWING FIELD DECLARATIONS.
*/
public Object item;
private DListNode prev;
private DListNode next;
/**
* DListNode() constructor.
*
* @param i
* the item to store in the node.
* @param p
* the node previous to this node.
* @param n
* the node following this node.
*/
DListNode(Object i, DListNode p, DListNode n) {
item = i;
setPrev(p);
setNext(n);
}
DListNode getNext() {
return next;
}
void setNext(DListNode next) {
this.next = next;
}
DListNode getPrev() {
return prev;
}
void setPrev(DListNode prev) {
this.prev = prev;
}
}
/* DList.java */
package cs61b.homework4;
/**
* A DList is a mutable doubly-linked list ADT. Its implementation is
* circularly-linked and employs a sentinel (dummy) node at the sentinel
* of the list.
*
* DO NOT CHANGE ANY METHOD PROTOTYPES IN THIS FILE.
*/
public class DList {
/**
* sentinel references the sentinel node.
* size is the number of items in the list. (The sentinel node does not
* store an item.)
*
* DO NOT CHANGE THE FOLLOWING FIELD DECLARATIONS.
*/
protected DListNode sentinel;
protected int size;
/* DList invariants:
* 1) sentinel != null.
* 2) For any DListNode x in a DList, x.next != null.
* 3) For any DListNode x in a DList, x.prev != null.
* 4) For any DListNode x in a DList, if x.next == y, then y.prev == x.
* 5) For any DListNode x in a DList, if x.prev == y, then y.next == x.
* 6) size is the number of DListNodes, NOT COUNTING the sentinel,
* that can be accessed from the sentinel (sentinel) by a sequence of
* "next" references.
*/
/**
* newNode() calls the DListNode constructor. Use this class to allocate
* new DListNodes rather than calling the DListNode constructor directly.
* That way, only this method needs to be overridden if a subclass of DList
* wants to use a different kind of node.
* @param item the item to store in the node.
* @param prev the node previous to this node.
* @param next the node following this node.
*/
protected DListNode newNode(Object item, DListNode prev, DListNode next) {
return new DListNode(item, prev, next);
}
/**
* DList() constructor for an empty DList.
*/
public DList() {
this.sentinel = this.newNode(null,null,null);
this.sentinel.setNext(sentinel);
this.sentinel.setPrev(sentinel);
}
/**
* isEmpty() returns true if this DList is empty, false otherwise.
* @return true if this DList is empty, false otherwise.
* Performance: runs in O(1) time.
*/
public boolean isEmpty() {
return size == 0;
}
/**
* length() returns the length of this DList.
* @return the length of this DList.
* Performance: runs in O(1) time.
*/
public int length() {
return size;
}
/**
* insertFront() inserts an item at the front of this DList.
* @param item is the item to be inserted.
* Performance: runs in O(1) time.
*/
public void insertFront(Object item) {
DListNode node = this.newNode(item, this.sentinel, this.sentinel.getNext());
node.getNext().setPrev(node);
this.sentinel.setNext(node);
this.size++;
}
/**
* insertBack() inserts an item at the back of this DList.
* @param item is the item to be inserted.
* Performance: runs in O(1) time.
*/
public void insertBack(Object item) {
DListNode node = this.newNode(item, this.sentinel.getPrev(), this.sentinel);
this.sentinel.setPrev(node);
node.getPrev().setNext(node);
this.size++;
}
/**
* front() returns the node at the front of this DList. If the DList is
* empty, return null.
*
* Do NOT return the sentinel under any circumstances!
*
* @return the node at the front of this DList.
* Performance: runs in O(1) time.
*/
public DListNode front() {
if (this.sentinel.getNext() == sentinel){
return null;
}else{
return this.sentinel.getNext();
}
}
/**
* back() returns the node at the back of this DList. If the DList is
* empty, return null.
*
* Do NOT return the sentinel under any circumstances!
*
* @return the node at the back of this DList.
* Performance: runs in O(1) time.
*/
public DListNode back() {
if(this.sentinel.getPrev() == sentinel){
return null;
}else{
return this.sentinel.getPrev();
}
}
/**
* next() returns the node following "node" in this DList. If "node" is
* null, or "node" is the last node in this DList, return null.
*
* Do NOT return the sentinel under any circumstances!
*
* @param node the node whose successor is sought.
* @return the node following "node".
* Performance: runs in O(1) time.
*/
public DListNode next(DListNode node) {
if ((node == null) || (node.getNext() == this.sentinel)){
return null;
}else{
return node.getNext();
}
}
/**
* prev() returns the node prior to "node" in this DList. If "node" is
* null, or "node" is the first node in this DList, return null.
*
* Do NOT return the sentinel under any circumstances!
*
* @param node the node whose predecessor is sought.
* @return the node prior to "node".
* Performance: runs in O(1) time.
*/
public DListNode prev(DListNode node) {
if ((node == null) || (node.getPrev() == this.sentinel)){
return null;
}else{
return node.getPrev();
}
}
/**
* insertAfter() inserts an item in this DList immediately following "node".
* If "node" is null, do nothing.
* @param item the item to be inserted.
* @param node the node to insert the item after.
* Performance: runs in O(1) time.
*/
public void insertAfter(Object item, DListNode node) {
if (node == null){
return;
}else{
DListNode newNode = this.newNode(item, node, node.getNext());
node.getNext().setPrev(newNode);
node.setNext(newNode);
}
this.size++;
}
/**
* insertBefore() inserts an item in this DList immediately before "node".
* If "node" is null, do nothing.
* @param item the item to be inserted.
* @param node the node to insert the item before.
* Performance: runs in O(1) time.
*/
public void insertBefore(Object item, DListNode node) {
if (node == null){
return;
}else{
DListNode newNode = this.newNode(item, node.getPrev(), node);
node.getPrev().setNext(newNode);
node.setPrev(newNode);
this.size++;
}
}
/**
* remove() removes "node" from this DList. If "node" is null, do nothing.
* Performance: runs in O(1) time.
*/
public void remove(DListNode node) {
if(node == null){
return;
}else{
node.item = null;
node.getPrev().setNext(node.getNext());
node.getNext().setPrev(node.getPrev());
this.size--;
}
}
/**
* toString() returns a String representation of this DList.
*
* DO NOT CHANGE THIS METHOD.
*
* @return a String representation of this DList.
* Performance: runs in O(n) time, where n is the length of the list.
*/
public String toString() {
String result = "[ ";
DListNode current = sentinel.getNext();
while (current != sentinel) {
result = result + current.item + " ";
current = current.getNext();
}
return result + "]";
}
}
/* LockDListNode.java */
package cs61b.homework4;
public class LockDListNode extends DListNode{
protected boolean lock;
protected LockDListNode(Object i, DListNode p, DListNode n){
super(i, p, n);
this.lock = false;
}
}
/* LockDList.java */
package cs61b.homework4;
public class LockDList extends DList {
/**
* newNode() calls the LockDListNode constructor. Use this method to
* allocate new LockDListNodes rather than calling the LockDListNode
* constructor directly.
*
* @param item
* the item to store in the node.
* @param prev
* the node previous to this node.
* @param next
* the node following this node.
*/
protected LockDListNode newNode(Object item, DListNode prev, DListNode next) {
return new LockDListNode(item, prev, next);
}
/**
* LockDList() constructor for an empty LockDList.
*/
public LockDList() {
super();
}
/**
* remove() removes "node" from this DList. If "node" is null, do nothing.
* Performance: runs in O(1) time.
*/
public void remove(DListNode node) {
if (node == null) {
return;
} else if (((LockDListNode)node).lock == true) {
return;
} else {
node.item = null;
node.getPrev().setNext(node.getNext());
node.getNext().setPrev(node.getPrev());
this.size--;
}
}
public void lockNode(DListNode node) {
if(node == null){
return;
}else{
((LockDListNode)node).lock = true;
}
}
}
With the given skeleton code for DList
and DListNode
here:
- Assume that a user passes a node that is part of the correct list. This is out of scope here.
- Access specifier for class/method/constructor can be improved (if required).
- Can I avoid typecasting in overriding the
remove
method of theLockDList
class? - Can I avoid typecasting in the
lockNode
method of theLockDList
class?
Note: The package name is cs61b.homework4
instead of list
.