链表
本章将学习一种全新的数据结构,链表。
链表的 机制灵活,用途广泛,它适用于许多通用的数据库,它也可以取代数组,作为其他存储结构的基础,例如栈、队列等等。除非需要频繁的通过下标随机访问各个数据,否则在很多实用数组的地方都可以用链表代替。
链结点
在链表中,每个数据项被包含在链结点中,一个链结点是某个类的对象,这个类可以叫做 Link ,因为一个链表中有许多类似的链结点,所有有必要用一个不同于链表的类来表达链结点,每一个Link对象都包含对下一个链结点引用的字段,但是链表本身的对象有一个字段指向对第一个链结点的引用。
如下图所示:
对一个链结点的申明:
class Link
{
public int iData; // data item
public double dData; // data item
public Link next; // next link in list
}
这种类定义有时叫做“自引用”式,因为它包含了一个和自己类型相同的字段。
链结点中仅包含两个数据项,一个 int型,一个 double型,但是真正的应用程序中可能包含更多的数据项,通常用这些数据的类对象来代替这些数据项。
单链表
代码实现:
class Link {
public int iData;
public double dData;
public Link next;
public Link(int id, double dd) {
iData = id;
dData = dd;
}
public void displayLink() {
System.out.print("{" + iData + ", " + dData + "} ");
}
}
class LinkList {
private Link first; // ref to first link on list
public LinkList() // constructor
{
first = null; // no links on list yet
}
public boolean isEmpty() // true if list is empty
{
return (first == null);
}
// insert at start of list
public void insertFirst(int id, double dd) { // make new link
Link newLink = new Link(id, dd);
newLink.next = first; // newLink --> old first
first = newLink; // first --> newLink
}
public Link deleteFirst() // delete first item
{ // (assumes list not empty)
Link temp = first; // save reference to link
first = first.next; // delete it: first-->old next
return temp; // return deleted link
}
public void displayList() {
System.out.print("List (first-->last): ");
Link current = first; // start at beginning of list
while (current != null) // until end of list,
{
current.displayLink(); // print data
current = current.next; // move to next link
}
System.out.println("");
}
}
class LinkListApp {
public static void main(String[] args) {
LinkList theList = new LinkList();
theList.insertFirst(22, 2.99); // 插入数据
theList.insertFirst(44, 4.99);
theList.insertFirst(66, 6.99);
theList.insertFirst(88, 8.99);
theList.displayList(); // 输出
while (!theList.isEmpty())
{
Link aLink = theList.deleteFirst();
System.out.print("Deleted ");
aLink.displayLink();
System.out.println("");
}
theList.displayList();
}
}
这里详细介绍两个地方,一个是插入,一个是删除。
在初始化链表的时候,头结点初始化为null,插入新的数据时,新数据的next指向头结点,如果是第一个数据项,则可以看出第一个数据项的next指向null,然后将头结点重新指向当前数据项,如果下次再插入,则新插入的数据的next指向当前的头结点,以此类推。
删除数据时,首先创建一个link对象来保存将要删除的节点,然后将第一个结点后移,即完成删除。
查找和删除指定链结点
代码实现:
class Link
{
public int iData;
public double dData;
public Link next;
public Link(int id, double dd)
{
iData = id;
dData = dd;
}
public void displayLink() // display ourself
{
System.out.print("{" + iData + ", " + dData + "} ");
}
}
class LinkList
{
private Link first;
public LinkList()
{
first = null;
}
public void insertFirst(int id, double dd)
{ // make new link
Link newLink = new Link(id, dd);
newLink.next = first; // it points to old first link
first = newLink; // now first points to this
}
// -------------------------------------------------------------
public Link find(int key) // find link with given key
{ // (assumes non-empty list)
Link current = first; // start at 'first'
while(current.iData != key) // while no match,
{
if(current.next == null) // if end of list,
return null; // didn't find it
else // not end of list,
current = current.next; // go to next link
}
return current; // found it
}
// -------------------------------------------------------------
public Link delete(int key) // delete link with given key
{ // (assumes non-empty list)
Link current = first; // search for link
Link previous = first;
while(current.iData != key)
{
if(current.next == null)
return null; // didn't find it
else
{
previous = current; // go to next link
current = current.next;
}
} // found it
if(current == first) // if first link,
first = first.next; // change first
else // otherwise,
previous.next = current.next; // bypass it
return current;
}
// -------------------------------------------------------------
public void displayList() // display the list
{
System.out.print("List (first-->last): ");
Link current = first; // start at beginning of list
while(current != null) // until end of list,
{
current.displayLink(); // print data
current = current.next; // move to next link
}
System.out.println("");
}
}
class LinkList2App
{
public static void main(String[] args)
{
LinkList theList = new LinkList(); // make list
theList.insertFirst(22, 2.99); // insert 4 items
theList.insertFirst(44, 4.99);
theList.insertFirst(66, 6.99);
theList.insertFirst(88, 8.99);
theList.displayList(); // display list
Link f = theList.find(44); // find item
if( f != null)
System.out.println("Found link with key " + f.iData);
else
System.out.println("Can't find link");
Link d = theList.delete(66); // delete item
if( d != null )
System.out.println("Deleted link with key " + d.iData);
else
System.out.println("Can't delete link");
theList.displayList();
}
}
查找和删除时都是通过不断的向后移动节点,然后比较节点的关键字,不同的是,删除之后,需要对链表做相关的操作,防止链表断掉,如果删除当前节点,应该让前一个节点的next指向挡墙节点的next,而不是指向当前节点。
双端链表
双端链表与传统的链表非常相似,但它有一个新增的特性,对最后一个节点的引用就像对第一个节点的引用一样。
代码实现:
class Link
{
public long dData;
public Link next;
public Link(long d)
{ dData = d; }
public void displayLink()
{ System.out.print(dData + " "); }
}
class FirstLastList
{
private Link first; // ref to first link
private Link last; // ref to last link
public FirstLastList() // constructor
{
first = null; // no links on list yet
last = null;
}
public boolean isEmpty() // true if no links
{ return first==null; }
public void insertFirst(long dd) // insert at front of list
{
Link newLink = new Link(dd); // make new link
if( isEmpty() ) // if empty list,
last = newLink; // newLink <-- last
newLink.next = first; // newLink --> old first
first = newLink; // first --> newLink
}
public void insertLast(long dd) // insert at end of list
{
Link newLink = new Link(dd); // make new link
if( isEmpty() ) // if empty list,
first = newLink; // first --> newLink
else
last.next = newLink; // old last --> newLink
last = newLink; // newLink <-- last
}
public long deleteFirst() // delete first link
{ // (assumes non-empty list)
long temp = first.dData;
if(first.next == null) // if only one item
last = null; // null <-- last
first = first.next; // first --> old next
return temp;
}
public void displayList()
{
System.out.print("List (first-->last): ");
Link current = first; // start at beginning
while(current != null) // until end of list,
{
current.displayLink(); // print data
current = current.next; // move to next link
}
System.out.println("");
}
// -------------------------------------------------------------
}
class FirstLastApp
{
public static void main(String[] args)
{ // make a new list
FirstLastList theList = new FirstLastList();
theList.insertFirst(22); // insert at front
theList.insertFirst(44);
theList.insertFirst(66);
theList.insertLast(11); // insert at rear
theList.insertLast(33);
theList.insertLast(55);
theList.displayList(); // display the list
theList.deleteFirst(); // delete first two items
theList.deleteFirst();
theList.displayList(); // display again
}
}
具体原理看代码实现。
特别注意:双端链表不是双向链表。