单链表
2.3线性表的链式表示和实现
2.3.1线性表的链式表示
线性表的链式结构是用一组任意的存储单元来存放线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。
对于每个数据元素ai,除了存储其本身的信息之外,还需存储一个指示其直接后继存放位置的指针。这两部分信息组成数据元素ai的存储映像,称为节点。
单链表节点结构:数据域|指针域,其中存储数据元素信息的域称为数据域,存储直接后继存放位置的域称为指针域。
结点类的泛型定义如下,数据域为data,指针域为next。构造器有两个,二者的区别是参数个数不同。
有一个参数的构造器,用参数n来初始化next指针域,数据域不存储有效的用户数据。
有两个参数的构造器,根据形参obj和n分别初始化数据域data和指针域next。
public class Node<T> {
T data;
Node<T> next;
public Node(Node<T> n) {
next=n;//参数n来初始化指针域
}
public Node(T obj,Node<T> n) {
data=obj;//obj来初始化数据域data
next=n;//n来初始化指针域next
}
public T getData() {
return data;
}
public Node<T> getNext(){
return next;
}
}
2.3.2单链表的实现
线性表链式存储结构泛型类的定义如下,在linkList类中有两个成员变量。一个指向头节点的指针head,习惯称head为头指针;另外一个是length,用来存放单链表的长度。
线性表的单链表存储结构描述:
public class linkList<T> {
private Node<T> head; //头指针
private int length; //单链表长度
public linkList() {} //构造一个空链表,并进行初始化
public Node<T> getHead(){} //获取链表头节点位置
public boolean add(T obj,int pos) {} //在链表中插入一个新元素
public T remove(int pos) {} //删除链表中某个元素
public T value(int pos) {} //获取链表中一个元素
public int find(T obj) {} //在链表中查找一个元素
public boolean modify(T obj,int pos) {} //更新链表中的某个元素
public boolean isEmpty() {} //判空
public int size() {} //求链表中数据元素的个数
public void nextOrder() {} //依次访问链表中每个元素并输出
public void clear() {} //清除链表
}
1.单链表的初始化
//构造一个空链表,并进行初始化
public linkList() {
length=0;
head=new Node<T>(null);
}
2.获取链表某个结点的位置
//获取链表头节点位置
public Node<T> getHead(){
return head;
}
3.单链表的插入
单链表的插入是指在单链表的第pos-1个结点和第pos个结点之间插入一个新的结点,要实现结点的插入,需要修改插入位置之前的结点和当前插入位置结点的指针指向。
在单链表的插入操作中,首先需要检验参数pos的合法性,当1<=pos<=length时,初始化num=1,变量引用单链表中头结点之后的第一个结点。
//在链表中插入一个新元素
public boolean add(T obj,int pos) {
if(pos<1||pos>length) {
System.out.println("pos is valid!");
return false;
}
else {
int num=1;
Node<T> p=head,q=head.next;
while (num<pos) {
p=q;
q=q.next;
num++;
}
p.next=new Node<T>(obj,q);
length++;
return true;
}
}
因为链表不具备随机访问的特点,所以在插入操作之前,要从单链表的头结点开始,顺序扫描每个结点并计数,从而找到插入位置,即第pos个结点,时间主要用来查找插入位置,该算法的时间复杂度为O(n)。
4.单链表的删除
单链表的删除是指删除单链表的第pos个结点,要实现该结点的删除,可将删除位置之前的结点,即第pos-1个结点的指针域指向第pos+1个结点。
单链表进行操作的前提时链表不为空,条件满足之后,分别找到要删除的pos个结点,通过q引用,和被删除结点的前一个结点(通过变量p引用),修改p引用结点指针域的值即可以完成删除操作,p.next=q.next,删除完成之后,链表的长度减一,并返回被删除结点数据域的值。
//删除链表中某个元素
public T remove(int pos) {
if(isEmpty()) {
System.out.println("the linklist is empty!");
return null;
}
else {
if(pos<1||pos>length) {
System.out.println("pos is valid!");
return null;
}
int num=1;
Node<T> p=head,q=p.next;
while(num<pos) {
p=q;
q=q.next;
num++;
}
p.next=q.next;
length--;
return true;
}
}
和插入操作类似,该算法的时间复杂度为O(n)。
5.单链表的查找
单链表的查找思路和顺序表类似,值得注意的是:由于构建的是带有头结点的单链表,所以首先变量p引用的是头结点之后的结点,当该链表结点不存在时链表为空。通过调用方法equals来判断两个对象的值是否相等,查找成功返回对象obj在单链表中的位序。
public int find(T obj) {
if(isEmpty()) {
System.out.println("linklist is empty!");
return -1;
}
else {
int num=1;
Node<T> p=head.next; //p引用的是头结点之后的结点
while(p!=null) { //单链表的判空
if(p.data.equals(obj)==false) { //equals判断两个对象值是否相等
p=p.next;
num++;
}
else break;
}
if(p==null) {
return -1;
}
return num;
}
}
6.获取单链表的第pos个结点的值
首先链表不能为空,获取第pos个结点地址,通过变量p引用该结点,返回p.data即可。
算法如下:
//获取链表中一个元素
public T value(int pos) {
if(isEmpty()) {
System.out.println("linklist is empty!");
return null;
}
else {
if(pos<1||pos>length+1) {
System.out.println("pos is valid!");
return null;
}
else {
int num=1;
Node<T> q=head.next;
while(num<pos) {
q=q.next;
num++;
}
return q.data;
}
}
}
7.更新单链表的第pos个结点的值
首先检查参数pos的合法性,当1<=pos<=length时,用参数obj的值替换顺序表中第pos个元素,当pos取值超出合法范围时,算法返回false,表示修改失败.
算法如下:
//更新链表中的某个元素
public boolean modify(T obj,int pos) {
if(isEmpty()) {
System.out.println("linklist is empty!");
return false;
}
else {
if(pos<1||pos>length+1) {
System.out.println("pos is valid!");
return false;
}
else {
int num=1;
Node<T> q=head.next;
while(num<pos) {
q=q.next;
num++;
}
q.data=obj;
return true;
}
}
}
8.判断单链表是否为空
根据单链表中表示长度的length的取值进行判断,当单链表的长度为0时则表示链表为空,否则不为空.
//判空
public boolean isEmpty() {
return length==0;
}
9.求单链表的长度
返回当前单链表的长度即length值.
//求链表中数据元素的个数
public int size() {
return length;
}
10.正序输出单链表中所有的元素
按照逻辑次序依次访问单链表中头结点之后的每个结点,并输出该结点数据域的值,算法如下:
//依次访问链表中每个元素并输出
public void nextOrder() {
Node<T> p=head.next;
while(p!=null) {
System.out.println(p.data);
p=p.next;
}
}
11.清空链表
把length值设置为0,并且把头结点指针域置为空,
public void clear() {
length=0;
head.next=null;
}
Test:主函数调试
public class linkListTest {
public static void main(String[] args) {
linkList<Integer> L = new linkList<Integer>();
int i;
int a[] = {23,56,12,49,35};
for(i=0;i<a.length;i++) {
L.add(a[i], i+1);//将数组中各元素插入到单链表中
// System.out.println(i+1);
// System.out.println(a[i]);
}
L.nextOrder();//依次访问链表中的每个元素
System.out.println("------1------");
L.add(90, 4);//在第四个结点插入90
L.nextOrder();
System.out.println("------2------");
L.remove(1);//删除第一个结点的数据元素
L.nextOrder();
System.out.println("------3------");
System.out.println(L.find(35));//查找35在链表中的位序
System.out.println("------4------");
L.modify(100, 1);//修改第一个结点的数据元素为100
L.nextOrder();
System.out.println("------4------");
System.out.println(L.size());//输出单链表的长度
L.clear();
System.out.println(L.isEmpty());
System.out.println(L.getHead());
}
}