在数据结构中,链表是基本一个的数据结构,所以我们要能够实现一个简单的单链表以及对链表的基本操作要十分熟悉,才能在笔试或者面试的时候更有把握!
如上图所示的是一个单链表的存储原理图,head为头节点。而每个节点中都有一个next引用,指向下一个节点,就这样一节一节往下面记录,直到最后一个节点,最后一个节点的next指向null 。而我们学习数据结构的链表时,使用的是如下图所示的虚拟头结点(head),即不存放任何的数据,只是充当一个指向链表中真正存放数据的第一个节点的作用。
实现单链表的思路:
1)链表类,结点类(链表类的内部类),创建一条链表类对象,通过方法逐步创建结点类,通过引用链接起来成为链表。
2)结点类包含数据和对下个结点的引用,以及可以对数据赋值的构造函数。
3)链表类的构造方法,只构造出不含数据的头结点。
节点类
链表是由一个个节点连接形成的,所以要先定义好节点类,节点类主要分为两块内容就是数据和next指针,定义可以参看下面代码:
/**
*
* 单向链表的节点类
*/
private class Node {
E data;// 数据域
Node next;// 指针域
public Node() {
this(null, null);
}
public Node(E data, Node next) {
this.data = data;
this.next = next;
}
@Override
public String toString() {
return data.toString();
}
}
接下来,我们就可以对链表的各种操作进行定义了:
给链表添加节点
注意:对于插入节点常用的思想主要有头插法 和 尾插法,在这里我们的代码都将它们实现了。
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("插入角标异常");
}
Node n = new Node(e, null);
if (index == 0) {// 头插
n.next = head.next;
head.next = n;
if (size == 0) {
rear = n;
}
} else if (index == size) {// 尾插
rear.next = n;
rear = rear.next;
} else {
Node p = head;
for (int i = 0; i < index; i++) {
p = p.next;
}
n.next = p.next;
p.next = n;
}
size++;
}
public E getFirst() {
return get(0);
}
public E getLast() {
return get(size - 1);
}
增加节点到链表指定的位置
对于传入的index需要先判断是否合法,然后通过for循环的遍历寻找index的位置来进行节点的插入。
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("修改角标非法");
}
if (index == 0) {
head.next.data = e;
} else if (index == size - 1) {
rear.data = e;
} else {
Node p = head;
for (int i = 0; i <= index; i++) {
p = p.next;
}
p.data = e;
}
}
返回链表的长度与判空
//获取链表的长度
public int getSize() {
return size;
}
//判空
public boolean isEmpty() {
return size == 0 && head.next == null;
}
删除链表的节点
//正常删除
public E remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("删除角标非法");
}
E res=null;
if(index==0) {//头删
Node p=head.next;
head.next=p.next;
res=p.data;
p.next=null;
p=null;
if(size==1) {
rear=head;
}
}else if(index==size-1) {//尾删
Node p=head;
res=p.data;
while(p.next!=rear) {
p=p.next;
}
p.next=null;
rear=p;
}else{
Node p=head;
for(int i=0;i<index;i++){
p=p.next;
}
Node del=p.next;
res=del.data;
p.next=del.next;
del.next=null;
del=null;
}
size--;
return res;
}
//删除头结点
public E removeFirst() {
// TODO 自动生成的方法存根
return remove(0);
}
//删除尾节点
public E removeLast() {
// TODO 自动生成的方法存根
return remove(size-1);
}
//删除指定元素
public void removeElement(E e) {
int index=find(e);
if(index==-1){
throw new IllegalArgumentException("元素不存在");
}
remove(index);
}
清空整个链表
public void clear() {
head.next=null;
rear=head;
size=0;
}
遍历链表(即重写toString()方法对链表进行遍历)
@Override
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append("LinkedList:size="+getSize()+"\n");
if(isEmpty()){
sb.append("[]");
}else{
sb.append("[");
Node p=head;
while(p.next!=null){
p=p.next;
if(p==rear){
sb.append(p.data+"]");
}else{
sb.append(p.data+",");
}
}
}
return sb.toString();
}
测试:
//测试程序
public class Main {
public static void main(String[] args) {
LinkedStack<Integer> list=new LinkedStack<Integer>();
for(int i=1;i<=10;i++){
list.push(i);;
}
System.out.println(list);
System.out.println(list.peek());
for(int i=1;i<=5;i++){
list.pop();
}
System.out.println(list);
}
}
测试结果:
基本的单链表操作就是上面这些了,我们实现单链表首先是为了理解链表的原理,同时也是为了后续对链式栈、链式队列、循环链表等数据结构的实现奠定基础。