203.移除链表元素(注意删除单链表节点必须拿到该节点的头一个节点以及是否使用虚拟头节点的区别即可)
//203. 移除链表元素
public class LeetCode_203 {//给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
public static void main(String[] args) {
}
public static ListNode removeElements(ListNode head, int val) {//todo 这里我使用的是虚拟头节点的写法,不用考虑原head被删除的情况,二刷的时候写一下不用虚拟头节点的写法
ListNode dummy=new ListNode(-1,head); //虚拟头节点
ListNode pre=dummy;
ListNode cur=head;
while (cur!=null){//因为需要遍历所有节点的值,所以不能是cur.next==null
if (cur.val==val){
pre.next=cur.next;//删除cur,就是让要删除的节点的前面那个节点的next指向待删除节点的next
cur=cur.next;
continue;
}
pre=cur;
cur=cur.next;
}
return dummy.next;//todo 这里要注意不能返回head而是返回我们的虚拟头结点的next,这才是真正的head,不然如果head被删了结果就是错误的
}
}
707.设计链表
public class LeetCode_707 {
// 单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
// 实现 MyLinkedList 类:
// MyLinkedList() 初始化 MyLinkedList 对象。
// int get(int index) 获取链表中下标为 index 的节点的值如果下标无效,则返回 -1 。
// void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
// void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
// void addAtIndex(int index, int val) 将一个值为val的节点插入到链表中下标为index的节点之前如果 index 等于链表的长度,
//那么该节点会被追加到链表的末尾如果 index 比长度更大,该节点将不会插入到链表中。
// void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
public static void main(String[] args) {
}
class MyLinkedList { //第一遍没做出来,对虚拟头结点还有index什么时候取等想不明白
//size存储链表元素的个数
int size;
//虚拟头结点
ListNode head;
//初始化链表
public MyLinkedList() {
size = 0;
head = new ListNode(0);//todo 这个就是虚拟头节点,其val为0不代表任何意义
}
//获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
public int get(int index) {
//如果index非法,返回-1
if (index < 0 || index >= size) {
return -1;
}
ListNode currentNode = head;
//todo 包含一个虚拟头节点,所以查找第 index+1 个节点
for (int i = 0; i <= index; i++) {
currentNode = currentNode.next;
}
return currentNode.val;
}
//在链表最前面插入一个节点,等价于在第0个元素前添加
public void addAtHead(int val) {
addAtIndex(0, val);
}
//在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
public void addAtTail(int val) {
addAtIndex(size, val);
}
// 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果 index 大于链表的长度,则返回空
public void addAtIndex(int index, int val) {
if (index > size) {
return;
}
if (index < 0) {
index = 0;
}
size++;
//找到要插入节点的前驱
ListNode pred = head;
for (int i = 0; i < index; i++) {//todo 找到index的前一个节点,即index-1,但是我们还有个虚拟头结点,所以是遍历index-1+1=index次
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;
}
}
}
707题总结:加深了链表虚拟头节点的使用,重点注意遍历的次数与index和虚拟头结点的关系
206 .反转单链表(面试较高频题,着重掌握双指针解法,递归解法可基于双指针的思路写出)
public ListNode reverseList(ListNode head) {
//解题思路:双指针,一个指针记录反转后的结果链表(pre),一个指针往原来的方向遍历(cur)
ListNode pre = null;
ListNode cur = head;
ListNode temp;
//思考循环的终止条件->遍历完原来的链表所有元素,即cur==null
while (cur != null) {
//因为我们每遍历一个原链表中的元素就要将该元素的next指向pre(这样就完全了链表的反转),
// 但是在此之前我们要保存好cur原来所指向的下一个节点,否则遍历将无法继续
temp = cur.next;
cur.next = pre;//遍历过的节点指向pre
pre = cur;//将反转的链表保存
cur = temp;
}
//循环结束后此时cur为null,pre为反转后的链表的头结点
return pre;
}
public ListNode reverseList2(ListNode head) {
//按照双指针的思路使用递归实现
return reverse(head, null);
}
public ListNode reverse(ListNode cur, ListNode pre) {
//思考递归终止条件时什么? todo 因为我前面写过双指针的解法,很明显递归其实就是代替了双指针中while循环的过程,所以递归终止条件就是 cur==null
if (cur == null) {
return pre;
}
ListNode temp;
temp = cur.next;
cur.next = pre;
// pre = cur;
// cur = temp;
return reverse(temp, cur);
}