目录
链表的定义
链表就像每个节点串起来一样构成了一串,每个节点都是一个节点对象
其中我们对于每个节点会去划分域,分为data域
跟next域
两种.
data代表数值域
,next代表引用
,用于存取下一个节点的地址.
链表的结构
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
单向、双向
带头、不带头
循环、非循环
单向组合起来一共有以下几种:
单向、带头、循环、
单向、不带头、非循环 (面试中经常考到的,也是我们重点掌握内容
)
单向、带头、非循环
单向、不带头、循环
单向不带头非循环链表(重点掌握,面试常考)
在单向不带头非循环链表中,第一个节点我们称之为头节点
,最后一个节点我们称之为尾节点
同时单向链表的尾节点的next域为null
,因为尾结点不再指向任何一个结点
同时我们可以发现链表上的地址不同于顺序表的是,链表的地址并不是连续的,链表中节点的相连是通过next域来实现的
单向带头非循环链表
在单向有头非循环链表中,我们是用一个专门的节点
来作为头节点
的,这个节点没有data这个数值域
,只有next指针域
,这个指针域用来存储我们第一个数据节点的地址,且这个头节点是无法删除的,它就像一个哨兵,指向我们第一个数据节点。
单向带头循环链表
自己实现一个链表(一定自己动手实现五遍以上)
单向不带头非循环链表(重点)
接口(方法)
//头插法(优化和非优化方法)
public void addFirst(int data);
//尾插法(优化和非优化方法)
public void addLast(int data);
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data);
//查找关键字key是否在单链表当中
public boolean contains(int key);
//删除第一次出现关键字为key的节点
public void remove(int key);
//得到单链表的长度
public int size();
//遍历输出链表
public void display();
//清空单链表
public void clear();
//找到链表的最后一个节点
public void findLastNode();
//找到链表的倒数第二个节点
public void findLastTwoNode();
//找到链表的第n个节点
public Node findNumber(int n);
代码示例
/**
* @author SongBiao
* @Date 2021/1/18
* 自己实现一个单向无头非循环链表
*/
//Node类适用于创建每个单个的节点对象
class Node {
//代表我们的数值域
public int data;
//next是用来存取下一个节点对象的地址的,所以是Node类型,说明其也是一个引用
public Node next;
public Node() {
}
public Node(int data) {
this.data = data;
}
}
public class MyLinkedList {
//head表示单链表的头,默认值为null,因为此时head这个头指针不指向我们的头节点
public Node head;
//在进行头插法之前,首先要进行链表的创建
//其次在创建的过程中,我们还要将获取到的头节点的地址赋值给head
public void createLinkList() {
//将第一个节点的地址赋给我们head
this.head = new Node(10);
Node node2 = new Node(20);
Node node3 = new Node(30);
Node node4 = new Node(40);
Node node5 = new Node(50);
this.head.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
}
//遍历输出链表
public void display() {
Node cur = this.head;
while (cur != null) {
System.out.println(cur.data);
cur = cur.next;
}
System.out.println();
}
//通过遍历,找到链表最后一个节点
//注意要考虑到链表为空的情况
public Node findLastNode() {
if (this.head == null) {
System.out.println("链表是空的,根本没有最后一个节点");
return null;
}
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
return cur;
}
//通过遍历,找到链表的倒数第二个节点
//注意要考虑到只有一个节点和链表为空的情况
public Node findLastTwoNode() {
if (this.head.next == null) {
System.out.println("只有一个节点");
return null;
}
if (this.head == null) {
System.out.println("一个节点都没有");
return null;
}
Node cur = this.head;
while (cur.next.next != null) {
cur = cur.next;
}
return cur;
}
//得到单链表的长度
public int size() {
int count = 0;
Node cur = this.head;
if (cur == null) {
return -1;
}
while (cur != null) {
cur = cur.next;
count++;
}
return count;
}
//找到链表的第n个节点
//同样需要判断链表为空和所找的第n个节点是否超过了当前链表的长度
public Node findNumber(int n) {
if (this.head == null) {
System.out.println("一个节点都没有");
return null;
}
if (n <= 0) {
System.out.println("根本不存在这个节点");
return null;
}
if (n > size()) {
System.out.println("所要找的节点超过了链表的长度");
return null;
}
Node cur = this.head;
int count = 1;
while (count != n) {
cur = cur.next;
count++;
}
return cur;
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key) {
Node cur = this.head;
while (cur != null) {
if (cur.data == key) {
return true;
}
cur = cur.next;
}
//找不到返回false
return false;
}
//头插法非优化方法
/*
分为三步:
第一步:首先的有一个节点
第二步:判断链表是否为空
第三步:如果不为空,进行插入
*/
public void addFirst(int data) {
//创建一个要插入的节点,其next域默认为null
Node node = new Node(data);
if (this.head == null) {
this.head = node;
} else {
//注意在任何情况下往链表里面插入数据的时候,先绑定后面,
node.next = this.head;
this.head = node;
}
}
//头插法的优化
public void addFirst1(int data) {
//创建一个要插入的节点,其next域默认为null
Node node = new Node(data);
//优化之后可以去掉判断空的情况
node.next = this.head;
this.head = node;
}
//尾插法非优化方法
/*
1:创造一个要插入的节点
2:判断此时要插入的链表是否为空,如果为空,就直接插入
3:非空的话就正常插入
*/
public void addLast(int data) {
Node node = new Node(data);
if (this.head == null) {
this.head = node;
} else {
//获取到当前非空链表的头节点
Node cur = this.head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
//该函数是为了找到index-1位置的节点的引用
//index是下标的意思,第三个节点的index值为2
//由于链表底部不是数组,没有下标,所以这个下标是我们自己定义的,方便我们自己使用
//例如此时我要在第三个节点处插入数据,此时这个节点index=2
//在第三个节点插入数据,就应该去寻找第二个节点是谁,而第二个节点的index=1
//所以说moveIndex方法的目标是寻找到所插入节点的位置的前一节点
public Node moveIndex(int index) {
if (index <= 1) {
System.out.println("寻找的位置出错");
return null;
}
if (index > size() + 1) {
System.out.println("所找的位置大了");
return null;
}
Node cur = this.head;
int count = 0;
while (count != index - 1) {
cur = cur.next;
count++;
}
return cur;
}
//任意位置插入,第一个数据节点为0号下标
//注意插入的时候不管什么插入,一定是先考虑后面插入,在考虑前面插入
public void addIndex(int index, int data) {
if (index < 0 || index > size()) {
System.out.println("所插入的位置不合法");
/*
return;这个语句一般出现在一个无返回值的函数里面,
函数里面可以有很多行,有的时候需要程序运行到某一行就要停止退出本函数,就需要用到return;语句
*/
return;
}
//index=0的时候为头插法
if (index == 0) {
addFirst1(data);
return;
}
//index等于链表长度的时候为尾插法
if (index == size()) {
addLast(data);
return;
}
//创建一个要插入的新的节点
Node node = new Node(data);
//查找到要插入的节点位置的前一节点
Node cur = moveIndex(index);
node.next = cur.next;
cur.next = node;
}
//删除第一次出现关键字为key的节点
/*
1:首先需要判断所删除的节点是否存在于链表中,可根据所要删除节点的前驱节点是否存在来判断
如果存在,说明在链表中,反之亦然。
2: 还要判断链表是否为空,如果为空,那么直接执行return;语句
3:如果存在于链表中,那么就去找所要删除节点的前驱
4:假设我们要删除的节点为del,此节点的前驱为cur,那么删除del这个节点的核心语句为
cur.next=del.next
5:
当删除的节点为首节点的时候,首节点是没有前驱的,cur不存在
所以必须进行独立的判断:
if(this.head.data==key){
this.head=this.head.next;
}
*/
public void remove(int key) {
//判断链表是否为空
if (this.head == null) {
return;
}
if (this.head.data == key) {
this.head = this.head.next;
return;
}
//找到所要删除的节点的前驱节点
Node pre = searchPre(key);
//如果找不到所要删除的节点的前驱节点,说明要删除的这个节点根本不存在
if (pre == null) {
System.out.println("根本没有这个节点的前驱");
} else {
//将所找到的key值对应的节点赋给cur这个引用
Node cur = pre.next;
pre.next = cur.next;
}
}
//找到存储关键字key这个节点的前驱节点
/*找到关键key的前驱,如果有返回前驱节点的引用
如果没有返回null*/
public Node searchPre(int key) {
Node cur = this.head;
//注意循环条件为cur.next,如果是cur,相当于遍历完成,但是尾结点不可能是其他节点的前驱,
//所以此处应该是走到最后一个节点时循环就应该终止了。
while (cur.next != null) {
if (cur.next.data == key) {
return cur;
}
cur = cur.next;
}
return null;
}
//清空链表
public void clear() {
this.head = null;
}
public static void main(String[] args) {
}
public static void main1(String[] args) {
//new Node()代表实例化了一个节点对象,其地址存储在node这个引用当中
Node node = new Node();
System.out.println(node.data);
System.out.println(node.next);
System.out.println("=========");
Node node1 = new Node(10);
System.out.println(node1.data);
System.out.println(node1.next);
}
}