系列文章目录
01 Java基本数据结构之栈实现
02 Java基本数据结构之队列实现
03 Java基本数据结构之优先级队列
04 Java基本数据结构之链表
如有错误,还请指出
文章目录
前言
链表这一部分内容较多,同时实现起来最开始的时候也是笔者最开始不太理解的,掌握基础链表后,再进一步实现双端链表、有序链表、双向链表以及用链表实现栈、队列。
一、栈(简述)
在链表中,每个数据项都被包含在“链节点”(Node
)中,一个链节点是某个类的对象,这个类可以叫做节点类Node
。每个Node
对象都包含一个对下一个节点引用的字段(通常叫做next
)。此外,链表本身对象中有一个字段指向对第一个链节点的引用。
二、链表实现
后面文章会介绍抽象数据类型
ADT
,明白这个概念更容易理解数据结构。
2.1 节点类
Node
各个字段的存取权限设为public
,如果他们是private
,必须提供公开的方法来访问它们,需要额外的代码。另外,这里假设存储了两种数据,一个 int
类型的数据,一个double
类型的数据,实际英语中,可能包含更多的数据项,通常用一个包含这些数据的类的对象来代替这些数据项。
public class Node {
public int iData; //设为公开,私有则必须提供公开的方法才访问,需要额外代码
public double dData;
public Node next;
//-----------------------------------
public Node(int iData, double dData) {
this.iData = iData;
this.dData = dData;
this.next = null; //可以不用初始化,引用类型对象创建时默认null
}
//-----------------------------------
public void display() {
System.out.println("Node{" + "iData=" + iData + ", dData=" + dData + '}');
}
}
2.2 单链表类
package list;
/*
* 01单链表
* isEmpty
* insertFirst 链表首部插入,新节点称为first节点,指向原来的first节点
* deletFirst first节点的next节点为新节点
* displayList 从first节点开始,打印数据,移动直到 节点的next为null
* 未插入节点时,即first=null,只插入一个节点时,这个节点和first只是一个节点
* 查找和删除指定链节点(包含特定数据)
* find 不是目标节点,找下一个
* delete 找到后再删除节点,将断开前后连接起来,考虑头节点的特殊情况:
* */
public class LinkList {
private Node first; //first 相当于一个指针
public LinkList() {
this.first = null; //可以不用初始化
}
public boolean isEmpty(){
return first==null;
}
//-----------------------------------
public void insertFirst(int iD,double dD){
Node newNode=new Node(iD,dD);
newNode.next=first;
first=newNode;
}
public Node deletFirst() throws Exception {
if (isEmpty()) throw new Exception("链表无节点");
Node temp=first;
first=first.next;
return temp; // 返回删除的头节点
}
public void displayList(){
Node current=first; //临时头节点
while (current!=null){
current.display();
current=current.next;
}
}
// 上述为链表的基本功能,下面增加了两个方法,查找和删除指定节点
//----------------查找和删除指定链节点(包含特定数据)-------------------
// 这里假设查找 iData为目标值的节点
public Node find(int key){
Node tempFirst=first;
while (tempFirst.iData!=key){
tempFirst=tempFirst.next; //重新赋了一个节点的地址
if (tempFirst==null) return null;
}
return tempFirst;
}
public Node delete(int key) throws Exception {
if (isEmpty()) throw new Exception("链表为空");
Node current=first; //当前节点
Node previous = null; // 当前节点的上个节点
while (current.iData!=key){
previous=current;
current=current.next;
if (current==null) return null;
}//找到目标节点
if (current==first) //为 头节点
first=first.next; //first=first.next=null 删除成功
else
previous.next=current.next; //连接断裂
return current;
}
public Node getFirst() {
return first;
}
}
三、注意点
3.1 first 头节点
LinkList
链表类只包含一个数据项:即对链表中第一个链节点的引用,叫做first
。它是唯一的链表需要维护的永久消息,用以定位所有其他的链节点。从 first
出发,沿着链表通过每个链节点的next
字段,就可以找到其他的节点。
构造函数把 first
赋值为 null
,实际上这不是必须的,因为引用类型在创建指出就会自动赋值为null
值。然后构造函数明确说明了赋初值,强调了 first
是怎么开始运作的。当 first
值为null
时,表明链表中没有数据项。如果有,first
字段中应该存有对第一个节点的引用值。isEmpty()
方法就用这种方法来判断链表是否为空。
另外,在笔者看来,first
这个可以理解为实际的节点,也可以理解为一个指针概念,永远指向头节点。
3.2 insertFirst()
在表头插入一个新的节点,这非常容易,只需要使新创建的节点的next
字段等于原来的 first
值,然后改变first
值,让它指向新创建的链接点。
public void insertFirst(int iD,double dD){
Node newNode=new Node(iD,dD);
newNode.next=first;
first=newNode;
}
3.3 deletFirst()
deletFirst()
方法是 insertFirst()
方法的逆操作。通过把 first
重新指向第二个节点,断开了和第一个节点的连接。
public Node deletFirst() throws Exception {
if (isEmpty()) throw new Exception("链表无节点");
Node temp=first;
first=first.next;
return temp;
}
3.4 displayList() 遍历打印链表
为了显示链表,从first
开始,沿着链表从一个节点到另一个节点。变量 current
按顺序指向每一个节点。current
首先指向 first
,然后使用语句 current=current.next;
不断移向下个节点,同时尾节点的next
字段是 null
值,作为结束条件。
public void displayList(){
Node current=first; //临时头节点
while (current!=null){
current.display();
current=current.next;
}
}
3.5 find( key )、delete( key )
find()
方法 很像上述的链表打印方法,通过不断移动节点,在每个节点处检查节点数据项里的关键值是否为目标值,找到了则返回对该节点的引用,反之移动到下个节点直到遍历完。
delete()
方法和 find()
类似,先搜索要删除的节点。然而它需要掌握的不仅是指向当前节点(current
) 的引用,还有指向当前节点的前一个(previous
)节点的引用。因为要删除当前节点,需要把前一个节点和后一个节点连在一起。同时需要注意如果目标节点是头节点,则处理不一样。
// 这里假设查找 iData为目标值的节点
public Node find(int key){
Node tempFirst=first;
while (tempFirst.iData!=key){
tempFirst=tempFirst.next; //重新赋了一个节点的地址
if (tempFirst==null) return null;
}
return tempFirst;
}
public Node delete(int key) throws Exception {
if (isEmpty()) throw new Exception("链表为空");
Node current=first; //当前节点
Node previous = null; // 当前节点的上个节点
while (current.iData!=key){
previous=current;
current=current.next;
if (current==null) return null;
}//找到目标节点
if (current==first) //为 头节点
first=first.next; //first=first.next=null 删除成功
else
previous.next=current.next; //连接断裂
return current;
}
测试
@Test
public void LinkList() throws Exception {
/************测试单链表***********/
LinkList list1=new LinkList();
list1.insertFirst(1,10);
list1.insertFirst(2,20);
list1.insertFirst(3,30);
list1.insertFirst(4,40);
list1.displayList();
System.out.println("删除的节点为:");
list1.deletFirst().display();
list1.deletFirst().display();
System.out.println("当前链表");
list1.displayList();
//
System.out.println("测试指定删除和查找");
list1.find(2).display();
list1.delete(2);
list1.displayList();
}
/*
Node{iData=4, dData=40.0}
Node{iData=3, dData=30.0}
Node{iData=2, dData=20.0}
Node{iData=1, dData=10.0}
删除的节点为:
Node{iData=4, dData=40.0}
Node{iData=3, dData=30.0}
当前链表
Node{iData=2, dData=20.0}
Node{iData=1, dData=10.0}
测试指定删除和查找
Node{iData=2, dData=20.0}
Node{iData=1, dData=10.0}
*/