代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客
第二章 链表part01
day1 任务以及具体安排:https://docs.qq.com/doc/DUG9UR2ZUc3BjRUdY
day 2 任务以及具体安排:https://docs.qq.com/doc/DUGRwWXNOVEpyaVpG
今日任务
● 链表理论基础
● 203.移除链表元素
● 707.设计链表
● 206.反转链表
详细布置
链表理论基础
建议:了解一下链接基础,以及链表和数组的区别
文章链接:https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html
203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html
707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html
206.反转链表
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html
目录
0203_移除链表元素
java删除链表元素的方式
ListNode curr = head;
while (curr != null) {
while (curr.next != null && curr.next.val == val) {
curr.next = curr.next.next;
}
curr = curr.next;
}
public class ListNode {
// 结点的值
int val;
// 下一个结点
ListNode next;
// 节点的构造函数(无参)
public ListNode() {
}
// 节点的构造函数(有一个参数)
public ListNode(int val) {
this.val = val;
}
// 节点的构造函数(有两个参数)
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
class Solution0203 {
/**
* 添加虚节点方式
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*
* @param head
* @param val
* @return
*/
public ListNode removeElements1(ListNode head, int val) {
if (head == null) {
return head;
}
// 因为删除可能涉及到头节点,所以设置dummy节点,统一操作
ListNode dummy = new ListNode(-1, head);
ListNode pre = dummy;
ListNode cur = head;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return dummy.next;
}
/**
* 不添加虚拟节点方式
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*
* @param head
* @param val
* @return
*/
public ListNode removeElements2(ListNode head, int val) {
while (head != null && head.val == val) {
head = head.next;
}
// 已经为null,提前退出
if (head == null) {
return head;
}
// 已确定当前head.val != val
ListNode pre = head;
ListNode cur = head.next;
while (cur != null) {
if (cur.val == val) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return head;
}
/**
* 不添加虚拟节点and pre Node方式
* 时间复杂度 O(n)
* 空间复杂度 O(1)
*
* @param head
* @param val
* @return
*/
public ListNode removeElements3(ListNode head, int val) {
while (head != null && head.val == val) {
head = head.next;
}
ListNode curr = head;
while (curr != null) {
while (curr.next != null && curr.next.val == val) {
curr.next = curr.next.next;
}
curr = curr.next;
}
return head;
}
}
ListNode curr = head; 这行代码是复制了一条相同的链表吗?
不是!在Java中,赋值语句
ListNode curr = head;
并不会复制链表,而是创建了一个新的引用curr
,指向了原始链表的头节点head
。这意味着现在两个引用head
和curr
都指向了同一个链表,它们指向的是相同的节点。因此,对
curr
进行操作会影响到原始链表,因为它们实际上是同一个链表的两个引用。任何对curr
的修改都会在原始链表上产生影响,反之亦然。如果你需要创建一个链表的副本,而不是引用原始链表,你需要使用另一种方法来复制链表,例如使用循环或递归遍历原始链表的每个节点,并创建新的节点来构建新的链表。
0707_设计链表
1、单链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
public class ListNode {//单链表
int val;
ListNode next;
ListNode() {}
ListNode(int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
class MyLinkedList {
int size;//size存储链表元素的个数
ListNode head;//虚拟头结点
//初始化链表
public MyLinkedList() {
size = 0;
head = new ListNode(0);
}
//获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
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;
}
//在链表最前面插入一个节点,等价于在第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++) {
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;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/
2、双链表
public class ListNode {//双链表
int val;
ListNode next, prev;
ListNode() {}
ListNode(int val) {
this.val = val;
}
}
class MyLinkedList2 {
int size;//记录链表中元素的数量
ListNode head, tail;//记录链表的虚拟头结点和尾结点
public MyLinkedList2() {
//初始化操作
this.size = 0;
this.head = new ListNode(0);
this.tail = new ListNode(0);
//这一步非常关键,否则在加入头结点的操作中会出现null.next的错误!!!
head.next = tail;
tail.prev = head;
}
public int get(int index) {
//判断index是否有效
if (index < 0 || index >= size) {
return -1;
}
ListNode cur = this.head;
//判断是哪一边遍历时间更短
if (index >= size / 2) {
//tail开始
cur = tail;
for (int i = 0; i < size - index; i++) {
cur = cur.prev;
}
} else {
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
}
return cur.val;
}
public void addAtHead(int val) {
//等价于在第0个元素前添加
addAtIndex(0, val);
}
public void addAtTail(int val) {
//等价于在最后一个元素(null)前添加
addAtIndex(size, val);
}
public void addAtIndex(int index, int val) {
//index大于链表长度
if (index > size) {
return;
}
//index小于0
if (index < 0) {
index = 0;
}
size++;
//找到前驱
ListNode pre = this.head;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
//新建结点
ListNode newNode = new ListNode(val);
newNode.next = pre.next;
pre.next.prev = newNode;
newNode.prev = pre;
pre.next = newNode;
}
public void deleteAtIndex(int index) {
//判断索引是否有效
if (index < 0 || index >= size) {
return;
}
//删除操作
size--;
ListNode pre = this.head;
for (int i = 0; i < index; i++) {
pre = pre.next;
}
pre.next.next.prev = pre;
pre.next = pre.next.next;
}
}
0206_反转链表
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution0206 {//双指针,时间复杂度: O(n)、空间复杂度: O(1)
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
ListNode temp = null;
while (cur != null) {
temp = cur.next;// 保存下一个节点
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
}
}
class Solution0206_2 {//递归,时间复杂度: O(n)、空间复杂度: O(n)
public ListNode reverseList(ListNode head) {
return reverse(null, head);
}
private ListNode reverse(ListNode prev, ListNode cur) {
if (cur == null) {
return prev;
}
ListNode temp = null;
temp = cur.next;// 先保存下一个节点
cur.next = prev;// 反转
// 更新prev、cur位置
// prev = cur;
// cur = temp;
return reverse(cur, temp);
}
}