JAVA中的Linkedlist (链表)

A linked data structure consists of capsules of data known as nodes that are connected via links, Links can be viewed as arrows and thought of as one way passages from one node to another. In Java, nodes are realized as objects of a node class. The data in a node is stored via instance variables. The links are realized as references, A reference is a memory address, and is stored in a variable of a class type, Therefore, a link is an instance variable of the node class type itself

链接的数据结构由称为链接节点的数据封装组成。链接可以看作箭头,可以看作是从一个节点到另一个节点的一种通道。 在Java中,节点被实现为节点类的对象。 节点中的数据通过实例变量存储。 链接被实现为引用,引用是内存地址,并存储在类类型的变量中,因此,链接是节点类类型本身的实例变量

 

/* The output of the program

 

Here are the contents of the list.

32 ---> 12 ---> 18 ---> 48 ---> 32 ---> 7 ---> 4 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

32 ---> 12 ---> 18 ---> 48 ---> 32 ---> 7 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Could not find value 95 in the list; no replacement is possible.

 

Here are the contents of the list.

32 ---> 12 ---> 18 ---> 48 ---> 32 ---> 7 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

32 ---> 12 ---> 18 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

32 ---> 12 ---> 18 ---> 55 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

68 ---> 32 ---> 12 ---> 18 ---> 55 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

68 ---> 32 ---> 12 ---> 44 ---> 55 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 108 ---> X

 

Here are the contents of the list.

68 ---> 32 ---> 12 ---> 44 ---> 55 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 98 ---> 108 ---> X

 

Value 60 was not found in the list. No insertion is possible

 

Here are the contents of the list.

68 ---> 32 ---> 12 ---> 44 ---> 55 ---> 48 ---> 32 ---> 7 ---> 93 ---> 52 ---> 22 ---> 18 ---> 98 ---> 108 ---> X

 

 

 */

 

The first node, or start node in a linked list is called the head node

The entire linked list can be traversed by starting at the head node and visiting each node exactly once

There is typically a variable of the node type (e.g., head) that contains a reference to the first node in the linked list. However, it is not the head node, nor is it even a node. It simply contains a reference to the head node

 

链表中的第一个节点或起始节点称为头节点

通过从头节点开始并精确地访问每个节点一次,可以遍历整个链表

通常存在节点类型(例如head)的变量,其中包含对链表中第一个节点的引用。 但是,它不是头节点,也不是节点。 它仅包含对头节点的引用

 

  • A linked list object contains the variable head as an instance variable of the class
  • A linked list object does not contain all the nodes in the linked list directly

Rather, it uses the instance variable head to locate the head node of the list

The head node and every node of the list contain a link instance variable that provides a reference to the next node in the list

Therefore, once the head node can be reached, then every other node in the list can be reached

 

-链表对象包含变量head作为类的实例变量

-链表对象并不直接包含链表中的所有节点

而是使用实例变量head来定位列表的head节点

头节点和列表中的每个节点都包含一个链接实例变量,该变量提供对列表中下一个节点的引用

因此,一旦可以到达头节点,那么就可以到达列表中的所有其他节点

 

An Empty List Is Indicated by null

  The head instance variable contains a reference to the first node in the linked list. If the list is empty, this instance variable is set to null. Note: This is tested using ==, not the equals method

  The linked list constructor sets the head instance variable to null n This indicates that the newly created linked list is empty

 

head实例变量包含对链表中第一个节点的引用。 如果列表为空,则此实例变量设置为null。 注意:这是使用==而不是equals方法测试的

   链表构造函数将head实例变量设置为nulln这表示新创建的链表为空

 

Indicating the End of a Linked List

The last node in a linked list should have its link instance variable set to null. That way the code can test whether or not a node is the last node

Note: This is tested using ==, not the equals method

链接列表中的最后一个节点应将其链接实例变量设置为null。 这样,代码可以测试一个节点是否为最后一个节点

注意:这是使用==而不是equals方法测试的

 

Traversing a Linked List遍历链接列表

If a linked list already contains nodes, it can be traversed as follows:

  • Set a local variable equal to the value stored by the head node (its reference)
  • This will provides the location of the first node
  • After accessing the first node, the accessor method for the link instance variable will provide the location of the next node
  • Repeat this until the location of the next node is equal to null

 

如果链表已经包含节点,则可以按以下方式遍历它:

-将局部变量设置为等于头节点(其引用)存储的值

-这将提供第一个节点的位置

-访问第一个节点后,链接实例变量的accessor方法将提供下一个节点的位置

重复此操作,直到下一个节点的位置等于null

class Car{



// Attributes

private long serialNum;

private static long serialNumCtr = 3000;



public Car clone()

{

return new Car(this);    // Create and return a new Car

// using the copy constructor

}

}   // end of Car class



class CarList

{

private class Node

{

private Car cr;

private Node next;    // A link to the next node, which is of type Node1

// Default constructors

public Node()

{

cr = null;

next = null;

}

// Parameterized constructor

public Node(Car c, Node xt)

{

cr = c;    // Notice the "=" here. Is there any privacy leak?

// Why not use cr = c.clone();

next = xt;    // Will that insert the right object in the node?

// Will more Cars be created in such case?

}

} // end of inner class Node



// copy constructor

public CarList(CarList lst)

{

if(lst.head == null)

head = null;

else

{

head = null;   

Node t1, t2, t3;    // create 3 temporary pointers

t1 = lst.head;

t2 = t3 = null;

while(t1 != null)   

{

if (head == null)    // this happens only once

{

t2 = new Node(t1.cr, null);                                       

head = t2;

}

else

{

t3 = new Node(t1.cr, null);                                       

t2.next = t3;                                               

t2 = t3;                       

}

t1 = t1.next;

}

t2 = t3 = null;     // t1 is already null by now

}

}

public CarList clone()

{

return new CarList(this);

}



public static void main(String[] args) {

Scanner kb = new Scanner(System.in);

Car c1 = new Car(4, 12000), c2 = new Car(5, 43000), c3 = new Car(),

    c4 = new Car(2, 19000), c5 = new Car(3, 37000), c6 = new Car(2, 52000);



long sn;

// Create two lists

CarList crlst1 = new CarList(), crlst2 = null;

System.out.println("A list has been created. Its current size is: " +

crlst1.size());

System.out.println("Will add few cars to the list.");

crlst1.addToStart(c2);

crlst1.addToStart(c5);

crlst1.addToStart(c3);

crlst1.addToStart(c1);

crlst1.addToStart(c4);

crlst1.addToStart(c6);       

System.out.println("\nThe list current size is: " + crlst1.size());   

crlst1.showListContents();



// Now clone crlst1 to crlst2

crlst2 = crlst1.clone();

System.out.println("\nList has been cloned to another. Here are the

   contents of the cloned list: ");   

crlst2.showListContents();



}



}



Here are the contents of the list.

Car with serial number: 3005 has 2 doors and its price is: 52000.0$. --->

Car with serial number: 3003 has 2 doors and its price is: 19000.0$. --->

Car with serial number: 3000 has 4 doors and its price is: 12000.0$. --->

Car with serial number: 3002 has 4 doors and its price is: 10000.0$. --->

Car with serial number: 3004 has 3 doors and its price is: 37000.0$. --->

Car with serial number: 3001 has 5 doors and its price is: 43000.0$. --->

X



List has been cloned to another. Here are the contents of the cloned list:



Here are the contents of the list.

Car with serial number: 3005 has 2 doors and its price is: 52000.0$. --->

Car with serial number: 3003 has 2 doors and its price is: 19000.0$. --->

Car with serial number: 3000 has 4 doors and its price is: 12000.0$. --->

Car with serial number: 3002 has 4 doors and its price is: 10000.0$. --->

Car with serial number: 3004 has 3 doors and its price is: 37000.0$. --->

Car with serial number: 3001 has 5 doors and its price is: 43000.0$. --->

 

结果只是一个浅复制

class CarList

{

private class Node

{

private Car cr;

private Node next;    // A link to the next node, which is of type Node1

// Default constructors

public Node()

{

cr = null;

next = null;

}

// Parameterized constructor

public Node(Car c, Node xt)

{

cr = c;    // Notice the "=" here. Is there any privacy leak?

// Why not use cr = c.clone();

next = xt;    // Will that insert the right object in the node?

// Will more Cars be created in such case?

}

// A copy constructor

public Node(Node cn)

{

cr = cn.cr.clone();    // Deep copy the Car contents of the node

next = cn.next;        // Is that okay? Would next = null;

// conform to a copy constructor operation?

}



} // end of inner class Node

这样就没有问题了,生成深层拷贝

双链表和循环链表

// *******************************************************************
// LinkedList10.java By: Aiman Hanna (C) 1993 - 2018
// This program introduces doubly linked lists. 
// In fact, the program shows doubly linked lists that are circular as well. 
//
// Key Points:
// 1) Doubly Linked Lists. 
// 2) Circular Linked Lists.  
// *******************************************************************
 
 
import java.util.Scanner;
import java.util.NoSuchElementException;
 
 
 
class List    
{
	// An inner class.
	// Node class. Each node has an integer, a link to the next node (or null) 
	// and a link to the previous node (or null). 
	class Node
	{
		private int x;    
		private Node next;    // A link to the next node
		private Node previous; // A link to the previous node
		// Default constructors 
		public Node()
		{
			x = 0;
			next = null;
			previous = null;
		}
		// Parameterized constructor 
		public Node(int y, Node xt, Node pr)
		{
			x = y;        
			next = xt;
			previous = pr;
		}
		public void setX(int y)
		{
			x = y;        
		}
		public void setNext(Node xt)
		{
			next = xt;
		}
		public void setPrevious(Node pr)
		{
			previous = pr;
		}
		public int getX()
		{
			return x;    
		}
		public Node getNext()
		{
			return next;
		}
		public Node getPrevious()
		{
			return previous;
		}
	}    // end of Node<T> inner class
	private Node head;
	private Node tail;
	int size;
	// Default constructor
	public List()
	{
		head = null;
		tail = null;
		size = 0;
	}
	public int getSize()
	{
		return size;
	}
	// A method that returns the value at a given index 
	public int getItemAt(int index)
	{
		if (index > size -1)
		{
			System.out.println("ERROR: Given index is out of range! Program will terminate. \n");
			throw new NoSuchElementException();
		}
		int i = 0;
		Node temp = head;
		while(i != index)
		{
			temp = temp.next;
			i++;
		}
		return temp.x;
	}
	// A method that replaces the value at a given index 
	public void replaceItemAt(int index, int val)
	{
		if (index > size -1)
		{
			System.out.println("ERROR: Given index is out of range! Program will terminate. \n");
			throw new NoSuchElementException();
		}
		int i = 0;
		Node temp = head;
		while(i != index)
		{
			temp = temp.next;
			i++;
		}
		System.out.println("Replacing value at index # " + index + " from " + temp.x + " to " + val + ". \n");
		temp.x = val;
	}
	// A method that inserts a node at a given index. The old node pointed by index is shifted forward (right) 
	public void insertItemAt(int index, int val)
	{
		if (index > size -1)
		{
			System.out.println("ERROR: Given index is out of range! Program will terminate. \n");
			throw new NoSuchElementException();
		}
		System.out.println("\nInserting new node with value " + val + " at index # " + index + ".");
		int i = 0;
		Node temp = head;
		// Handle the special case when insertion on head
		if (index == 0)
		{
			Node newNode = new Node(val, head, tail);
			head = newNode;
			newNode = null;
		}
		else
		{
			while (i != index -1)    // Stop at the node that precedes index
			{
				temp = temp.next;
				i++;
			}
			// Now we are pointing at the node preceding index
			Node newNode = new Node(val, temp.next, temp);    // next will point to temp.next and 
														// previous will point to temp    
			temp.next.previous = newNode;
			temp.next = newNode;
			newNode = null;
		}
		size++;
	}
		// A method that deletes a node at a given index
		public void deleteItemAt(int index)
		{
			if (index > size -1)
			{
				System.out.println("ERROR: Given index is out of range! Program will terminate. \n");
				throw new NoSuchElementException();
			}
 
			int i = 0;
			Node temp = head;
			// Handle the special case when list has only one node
			if (size == 1)
			{
				System.out.println("\nDeleting the only node of the list at index # " + 0 + ".");
 
				head = tail = null;
				size--;
				return;
			}
			// Handle the special cases when deletion on head or tail
			if (index == 0)
			{
				System.out.println("\nDeleting node with value " + head.x + " from index # " + index + ".");
				head = head.next;
				head.previous = tail;
				tail.next = head;
			}
			else if (index == size-1)
			{
				System.out.println("\nDeleting node with value " + tail.x + " from index # " + index + ".");
				tail = tail.previous;
	tail.next = head;
				head.previous = tail;
			}
			else    // The more general case; deletion from the middle
			{
				while (i != index -1)    // Stop at the node that precedes index
				{
					temp = temp.next;
					i++;
				}
				// Now we are pointing at the node preceding index where deletion should take place
				System.out.println("\nDeleting node with value " + temp.next.x + " from index # " + index + ".");
				temp.next = temp.next.next;
				temp.next.previous = temp;
			}
			size--;
	}
	// A method that adds a node at the end of the list
	public void append(int v)
	{
		if (head == null)    // List is empty
		{
			Node temp = new Node(v, null, null);
			head = temp;
			head.next = head;
			head.previous = head;
			tail = head;
		}
		else
		{
			Node temp = new Node(v, head, tail);    
			head.previous = temp;
			tail.next = temp;
			tail = temp;
			temp = null;
		}
		size++;
	}
 
 
 
	public void showListContents()
	{
		if (size == 0)
		{
			System.out.println("There is nothing to display; list is empty." );
			return;
		}
		System.out.println("List size is: " + size + ". Here are the contents of the list." );
		Node temp = head;
		while(temp != tail)
		{
			System.out.print("" + temp.x  + " <===> ");
			temp = temp.next;        
		}
		// Display that last node
		System.out.println("" + temp.x  + " <===> (Back to First Node)");
	}

 

 

 

Iterators

  A collection of objects, such as the nodes of a linked list, must often be traversed in order to perform some action on each object, An iterator is any object that enables a list to be traversed in this way

  A linked list class may be created that has an iterator inner class n If iterator variables are to be used outside the linked list class, then the iterator class would be made public

  The linked list class would have an iterator method that returns an iterator for its calling object

  Given a linked list named list, this can be done as follows:

LinkedList2.List2Iterator i = list.iterator();

 

 

  The basic methods used by an iterator are as follows:

  restart: Resets the iterator to the beginning of the list

  hasNext: Determines if there is another data item on the list

  next: Produces the next data item on the list

 

迭代器

   为了对每个对象执行某些操作,通常必须遍历对象的集合,例如链表的节点。迭代器是使对象能够以这种方式遍历的任何对象

   可能会创建一个具有迭代器内部类的链表类

  如果要在链表类之外使用迭代器变量,则该迭代器类将被公开

   链表类将具有一个迭代器方法,该方法返回其调用对象的迭代器

   给定一个名为list的链接列表,可以按以下步骤完成:

LinkedList2.List2Iterator i = list.iterator();

 

 

   迭代器使用的基本方法如下:

   restart:将迭代器重置为列表的开头

   hasNext:确定列表上是否还有另一个数据项

   next:产生列表中的下一个数据项

 

// *******************************************************************
// LinkedList9.java By: Aiman Hanna (C) 1993 - 2018
// This program illustrates iterators. The program is indented only  
// to provide an introduction to iterators, so it is kept as simple 
// as possible. You should hence note that there are some obvious 
// situations where this code may misbehave. Look for "WARNING" in the 
// text. This is just an example of such potential misbehavior. 
// You should also look at the output for cases when exceptions will 
// be thrown by the program. 
//
// Key Points:
// 1) Iterators.  
// *******************************************************************
 
 
import java.util.Scanner;
import java.util.NoSuchElementException;
 
 
 
// A generic linked list class that uses the generic Node class
// Notice the bounded use of type T
class List    
{
	// An inner class.
	// Node class. Each node has an integer and a link to the next node (or null). 
	private class Node
	{
		private int x;    
		private Node next;    // A link to the next node
		// Default constructors 
		public Node()
		{
			x = 0;
			next = null;
		}
		// Parameterized constructor 
		public Node(int y, Node xt)
		{
			x = y;        
			next = xt;
		}
		public void setX(int y)
		{
			x = y;        
		}
		public void setNext(Node xt)
		{
			next = xt;
		}
		public int getX()
		{
			return x;    
		}
		public Node getNext()
		{
			return next;
		}
	}    // end of Node<T> inner class
	// An inner iterator class 
	public class ListIterator         // ListIterator is just a name; it can be given a different name 
	{
		// Two attributes. Except for special cases, such as empty list, "current" will always point
		// to a specific location on the list, where various operations will take place. "previous" 
		// points to the node preceding the one pointed by "current" 
		private Node current;
		private Node previous;
		// Resets the pointers
		public void reset()
		{
			current = head;        // This is the attribute "head" of the outer class
			previous = null;
		}
		// Find out if there are more nodes after the one pointed by "current". "current" is allowed 
		// to move to the null pointed by the last node in the list, and so hasNext will still 
		// return true if "current" is pointing to the last node in the list at the time it is called. 
		public boolean hasNext()
		{
			if(current == null)
				return false;        // No next nodes (from the current position) are there in the list 
			else 
				return true;
		}
		// Return the value of the node pointed by "current", then moves both "current" and "previous" 
		// ahead by one node
		public int next()
		{
			if(!hasNext())
			{
				System.out.println("ERROR: Pointing to NULL. No value to return and cannot move forward!
									  Program will terminate. \n");
				throw new NoSuchElementException();
			}
 
			else         // You do not really need that else. You are only here if nothing is thrown
			{
				int val = current.x;
				previous = current;        // Move to the following node 
				current = current.next;        // Move to the following node      
				return val;
			}
		}
		// Return the value of the node pointed by "current", but do NOT move "current" or "previous" 
		public int peek()
		{
			if(!hasNext())
			{
				System.out.println("ERROR: Pointing to NULL. Cannot peek from that node! Program will
									  terminate. \n");
				throw new NoSuchElementException();
			}
			else 
			{
				return current.x;
			}
		}
		// Adds a new node before current. "current" does not move; "previous" moves to point to this new node
		public void addBeforeCurrent(int v)
		{
			if(head == null)    // List is empty
			{
				head = new Node(v, null);
				current = head;
				previous = null;
			}
			else if (current == head)    // current points to the first node in the list
			{
				previous = new Node(v, head);
				head = previous;
			}
			else 
			{
				previous.next = new Node(v, previous.next);
				previous = previous.next;
			}
		}
		// Change the contents of the node pointed by "current"
		public void change(int v)
		{
			if(!hasNext())
			{
				System.out.println("ERROR: Pointing to NULL. Cannot change value! Program will terminate. \n");
				throw new NoSuchElementException();
			}
			else 
			{
				current.x = v;
			}
		}
		// Delete the node pointed by "current"; and move "current" to the next node
		public void delete()
		{
			if(!hasNext())
			{
				System.out.println("ERROR: Pointing to NULL. Cannot delete node! Program will terminate. \n");
				throw new NoSuchElementException();
			}
			if(current == head)        // Case when current is pointing at the first node
			{
				current = current.next;
				head = current;
			}
			else 
			{
				previous.next = current.next;
				current = previous.next;
			}
		}
	}
	private Node head;
	// Default constructor
	public List()
	{
		head = null;
	}
	// Copy constructor -  use the clone() method of the Node class
	// Does this really work? Is the result a deep copy? 
	public List(List lst)
	{
		if(lst == null) throw new NullPointerException();
		if (lst.head == null)
			head = null;
		else
		{
			// Call our copyList() method to copy the list
			head = copyList(lst.head);
		}
	}
	// A method that returns an iterator to for "this" List object
	public ListIterator createIterator()
	{
		return new ListIterator();
	}
	// A method that adds a node to the start of the list
	// WARNING: This method does not manipulate the iterator, and hence 
	// programmers must either reset the iterators when this method is called or
	// write additional code to adjust the iterators 
	public void addToStart(int v)
	{
		head = new Node(v, head);    
	}
 
	// A method that removes a node form the start of the list 
	public boolean deleteFromStart()
	// WARNING: This method does not manipulate the iterator, and hence 
	// programmers must either reset the iterators when this method is called or
	// write additional code to adjust the iterators 
	{
		if (head != null)
		{
			head = head.next;    // Access to inner class private attributes are possible 
			return true;
		}
		else
			return false;
	}
	// A method to return the size of the list
	public int size()
	{
		int ctr = 0;
		Node temp = head;    // Point to the start of the list
		while( temp!= null)
		{
			ctr++;
			temp = temp.next;    
		}
		return ctr;
	}
	// A method that searches the list for a given value and returns the first node that has this 
	// value of null if no node exists with this value
	private Node find(int v)        
	{
		Node temp = head;
		while( temp != null )
		{
			if (temp.x == v)
			{
				return temp;        
			}                        
			temp = temp.next;        // Move temp to the next node
		}
		// If this point is reached then the car was not found in the list
		return null;
	}
 
	// A method that indicates whether or not a a node in the list has some value
	public boolean contains(int v)
	{
		if(find(v) != null)
			return true;
		else 
			return false;
	}
 
 
	public void showListContents()
	{
		Node temp = head;
		if (temp == null)
			System.out.println("There is nothing to display; list is empty." );
		else
			System.out.println("Here are the contents of the list." );
		while(temp != null)
		{
			System.out.print("" + temp.x  + " ---> ");
			temp = temp.next;        
		}
		System.out.println("X");    // Just show "X" indicating the end of the list (null)
	}
	// This clone() method will perform the entire copying operation itself instead of 
	// just calling the copy constructor 
 
	public List clone()
	{
		// First call the clone() method from the Object class. This will 
		// verify whether the class implements the Cloneable interface. If this test 
		// passes, then a copy of the object is returned. However this copy is a 
		// shallow copy, so further operations need to be done after that to create a 
		// deep copy
		try
		{
			List newLst = (List)super.clone();
			if (head == null)
				newLst.head = null;
			else
				// Call our copyList() method to copy the list
				newLst.head = copyList(head);
			return newLst;
		}
		catch (CloneNotSupportedException e)
		{    //This should not happen
			return null;
		}
	}
	// Given a Node pointer that is not null, this method create and return
	// a deep copy of this list pointed by this pointer
	private Node copyList(Node n1)
	{
		Node temp = n1;
		Node newHead = null;
		Node end = null;        // This pointer will always point at the end of the new list 
								// while it is being created (growing)
		newHead = new Node(temp.x, null);        
		end = newHead;
		temp = temp.next;
		while (temp != null)
		{
			end.next = new Node(temp.x, null);
 
			end = end.next;
			temp = temp.next;
		}
		// Now the entire list is created, just return its head pointer
		return newHead;
	}
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值