203.移除链表元素|707.设计链表|206.反转链表【算法学习笔记】
- 移除链表元素 给一个链表,要求删除等于val的元素,并返回头结点
第一种解法是 直接使用原来的链表 ,即在原本的链表上进行移除结点操作。
要注意的是因为头结点的删除和其它结点不一样,其他结点是要通过前结点来删除的,所以删除头结点和删除其他结点的操作要区分开来。
class Solution {
public ListNode removeElements(ListNode head, int val) {
//先判断头结点,如果满足条件头结点向后移
while (head != null && head.val == val) {
head = head.next;
}
ListNode curr = head; //用curr指针记录当前头结点的位置
//需要注意的是curr指针此时指向不是val的结点,所以删除操作从curr.next开始判断
while (curr != null && curr.next != null) {
if (curr.next.val == val) { //判断下个结点的值是不是val
curr.next = curr.next.next;
}
else
curr = curr.next; //如果不是curr向后移动
}
return head;
}
}
第二种解法是 虚拟头结点解法 ,即在第一个结点前增加一个虚拟结点,让删除头结点的规则和删除后面元素规则一样。
要注意的第一点是定义临时指针curr应该指向虚拟头结点,这样才能对后面结点进行删除操作。第二点是不能return head,因为这时候头结点的值可能已经发生改变了,也就是head可能还是指向原本链表的第一个元素,而此时头结点已经指向后面的元素了,再return head 就会报错。
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyhead = new ListNode(-1 ,head); //定义一个虚拟头结点
ListNode curr = dummyhead; //定义curr指针指向虚拟头结点
while (curr.next != null) {
if(curr.next.val == val) {
curr.next = curr.next.next;
}
else
curr = curr.next;
}
return dummyhead.next;
}
}
- 设计链表 这道题的要求是使用单链表或双链表来设计对链表进行增删查的操作
这道题我用的是单链表。做这道题要注意的是设计链表和初始化链表这两个步骤。在设计链表时加入虚拟头结点的操作有利于后面增删改的实现,加入size可以控制链表的大小。不要忘记初始化链表这个步骤,不然虚拟头结点的next指针还是指向null,初始化后才能和链表联系起来。
//单链表
//定义构造函数(可写可不写)
class ListNode {
int val;
ListNode next;
ListNode(){}
ListNode(int val) {
this.val=val;
}
}
class MyLinkedList {
//size存储链表元素的个数
int size;
//虚拟头结点
ListNode head;
//初始化链表
public MyLinkedList() {
size = 0;
//初始化后head指针指向链表
head = new ListNode(0);
}
//获取第index个节点的数值
public int get(int index) {
//如果index非法,返回-1
if (index < 0 || index >= size) {
return -1;
}
ListNode currentNode = head;
//包含一个虚拟头节点,所以查找第 index+1 个节点
for (int i = 0; i <= index; i++) {
currentNode = currentNode.next;
}
return currentNode.val;
}
//在链表最前面插入一个节点
public void addAtHead(int val) {
addAtIndex(0, val);
}
//在链表的最后插入一个节点
public void addAtTail(int val) {
addAtIndex(size, val);
}
// 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果 index 大于链表的长度,则返回空
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
size++;
//找到要插入节点的前驱
ListNode pred = head;
for (int i = 0; i < index; i++) {
pred = pred.next;
}
ListNode toAdd = new ListNode(val);
toAdd.next = pred.next;
pred.next = toAdd;
}
//删除第index个节点
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
if (index == 0) {
head = head.next;
return;
}
ListNode pred = head;
for (int i = 0; i < index ; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
- 反转链表 这道题的要求是通过给的头结点返回一个翻转的链表。
第一种解法是 双指针解法,即用两个指针记录前后位置实现翻转
要注意的是反转链表后第一个元素指向的是null,需要给前指针赋值为null。并且在改变结点的next时,需要用temp临时指针记录下个结点的位置。
class Solution {
public ListNode reverseList(ListNode head) {
ListNode curr = head;
ListNode pre = null;
while(curr != null) {
ListNode temp = curr.next;
curr.next = pre;
pre = curr;
curr = temp;
}
return pre;
}
}
第二种解法是递归,即通过重复调用函数得出结果,实现过程还是依照双指针的解法来写的。
要注意的是写函数时参数要写类型
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(head,null);
}
ListNode reverse(ListNode curr,ListNode pre) {
if(curr == null) {
return pre;
}
ListNode temp = curr.next;
curr.next = pre;
return reverse(temp,curr);
}
}