首先我们先了解一下什么是链表:
链表是一种数据结构,它是一种物理存储单元上非连续,非顺序的存储单元,只是在逻辑上是连续的,链表逻辑形态和铁链相识,链表是由一系列结点组成,结点在运行时动态生成,每个结点由两部分组成及:存储数据元素的数据域,和存储下一个结点的地址的指针域,链表有易于插入,删除,存储空间灵活等优点,但是不允许随机存取。
回归正题,我们来了解一下什么是单向链表:
单向链表是指每个结点只含有一个指针域,即构成链表的结点只有一个指向后一个结点的指针域,并且尾结点的指针为null。下面我画一个单向链表结点构成图帮助我们更好理解:
注:
头指针:1.在线性表的链式存储结构中,头指针是指向第一个结点的指针,假如链表有头结点,则头指针就是指向链表头节点的指针
2.头指针具有标识作用,常用头指针冠以链表的名字
3.头指针是指针,不是结点,没有数据域。
4.无论链表是否为空,头指针均不为空。头指针是链表必需的元素。可以理解为头指针是存储在栈中的地址,用来访问链表。
头结点:1. 头结点是为了操作方便和统一设置的,放在第一个有数据元素的结点之前,其数据域一般无意义(有些情况下也可以用来存放链表长度,用作监视哨等)
2.首元结点也就是第一个有数据元素的结点,是头结点后面第一个结点
3.头结点不是链表必须的,头结点不算在链表长度中。
因为链表在物理存储结构上不连续,只是逻辑是连续。而头指针很大一部分意义在于,我们访问链表时需要知道链表由何处开始访问,从而由头指针指向头结点(如果没有则指向第一个有数据元素的结点),每个结点的指针依次指向下一个结点,这样可以通过指针和给定下标找到需要的结点。所以说头指针是必须的元素,没有头指针无法访问链表。
好了,单链表粗略的介绍完了,现在我们开始自己定义写一个单链表,我们以int数据类型为列:
1.我们知道链表都是由一个个结点组成;所以首先我们需要一个结点类,以定义数据类型和指针域,结点类代码如下:
public class Node {
//数据
int data;
//下一个节点
Node next;
Node before;
//创建一个无参数构造方法,用于初始化
public Node(){
}
//创建一个有参数构造方法,便于给结点传数据
public Node(int data){
this.data=data;
}
}
2,接口类,我们在接口类中定义我们想要这个链表实现什么功能的方法;如删除,添加,修改插入等方法,代码如下
public interface LinkList {
//存储
public void add(int e);
//插入
public void insert(int e,int index);
//删除
public void delete(int index);
//获取
public int get(int index);
public void set(int data);
//修改
public void updata(int index,int newValue);
//获取元素个数
public int getSize();
public Node getNext();
public void setNext(Node next);
}
3.链表类,我们在链表类中继承接口类并且重写链表类中所有的方法,定义链表的3个重要属性:头结点,尾节点,链表长度(元素个数)
首先我们分析一下链表删除功能的方法,要实现删除功能我们只需要找到我们要删除的结点,我们通过下标遍历找到这个结点的上一个结点,断开上一个结点和要删除结点的指针连接,让上一个结点的指针指向要删除结点的下一个结点,置空要删除结点的数据元素域和指针域,如图所示:
代码如下:
public void delete(int index) {
check(index);
// 下标为index的节点
Node node1 = head.next;
if (index != 0) {
for (int i = 0; i < index; i++) {
node1 = node1.next;
}
// 下标为index-1的节点,index的前一个节点
Node node2 = head.next;
for (int i = 0; i < index - 1; i++) {
node2 = node2.next;
}
if (index != size - 1) {// 如果不是最后一个节点
Node node3 = node1.next;// index的下一个节点
node2.next = node3;// 将前一个节点指向后一个节点
node1.next = null;// 断开前一个节点和index节点
} else {
node1.next = null;// 断开前一个节点和index节点
}
node1 = null;
// node1.next=null;
} else {
Node node6 = head.next;
Node node7 = node6.next;
head.next = null;
head.next = node7;
node6.next = null;
}
size--;
}
插入方法:创建一个要插入的结点。找到要插入结点位置index和前一结点位置index-1 。若要插入点是最后一个,直接将原最后结点指向新加入结点。若不是,置空index-1的结点指针,再设置指向要加入结点,设置要加入结点指针指向原index结点。
代码如下:
public void insert(int e, int index) {
check(index);
Node node1 = head.next;
for (int i = 0; i < index; i++) {
node1 = node1.next;// 找到index节点
}
Node node2 = new Node(e);
if (index == 0) {// 如果插在最前面
head.next = null;// 断开头结点和第一个节点
head.next = node2;// 头结点指向第一个节点
node2.next = node1;// 新节点指向原来第一节点
} else {
Node node3 = head.next;
for (int i = 0; i < index - 1; i++) {
node3 = node3.next;
}
node3.next = null;
node3.next = node2;
node2.next = node1;
}
size++;
}
添加结点的方法:代码如下
public void add(int e) {
// 创建一个新节点
// next=new Node(obj);
// if(size==0){
// // root.
// }
Node node = new Node(e);
// 尾节点的下一个节点,设为新节点
tail.next = node;
// 修改尾节点变为新节点
tail = node;
// 修改元素个数
size++;
}
其他方法不再细讲,留给读者,下面给出我的整个链表类的代码:
import java.util.ArrayList;
public class Jiedian implements LinkList {
// Node root=new Node();
Node head = new Node();// 头节点
// 尾节点
Node tail;
// 元素个数
// Node next;
int size;
private int data;
public Jiedian() {
// 初始化头结点和尾节点
head = new Node();
tail = head;
size = 0;
}
public void check(int index) {
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException("参数不对");
}
}
public void add(int e) {
// 创建一个新节点
// next=new Node(obj);
// if(size==0){
// // root.
// }
Node node = new Node(e);
// 尾节点的下一个节点,设为新节点
tail.next = node;
// 修改尾节点变为新节点
tail = node;
// 修改元素个数
size++;
}
public void insert(int e, int index) {
check(index);
Node node1 = head.next;
for (int i = 0; i < index; i++) {
node1 = node1.next;// 找到index节点
}
Node node2 = new Node(e);
if (index == 0) {// 如果插在最前面
head.next = null;// 断开头结点和第一个节点
head.next = node2;// 头结点指向第一个节点
node2.next = node1;// 新节点指向原来第一节点
} else {
Node node3 = head.next;
for (int i = 0; i < index - 1; i++) {
node3 = node3.next;
}
node3.next = null;
node3.next = node2;
node2.next = node1;
}
size++;
}
public void delete(int index) {
check(index);
// 下标为index的节点
Node node1 = head.next;
if (index != 0) {
for (int i = 0; i < index; i++) {
node1 = node1.next;
}
// 下标为index-1的节点,index的前一个节点
Node node2 = head.next;
for (int i = 0; i < index - 1; i++) {
node2 = node2.next;
}
if (index != size - 1) {// 如果不是最后一个节点
Node node3 = node1.next;// index的下一个节点
node2.next = node3;// 将前一个节点指向后一个节点
node1.next = null;// 断开前一个节点和index节点
} else {
node1.next = null;// 断开前一个节点和index节点
}
node1 = null;
// node1.next=null;
} else {
Node node6 = head.next;
Node node7 = node6.next;
head.next = null;
head.next = node7;
node6.next = null;
}
size--;
}
public int get(int index) {
Node node = head.next;
// 从头节点开始,依次向后查找
for (int i = 0; i < index; i++) {
node = node.next;
}
return node.data;
}
public void updata(int index, int e) {
Node node1 = head.next;
for (int i = 0; i < index; i++) {
node1 = node1.next;
}
node1.data = e;
}
public int getSize() {
// TODO Auto-generated method stub
return size;
}
public static void main(String[] args) {
// 创建链表
Jiedian kk = new Jiedian();
// 添加元素
kk.add(55);
kk.add(89);
kk.add(18);
kk.add(92);
kk.add(562);
kk.add(782);
kk.add(45);
kk.delete(4);
kk.insert(666, 3);
kk.updata(4, 5555);
for (int i = 0; i < kk.getSize(); i++) {
int e = kk.get(i);
System.out.println("第" + i + "个元素是:" + e);
}
}
写到这就收笔了,也请各位技术大佬多多指正,