/**This class implements a doubly linked list of characters in Java.
* The instance variables head and tail are initially null.
* As elements are added head points to the first element on the
* list and tail points to the last element.
* Each node on the list is of type DoubleNode.
* Each DoubleNode holds a pointer to the previous
* node and a pointer to the next node in the list.
* @param head Keeps track of the first node of the list. Null if nothing.
* @param tail Keeps track of the last node of the list. Null if nothing.
*/
public class DoublyLinkedList {
//data field
DoubleNode head;
DoubleNode tail;
/**Constructor
* Precondition: The object to be created is casted as DoublyLinkedList
* Postcondition:Constructs a new DoublyLinkedList object with head and
tail as null
* Best case and worst case are the same: theta(1)
* Afterwards a new null list comes into existence
*/
public DoublyLinkedList(){
head = null;
tail = null;
}
/**Mutator
* @param c The character to be added
* Precondition: The character is an uppercase
* or lowercase letter (in the range 'A' ... 'Z' or 'a' ... 'z')
* Postcondition:A character node containing the character
* c will be added to the end
* Best case and worst case are the same: theta(1)
*/
public void addCharAtEnd(char c){
if(this.isEmpty()) {//When no node in list so far
head = new DoubleNode(null,null,c);
tail = head;
}
else{//More than one node exist(s)
DoubleNode endNode = new DoubleNode(tail,null,c);
tail.setNext(endNode);
tail = endNode;
}
}
/**Deletes the first occurence of the character c from the list
* @param c the character want to find and then deleted
* @return True if a deletion occurred and false otherwise
* Precondition: The character is an uppercase
* or lowercase letter (in the range 'A' ... 'Z' or 'a' ... 'z')
* Postcondition:Afterwards the list will abandon the first occurence of
* the character c from the list or won't change(if c not presented)
* Best case: O(1)(theta(1),when node containing c happens to be the head)
* Worst case: went over the whole list. Theta(n)
* Average: theta(n)
*/
public boolean deleteChar(char c){
if(isEmpty()) return false;
else {
for(DoubleNode current = head; current != null; current = `enter code here`current.getNext()){
if(current.getC() == c){
if(current == head && tail != null){//More than one `enter code here`node and the node to be deleted is the head
head = head.getNext();
head.setPrev(null);
}
if(current == head && tail == null){//There is only `enter code here`one node
head = tail = null;
}
if(current == tail){//More than one node and the `enter code here`node to be deleted is the tail
tail = tail.getPrev();
tail.setNext(null);
}
else{//The node is between two more nodes in the list
DoubleNode previous = current.getPrev();
current.getNext().setPrev(previous);
previous.setNext(current.getNext());
}
}
return true;
}
return false;//After looping still nothing found
}
}
/**Mutator
* Precondition: The character is an uppercase
* or lowercase letter (in the range 'A' ... 'Z' or 'a' ... 'z')
* Postconditions: a character node containing the
* character c will be added to the start
* @param c The character to be added
* Best case and worst case are the same: theta(1)
*/
public void addCharAtFront(char c){
if(this.isEmpty()) {
head = new DoubleNode(null,null,c);
tail = head;
}
else{
DoubleNode newHead = new DoubleNode(null,head,c);
head.setPrev(newHead);
head = newHead;
}
}
/**Count the nodes in the list
* Precondition: The instance used to invoke the method exists
* Postcondition: Return the int number of the node in that
* instance, leaving everything unchanged.
* @return the number of nodes
* Could be no cases(nothing in list)
* If there are node(s)Best and Worst case: theta(n)(go over the list)
*/
public int countNodes(){
int counter = 0;
for(DoubleNode current = this.head; current != null; current = `enter code here`current.getNext()){
counter++;
}
return counter;
}
/**Remove and return the character at the beginning of the doubly linked list.
* Precondition: The instance used to invoke the method exists and
* there is at least one node in the instance(if not, returning ']')
* Postcondition: Remove and return the character at the beginning
* of the doubly linked list
* @return the character stored in the deleted node
* Best and worst case: theta(1)
*/
public char removeCharFromFront(){
char c;
if(isEmpty()) {
System.out.println("Empty list, returning ']'");
return ']';
}
else {
if(head == tail){//There is only one node
c = head.getC();
head = null;
tail = null;
return c;
}
else{
c = head.getC();
head = head.getNext();
head.setPrev(null);// line 156?
return c;
}
}
}
/**Remove and return the character at the end of the doubly linked list
* Precondition: The instance used to invoke the method exists and
* there is node in the instance(if not, returning ']')
* Postcondition: Remove and return the character at the end of the
* doubly linked list.
* @return the character stored in the deleted node
* Best and worst case: theta(1)
*/
public char removeCharAtEnd(){
char c;
if(isEmpty()) {
System.out.println("Empty list, returning ']'");
return ']';
}
else {
if(head == tail){//There is only one node
c = head.getC();
head = null;
tail = null;
return c;
}
else{
c = tail.getC();
tail.getPrev().setNext(null);
tail = tail.getPrev();
return c;
}
}
}
/**Check if the list is empty
* Precondition: The instance used to invoke the method exists
* Postcondition: Returns true if the list is empty false otherwise,
* leaving everything unchanged
* @return true if the list is empty false otherwise
* Best and worst case: Theta(1)
*/
public boolean isEmpty(){
if(head == null)
return true;
else return false;
}
/**Reverse the whole list by changing the pointer
* Precondition: The instance used to invoke the method exists
* Postcondition: The whole list will be reversed
* Could be no cases
* Best and worst is theta(n)
*/
public void reverse(){
if(isEmpty())
System.out.println("No node in the list");
else{
DoubleNode newTail = head;
head = tail;
for (DoubleNode current = tail; current != null; current = `enter code here`current.getPrev()){
current.setNext(current.getPrev());
current.setPrev(current.getNext());
}
tail = newTail;
}
}
/**toString methods overriding the one in java.lang.object
* Precondition: The instance used to invoke the method exists
* Postcondition: return the characters in list as String, leaving
* everything else unchanged.
* @return the String expression of the characters stored in the list
* Best case and worst case are the same: theta(n)
*/
public String toString(){
StringBuffer sb = new StringBuffer();
if(isEmpty())
return new String("No node in the list");
DoubleNode current = new DoubleNode();
for(current = this.head; current != null; current = `enter code here`current.getNext()){
sb.append(String.valueOf(current.getC()));
System.out.println(sb);
}
System.out.println(sb);
return new String(sb);
}
/**Test the DoublyLinkedList class
* @param args command-line arguments
* Initiate two nodes using different constructor. Test the above
* method.
*/
public static void main(String a[]) {
DoublyLinkedList list = new DoublyLinkedList();
list.addCharAtEnd('H');
list.addCharAtEnd('e');
list.addCharAtEnd('l');
list.addCharAtEnd('l');
list.addCharAtEnd('o');
System.out.println(list);
System.out.println("Deleting l");
list.deleteChar('l');
System.out.println(list);
System.out.println("Deleting H");
list.deleteChar('H');
System.out.println(list);
System.out.println("Deleting o");
list.deleteChar('o');
System.out.println(list);
System.out.println("Deleting e");
list.deleteChar('e');
System.out.println(list);
System.out.println("Deleting l");
list.deleteChar('l');
System.out.println(list);
list.addCharAtFront('o');
list.addCharAtFront('l');
list.addCharAtFront('l');
list.addCharAtFront('e');
list.addCharAtFront('H');
System.out.println(list);
System.out.println(list.countNodes());
System.out.println("Popping everything");
while(!list.isEmpty()){
System.out.println(list.removeCharFromFront());//line 294?
}
list.addCharAtFront('o');
list.addCharAtFront('l');
list.addCharAtFront('l');
list.addCharAtFront('e');
list.addCharAtFront('H');
System.out.println("Popping everything from the end");
while(!list.isEmpty()){
System.out.println(list.removeCharAtEnd());
}
System.out.println(list.countNodes());
list.addCharAtEnd('o');
list.addCharAtEnd('l');
list.addCharAtEnd('l');
list.addCharAtEnd('e');
list.addCharAtEnd('H');
list.reverse();
System.out.println(list);
list.reverse();
System.out.println(list);
}
}
public class DoubleNode {
// data field
private DoubleNode p;
private DoubleNode n;
private char c;
/**
* Constructor with parameters to initialize instance variables with given p, c, n
* Preconditions: p, c, n should be casted correctly to be the right instances
* Postcondition: Afterwards generates a new node with double pointers
* Best case and worst case are the same: theta(1)
*/
public DoubleNode(DoubleNode initialp, DoubleNode initialn, char initialc) {
DoubleNode p = initialp;
DoubleNode n = initialn;
char c = initialc;
}
/**Constructor without parameters
* Precondition: Initialize instance variables with unspecified value
* So no pre.
* Postcondition: A new node with null pointers come into existence
* Best case and worst case are the same: theta(1)
*/
public DoubleNode() {
this.p = null;
this.n = null;
}
/**Access to get the reference to previous node in specific node
* @return the reference to the previous node. Null if nothing
* Precondition: the instance used to invoke the method does exist
* Postcondition: Return the previous pointer in current instance while doesn't change the node
* Best case and worst case are the same: theta(1)
*/
public DoubleNode getPrev() {
return p;
}
/**Mutator to change the pointer pointing the previous node
* @param p the value of new previous pointer of that node
* Precondition: the instance used to invoke the method does exist. n is `enter code here`created to be DoubleNode.
* Postcondition: Set the previous pointer in Node while leave other things `enter code here`the same.
* Best case and worst case are the same: theta(1)
*/
public void setPrev(DoubleNode p) {
this.p = p;
}
/**Access to get the reference to next node in specific node
* @return the reference to the next node. Null if nothing
* Precondition: the instance used to invoke the method does exist
* Postcondition: Return the next pointer in current instance while doesn't change the node
* Best case and worst case are the same: theta(1)
*/
public DoubleNode getNext() {
return n;
}
/**Mutator to change the pointer pointing the next node
* @param n the value of new next pointer of that node
* Precondition: the instance used to invoke the method does exist. n is `enter code here`created to be DoubleNode
* Postcondition: Set the next pointer in Node while leave other things the `enter code here`same.
* Best case and worst case are the same: theta(1)
*/
public void setNext(DoubleNode n) {
this.n = n;
}
/**Access to get the character in specific node
* @return the character in the node
* Precondition: the instance used to invoke the method does exist
* Postcondition: Return the character in current instance while doesn't `enter code here`change the node
* Best case and worst case are the same: theta(1)
*/
public char getC() {
return c;
}
/**Mutator to change the character in specific node
* @param c the value to be changed to
* Precondition: the instance used to invoke the method does exist. The `enter code here`character is an uppercase
* or lowercase letter (in the range 'A' ... 'Z' or 'a' ... 'z')
* Postcondition: Reset the character in the Node while leave pointers the `enter code here`same
* Best case and worst case are the same: theta(1)
*/
public void setC(char c) {
this.c = c;
}
/**toString methods overriding the one in java.lang.object
* @return the String expression of the character stored in the node
* Precondition: the instance used to invoke the method does exist
* Postcondition: return the character as String in the node while dosen't `enter code here`change anything else
* Best case and worst case are the same: theta(1)
*/
public String toString() {
return String.valueOf(c);
}
/**Test the DoubleNode class
* @param args command-line arguments
* Initiate two nodes using different constructor. Test the methods above
*/
public static void main(String[] args){
DoubleNode dllNode = new DoubleNode();
StringBuffer test = new StringBuffer();
System.out.println("Test: "+dllNode);
dllNode.setC('H');
dllNode.setNext(null);
dllNode.setPrev(null);
System.out.println("Test2: "+dllNode);
}
}
结果应该是:
你好
删除l
直升机
删除H
ELO
删除o
埃尔
删除e
升
删除l
列表中没有节点
你好
5
弹出一切
^ h
Ë
升
升
Ø
从头到尾弹出一切
Ø
升
升
Ë
^ h
0
你好
olleH
但我无法打印出链表,并且还有如下错误:
Exception in thread "main" java.lang.NullPointerException
at DoublyLinkedList.removeCharFromFront(DoublyLinkedList.java:156)
at DoublyLinkedList.main(DoublyLinkedList.java:294)
答案 0 :(得分:2)
我不会为你解决这个家庭作业问题中的错误,因为如果我给你解决方案你就不会学习,但这里有一些建议:
addCharAtEnd
有注释表明,如果列表不为空,则必须存在多个节点。真的不允许单节点列表吗?isEmpty
的正文更改为return head == null;
。没有理由使用if语句测试布尔表达式,然后返回该表达式的确切值。head
之前的节点是null
,tail
之后的节点是{ {1}},如果A的下一个节点是B,那么B的前一个节点是A),并打印列表中的字符。然后,您应该在每个变异(即更改)列表的方法之后添加对该方法的调用。或者,您可以在调试器中单步调试代码,并在每次操作后检查列表。在交付作业后取出调用,但这样的跟踪打印可能是一种有用的调试技术。当然,有些技术在某些情况下比跟踪打印更好,跟踪打印可以改变系统的行为,但在这种情况下,它完全没问题。第三个是非常重要的,因为你成为一个更好的程序员。能够调试代码,并将您的假设与实际情况进行比较,将帮助您找到许多错误。此外,尝试边缘情况(奇数,奇怪或意外输入)以查看您的代码是否很好地处理它们。您应该研究单元测试框架,例如JUnit,以便几乎自动发现错误。 JUnit是一个很好的框架,甚至内置于Eclipse,使其更加方便。