前言
逆水行舟,不进则退!!!
目录
尾插法的实现
实现尾插法主要有两个关键点:1,判断链表是否为空;2,如何找到链表的最后一个结点;
1,如果链表为空,也就是head为空,当head为空,那就令head指向需要尾插的结点;
2,找到链表的最后一个结点也简单,通过循环来寻找就可以了,只是有一个地方需要注意:在我们找到最后一个结点后,是要将新的结点插入到找到的结点之后,而单向链表的性质决定了循环的判断条件。
如图所示,需要将遍历链表的指针指在最后一个结点上,然后令cur.next = node;就可以了,
所以,循环的结束条件就是(cur.next != null) 而不是 (cur != null);
//尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
}else {
ListNode cur = head; //需要一个额外的指针去遍历链表
while(cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
任意位置插入的方法的实现
· 实现任意位置的插入,也是两个关键点:1,链表为空时要怎么办;2,index的值大于链表的长度时怎么处理;
1,我的想法是,不单独考虑链表为空时,只考虑index的数值,当index == 0时,调用头插法,头插法内部就有判空程序,并且也简洁明了。
2,第二个问题吗就是看判断条件怎么写了。中间位置也好,链尾位置也罢,都是要找到插入位置的前一个结点,不过这里吗,就和上面的尾插法的处理不一样了,因为还要考虑到链表为空时的情况。所以这里再定义一个计数器,直接找要插入位置的前一个位置,并且这个位置上的结点不为空。 条件这么写 : (cur != null && count != index - 1) 这么处理,即使链表为空,也不会报错,并且在index超出链表的长度时,也可以处理,只需要在循环后边加上一个判断语句 if(cur == null) { return false;}。 一举两得的写法。
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data) {
ListNode node = new ListNode(data);
if(index == 0) { //如果index == 0,那就直接调用头插法
addFirst(data);
return true;
}else {
ListNode cur = head;
int count = 0;
while(cur != null && count != index - 1) { //即使插入到链尾,找到的也是倒数第二个结点的位置,
// 所以cur不能等于null
cur = cur.next;
count++;
}
if (cur == null) { // cur等于null,说明index的下标大于链表的长度了
return false;
}
node.next = cur.next;
cur.next = node;
return true;
}
}
删除所有关键字key方法的实现
这一部分的代码,我认为需要注意的地方有三点:1,如何寻找到所有的关键字key;2,循环的结束条件如何写;3,头结点为目标结点时的处理细节;
1,因为单向链表的性质,删除一个结点同样需要前一个结点。如果只有一个指针的话,也可以处理,不过我这里用到的是双指针,更容易理解。
2,因为用的是双指针,这里的判断条件是,快的那个指针不等于空 即: (curNext != null);
3,上述的处理方法,是从第二个结点开始遍历数组查找关键字key,当处理完后,就只剩头结点还没有遍历,所以最后再来一个判断语句来处理头结点。这里也是一个细节。不能先处理头结点,万一头几个结点都是目标结点,就不好处理了。
//删除所有值为key的节点
public void removeAllKey(int key) {
ListNode cur = head;
ListNode curNext = head.next; //同样使用的是双指针
while(curNext != null) { //这里的判断条件如果是(curNext.next != null),就拿不到最后一个结点的val值。
if(curNext.val == key) {
cur.next = curNext.next;
curNext = curNext.next;
}else {
curNext = curNext.next;
cur = cur.next;
}
}
if(head.val == key) { //最后在处理头结点,是为了避免前两个结点都是目标结点。
head = head.next; //因为上面的代码已经将头结点后面的所有的目标节点都处理完了。就只剩下头结点没有处理。 这是这样写代码的巧妙。
}
}
完整代码
// 无头单向非循环链表实现
public class SingleLinkedList {
static class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head; //头指针
//头插法
public void addFirst(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
}else {
node.next = head;
head = node;
}
}
//尾插法
public void addLast(int data){
ListNode node = new ListNode(data);
if(head == null) {
head = node;
}else {
ListNode cur = head; //需要一个额外的指针去遍历链表
while(cur.next != null) {
cur = cur.next;
}
cur.next = node;
}
}
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index,int data) {
ListNode node = new ListNode(data);
if(index == 0) { //如果index == 0,那就直接调用头插法
addFirst(data);
return true;
}else {
ListNode cur = head;
int count = 0;
while(cur != null && count != index - 1) { //即使插入到链尾,找到的也是倒数第二个结点的位置,
// 所以cur不能等于null,
cur = cur.next;
count++;
}
if (cur == null) { // cur等于null,说明index的下标大于链表的长度了
return false;
}
node.next = cur.next;
cur.next = node;
return true;
}
}
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key) {
ListNode cur = head;
while(cur != null) {
if(cur.val == key) {
return true;
}
cur = cur.next;
}
return false;
}
//删除第一次出现关键字为key的节点
public void remove(int key) {
if(head.val == key) { // 若头结点为目标节点
head = head.next; // 将head往后移
return; // 结束
}
ListNode cur = head;
ListNode curNext = head.next; //如果删掉一个中间节点,还要用到该节点的前驱结点,
while(curNext.val != key) { // 所以用双指针来遍历链表 也叫快慢指针。
curNext = curNext.next;
cur = cur.next;
}
cur.next = curNext.next;
curNext.next = null;
}
//删除所有值为key的节点
public void removeAllKey(int key) {
ListNode cur = head;
ListNode curNext = head.next; //同样使用的是双指针
while(curNext != null) { //这里的判断条件如果是(curNext.next != null),就拿不到最后一个结点的val值。
if(curNext.val == key) {
cur.next = curNext.next;
curNext = curNext.next;
}else {
curNext = curNext.next;
cur = cur.next;
}
}
if(head.val == key) { //最后在处理头结点,是为了避免前两个结点都是目标结点。
head = head.next; //因为上面的代码已经将头结点后面的所有的目标节点都处理完了。就只剩下头结点没有处理。 这是这样写代码的巧妙。
}
}
//得到单链表的长度
public int size() {
ListNode cur = head;
int count = 0;
while(cur != null) {
cur = cur.next;
count++;
}
return count;
}
public void display() {
ListNode cur = head;
while(cur != null) { 这里的判断条件如果是(cur.next != null),就拿不到最后一个结点的val值。
System.out.println(cur.val);
cur = cur.next;
}
}
public void clear() {
head = null;
}
}
我是专注学习的章鱼哥~