目录
4.查找关键字key是否在单链表当中,获取单链表长度size
1.链表:
物理上不一定连续,逻辑上一定连续的
head里面存储的是第一个节点(头结点)的地址
head.next存储的下一个节点的地址
尾结点的next域为null
2.链表的整体结构设计
//ListNode 代表一个节点
class ListNode { //在链表mylinkedlist里定义一个类:节点,包括两个属性数据域:val和节点域next
public int val;
public ListNode next; //next存储的是节点的地址,所以类型为ListNode
public ListNode(int val) { //构造函数(带一个参数val),构造函数名与类相同:ListNode
this.val= val; //节点的引用
this.next = next; // next域默认值null
}
}
public class MyLinkedList { //head属于链表的属性,只能定义在链表里
//成员变量
public ListNode head; //链表 的 头引用,head一致指向我的链表mylinkedlist的头部分, 因为head指向的是第一个节点的地址
//========================以上为链表结构的基本结构,===========================//
//方法的实现
public void creatList() { //创建一个方法 枚举法 创建链表(数组)数据
ListNode listnode1 = new ListNode(12); //创建一个链表下的各个节点
ListNode listnode2 = new ListNode(23); //实例化节点对象
ListNode listnode3 = new ListNode(34); //每个节点的数据val已知,但是next未给出,所以是默认null
ListNode listnode4 = new ListNode(45); //对象存放在堆里,包含属性val,next
ListNode listnode5 = new ListNode(56); //listnode1-5存放的是该节点的地址
listnode1.next = listnode2;
listnode2.next = listnode3;
listnode3.next = listnode4;
listnode4.next = listnode5; //尾结点next为null,因为尾节点之后无节点了
listnode5.next = null; //可以不写,因为默认值就是null
this.head = listnode1; //head指向第一个节点
}
}
3.显示打印链表中的val数值
显示打印链表中的val head一直在移动会导致最后找不到head,因此用cur指代head
public void display() {
ListNode cur = this.head; //用cur临时引用,来指向this.head所指向的对象
while(cur != null) { //当遍历到尾结点null之后,结束遍历 要最后一个尾结点地址不为null,尾节点的next域为null
System.out.println(cur.val+" ");
cur = cur.next;
}
System.out.println();
}
4.查找关键字key是否在单链表当中,获取单链表长度size
//查找是否包含关键字key是否在单链表当中 需要遍历链表的各个关节点(直至节点为null)
public boolean contains(int key) {
ListNode cur = this.head; //用cur临时引用 ,来遍历链表各个节点
while(cur != null) { //cur遍历结束即停止
if(cur.val == key) {
return true;
}
cur = cur.next; //继续遍历节点向后走
}
return false; //没有找到关键字key
}
//===============================================================================//
//得到单链表的长度 //求多少个数据节点
public int size() {
int count = 0;
ListNode cur = this.head;
while(cur != null) { //遍历到节点为null截止
count++;
cur = cur.next;
}
return count;
}
5.头插法、尾插法
//头插法 头插法属于倒序,因为后一个数要插在已有数的前面
// 用cur来遍历节点 头部插入之后,head发生变化要变为插入的那个节点
// node.next=head ,head=node
public void addFirst(int data) {
ListNode node = new ListNode(data); // new一个节点, node 引用了对象,指代插入的节点
node.next = head;
this.head = node; //若链表为空 this.head= node 把插入的节点给作为头节点
// if(this.head == null) { //若链表为空 this.node= head
// this.head = node;
// } //也可
}
//===================================================================================//
//尾插法
//寻找尾节点的方式:cur.next==null,cur所指向的节点即尾节点: cur.next = node
//若链表为空null,没有节点,尾插法第一次插入必须判断,若为空head=node
public void addLast(int data) {
ListNode node = new ListNode(data); //new一个节点node,指代插入的节点
if(this.head == null) { //判断链表是否为空 为空head就是node
head = node;
} else {
ListNode cur = this.head; //定义一个cur,遍历链表的各个节点
while(cur.next != null) { //cur.next = null,即找到尾结点,当前cur即尾结点
cur = cur.next;
}
cur.next = node; //找到尾节点之后便可插入给定节点node
}
}
6.在任意位置(给定任意下标)插入节点node
// 当遍历的cur节点走到需要插入节点的下标号node.next=cur.next(node替代了当前的cur)
// 要插入到目标位置,必须先找到目标位置的前一个位置index-1的位置
public ListNode FindIndex(int index) {
ListNode cur = this.head;
while(index-1 != 0) { //只要没走到需要插入的点,就需要一直遍历
cur = cur.next;
index--;
}
return cur;
}
//任意位置插入节点node,第一个数据节点为0号下标
public void addIndex(int index,int data) {
if(index < 0 || index > size()) { //插入位置需要合法
System.out.println("index位置不合法");
return;
}
if(index == 0) { //如果索引为0,即头插方式
addFirst(data);
return;
}
if(index == size()) { //如果索引为size大小,即尾插方式
addLast(data);
return;
}
ListNode cur = FindIndex(index);
ListNode node = new ListNode(data);
node.next = cur.next; //当遍历的cur节点走到需要插入节点的下标号node.next=cur.next(node替代了当前的cur)
cur.next= node;
}
7.删除第一次出现关键字为key的节点
//需要先找删除节点key的前驱cur寻找前驱,因此定义一个cur
public ListNode searchPerv(int key) {
ListNode cur = this.head; //定义cur,用于遍历链表各个节点
while(cur.next != null) { //判断结束条件
if(cur.next.val == key) {
return cur; //如果找到删除的关键字,返回cur(此时指向要删除节点的前一个节点)
}
cur = cur.next; //没找到则继续遍历
}
return null; //cur.next == null 结束遍历,到达尾结点,cur为空
}
//删除第一次出现关键字为key的节点 要考虑头、尾、中间情况
public void remove(int key) { //指向跳过关键字可以,也即是不指向就跳过了
if(this.head == null) { //头节点为空不能删除
System.out.println("单链表为空,不能删除");
return;
}
if(this.head.val == key) { //判断头节点, 因为cur.next.val指的是第二个节点的val域,没有包含第一个头节点
this.head = this.head.next;
return;
}
ListNode cur = searchPerv(key); //如果前驱没找到
if(cur == null) {
System.out.println("没有需要删除的节点!");
return;
}
ListNode del = cur.next;
cur.next = del.next;
}
8.删除所有值为key的节点
//删除所有值为key的节点 只需要遍历一遍就可删除所有点
public ListNode removeAllKey(int key) { //删除当前节点cur,指代地址,,必须知道前一节点prev prev.next = cur.next,cur = cur.next
if(this.head == null) return null;
ListNode prev = this.head; //pre为cur的前一个节点
ListNode cur = this.head.next; //cur为当前需要删除的节点
while(cur != null) { //循环条件
if(cur.val == key) {
prev.next = cur.next; //删除之后prev指向cur.next
cur = cur.next; // cur继续遍历,向后移动
}else {
prev = cur; // 不需要删除prev需要和cur一起向后移动,继续遍历
cur = cur.next;
}
}
if(this.head.val == key) { //最后处理头
this.head = this.head.next;
}
return this.head;
}
//====================================================================================
//清空链表 //使每一个链表节点没有被引用
public void clear() {
while(this.head != null) {
ListNode curnext =head.next;
this.head.next = null; //挨个释放节点 把cur置空null
this.head = curnext;
}
}