文章目录
- 前言
- 1. 链表
- 1.1 什么是链表?
- 1.2 链表的分类
- 2. 链表方法的实现
- 2.1 实现构建思想
- 2.2 代码实现
- 2.2.1 实现方法前的准备工作
- 2.2.2 链表方法:display() - 打印链表, contains() - 查找链表中key值, size() - 求链表长度
- 2.2.3 头插法-addFirst(), 尾插法-addLast()
- 2.2.4 addIndex-插入任意位置
- 2.2.5 remove()-删除第一次遇到的值key,removeAllKey()-删除所有的值key
- 总结
✨✨✨学习的道路很枯燥,希望我们能并肩走下来!
编程真是一件很奇妙的东西。你只是浅尝辄止,那么只会觉得枯燥乏味,像对待任务似的应付它。但你如果深入探索,就会发现其中的奇妙,了解许多所不知道的原理。知识的力量让你沉醉,甘愿深陷其中并发现宝藏。
前言
本篇通过写自己代码书写一些常用的方法,熟悉方法的使用,以至于使用方法时能够更好的操作。如有错误,请在评论区指正,让我们一起交流,共同进步!
本文开始
1. 链表
1.1 什么是链表?
链表:由一个一个节点组成,每个节点分为数据域(储存数据)和地址域(储存地址),连接节点是通过他们的地址。
链表特点:物理上不连续(与顺序表不同空间不是连续的),逻辑上连续;
1.2 链表的分类
链表分类图:
2. 链表方法的实现
2.1 实现构建思想
构建 MyLinkedList 链表(类),每个链表中一定有节点,而节点在Java中是类,每个类中赋值val,和记录下一个节点的地址next;定义一个head代表头节点的引用,从而操作链表;再在链表中实现常用的方法。
2.2 代码实现
2.2.1 实现方法前的准备工作
首先定义一个类MyLinkedList,在其中定义一个节点类Node;
再定义一个head节点,表示头节点的引用;
为什么使用再定义head : 操作实现链表的时候,我们都从head 开始 遍历至结束。
定义节点时为什么使用static?
这样在操作节点LinkNode时不需要依赖于对象(不需要new对象), 直接类名调用即可;
public class MyLinkedList {
static class LinkNode {
public int val;
public LinkNode next;
public LinkNode(int val) {
this.val = val;
}
}
public LinkNode head; //当前链表头结点的引用
}
2.2.2 链表方法:display() - 打印链表, contains() - 查找链表中key值, size() - 求链表长度
遇到的问题:
遍历列表:涉及循环,为什么不使用head来遍历呢?、
定义变量 cur 来遍历,当head遍历完,head = null ,当再操作链表时无法再找到头部,所以需要一个变量来代替。(链表:这里举例的方法都需要这样)
遍历列表,需要找到结束条件;
这里需要清楚 cur.next != null 与 cur != null 的区别?
cur.next: 循环会停在链表最后一个的位置,指向节点的尾端; (少遍历一个)
cur = null: 所有元素遍历完全,最后指向空;
//遍历列表
public void display(){
LinkNode cur = head;
//为啥不使用head遍历:head遍历完最后会为null,如果再遍历会找不到链表的头部,
//所以我都会再定义一个遍历来操作链表,让head起到标记作用
while(cur != null) {
System.out.print(cur.val + " ");
cur = cur.next;//往后走一步
}
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
LinkNode cur = head;
while (cur != null) {
if(cur.val == key) { //找到值就返回ture
return true;
}
cur = cur.next;
}
return false;
}
//得到单链表的长度:需要遍历完链表
public int size(){
int num = 0;
//num记录个数
LinkNode cur = head;
while (cur != null) {
cur = cur.next;
num++;
}
return num;
}
2.2.3 头插法-addFirst(), 尾插法-addLast()
头插法:插在头的位置,所以每次需要更新一下头head;
首先生成一个需要插入的节点newLinkNode(new一下), 先连接后面的newLinkNode.next = cur.next,再更新head (head = newLinkNode);
头插代码:
//头插法
public void addFirst(int data){
LinkNode newLinkNode = new LinkNode(data);
newLinkNode.next = head;
head = newLinkNode;
}
尾插法:查找链表的尾部,每次需要先找到尾部,再插入;
思想:首先生成一个需要插入的节点newLinkNode(new一下),再找到最后一个节点cur.next == null,再连接cur.next = newLinkNode即可;
尾插代码:
//尾插法
public void addLast(int data){
LinkNode newLinkNode = new LinkNode(data);
if(head == null) {//如果一个节点也没有,插入的节点就是head
head = newLinkNode;
return;
}
//寻找链表尾部
LinkNode cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = newLinkNode;
}
2.2.4 addIndex-插入任意位置
思想:首先判断插入的位置是否合法,考虑特殊情况(插入位置为首尾),找到要插入位置的前一个位置prvNode, 再连接;
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
checkIndex(index);
if(index == 0) {
addFirst(data);
return;
}
if(index == size()) {
addLast(data);
return;
}
LinkNode cur = findIndex(index);
LinkNode newLinkNode = new LinkNode(data);
newLinkNode.next = cur.next;//先连后面的
cur.next = newLinkNode;
}
//返回发现的下标的节点地址
private LinkNode findIndex(int index) {
LinkNode cur = head;
int num = 0;
while (num != index - 1) {
cur = cur.next;
num++;
}
return cur;
}
private void checkIndex(int index) throws IndexOutOfExpection {
if(index < 0 || index >= size()) {
throw new IndexOutOfExpection("下标异常");
}
}
2.2.5 remove()-删除第一次遇到的值key,removeAllKey()-删除所有的值key
remove思想:考虑特殊情况,没有节点,节点只有一个;再定义一个方法findIndex()找到要删除的前一个节点prev, 再定义要删除的节点del,直接让prev.next = del.next (连接删除节点的下一个节点的地址);
//删除第一次出现关键字为key的节点
public void remove(int key){
//一个节点都没有
if(head == null) {
return;
}
//有一个节点
if(head.val == key) {
head = head.next;
return;
}
//findPrv : 发现要删除的数的前一个节点
LinkNode prev = findPrv(key);
//如果没找到返回null
if(prev == null) {
return;
}
LinkNode del = prev.next;
prev.next = del.next;
}
private LinkNode findPrv(int key) {
LinkNode cur = head;
while (cur != null) {
if(cur.next.val == key) {
return cur;
}
cur = cur.next;
}
return null;
}
removeAllKey()
思想: 双指针操作, 首先定义两个指针prev,cur; cur指针用于遍历,prev用于记录位置, cur遍历时,如果遇到不是要删除的值,prev = next,跟随next一起走;如果遇到要删除的值,直接让prev指针连接cur指针的后一个节点(如果是连续的,cur会向后走一步直到不是要删除的值)当cur为空时,链表中的key值除头部全部删除完全;可以再判断一下第一个节点位置head.val == key;
//删除所有值为key的节点
//思想:双指针删除所有key
//双指针只能删除除head之外所有key
//if第一个为要删除的key 最后需要单独处理一下,也可以开始时进行循环删除头
public void removeAllKey(int key){
if(head == null) {
return;
}
LinkNode prev = head;
LinkNode cur = head.next;
while (cur != null) {
if(cur.val == key) {
prev.next = cur.next;
cur = cur.next;
} else {
prev = cur;
cur = cur.next;
}
}
//单独处理头部
if(head.val == key) {
head = head.next;
}
}
总结
✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!