今日学习的文章链接和视频链接
leetcode题目地址:707. 设计链表
代码随想录题解地址:代码随想录
题目简介
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 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
的节点。
看到题目的第一想法(可以贴代码)
1.就是简单的链表模拟实现,没什么逻辑上的难度。
class MyLinkedList {
private class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
ListNode head = null;
public MyLinkedList() {}
public int get(int index) {
ListNode temp = this.head;
for (int i = 0; i < index; i++) {
if (temp.next == null) { return -1; }
temp = temp.next;
}
return temp.val;
}
public void addAtHead(int val) {
if (this.head == null) {
ListNode newHead = new ListNode(val);
this.head = newHead;
} else {
ListNode newHead = new ListNode(val, this.head);
this.head = newHead;
}
System.out.println("add head"+head.val);
}
public void addAtTail(int val) {
if (head == null){
ListNode newHead = new ListNode(val);
head = newHead;
}
ListNode temp = this.head;
System.out.println("add tail head"+temp.val);
while (temp.next != null) {
System.out.println("add tailffffffffff"+temp.val);
temp = temp.next;
}
ListNode newTail = new ListNode(val);
temp.next = newTail;
System.out.println("add tail new"+newTail.val);
print();
}
public void addAtIndex(int index, int val) {
ListNode temp = this.head;
if (index == 0){
addAtHead(val);
return;
}
for (int i = 0; i < index - 1; i++) {
if (temp.next == null) {
if (i == index - 1) {
addAtTail(val);
}else {
return;
}
}
temp = temp.next;
}
System.out.println("add index now" + temp.val);
ListNode newNode = new ListNode(val, temp.next);
temp.next = newNode;
System.out.println("add index temp" + temp.val);
System.out.println("add index" + newNode.val);
print();
}
public void deleteAtIndex(int index) {
ListNode temp = this.head;
if (index == 0){
if (head.next == null){
head = null;
}else {
head = head.next;
}
return;
}
if (head.next == null && index >= 1){
return;
}
print();
for (int i = 0; i < index - 1; i++) {
if (i == index - 1 && temp.next != null && temp.next.next == null) {
temp.next = null;
System.out.println("delete a" + temp.val);
print();
return;
}
if (i <= index - 2 && temp.next.next == null) {
System.out.println("delete b" + temp.val);
print();
return;
}
temp = temp.next;
}
if (temp.next.next == null){
temp.next = null;
} else {
temp.next = temp.next.next;
}
System.out.println("delete c" + temp.val);
print();
}
public void print(){
ListNode temp = this.head;
while (temp.next != null){
System.out.println("ppppp"+temp.val);
temp = temp.next;
}
System.out.println("ppppp"+temp.val);
}
}
实现过程中遇到哪些困难
1.边界条件的处理,处理的很不清晰。。逻辑太混乱了
2. 改完大部分bug之后,显示“运行超出时间限制”。。(后来发现好像是我print太多了)
看完代码随想录之后的想法
【解题思路】采用虚拟头节点统一操作,虚拟节点需要给它随便赋个值,如0。
【想法】
1. 解题的时候,定义while或for里面的变量更新情况时,需要经常考虑边界条件(“0”和最大值),最好想清楚之后再开始写代码。
2. 注意以 “第0个节点” 或 “只有一个节点” 等极端情况去(定位?)考虑其他情况。
3. 其实整个逻辑没什么难度,就是代码的边界条件处理上太差,以及没有使用虚拟头节点,导致无法ACC。看完代码之后,发现整个链表结构还可以存一个size来存储链表的长度。果然选择很重要啊,之前因为不熟悉虚拟节点的具体实现,打算直接用头节点开写,却更麻烦。
看完视频自己写的ACC:
class MyLinkedList {
private class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
ListNode head = new ListNode(0);
int size = 0;
public MyLinkedList() {}
public int get(int index) {
if (index >= size){
return -1;
}
ListNode temp = this.head;
for (int i = 0; i <= index; i++) {
temp = temp.next;
}
return temp.val;
}
public void addAtHead(int val) {
ListNode newHead = new ListNode(val);
if (size == 0) {
this.head.next = newHead;
this.size++;
//print();
return;
}
newHead.next = this.head.next;
this.head.next = newHead;
this.size++;
//print();
}
public void addAtTail(int val) {
if (size == 0){
ListNode newTail = new ListNode(val);
this.head.next = newTail;
this.size++;
//print();
return;
}
ListNode temp = this.head;
while (temp.next != null) {
temp = temp.next;
}
ListNode newTail = new ListNode(val);
temp.next = newTail;
this.size++;
//print();
}
public void addAtIndex(int index, int val) {
if (index > size){
return;
}
if (size == 0) {
if (index != 0) {
return;
}else {
addAtHead(val);
this.size++;
return;
}
}
ListNode temp = this.head;
for (int i = 0; i < index ; i++) {
temp = temp.next;
}
ListNode newNode = new ListNode(val);
if (size == index){
temp.next = newNode;
this.size++;
}else {
newNode.next = temp.next;
temp.next = newNode;
this.size++;
//print();
}
}
public void deleteAtIndex(int index) {
if (size == 0 || index >= size) {
return;
}
ListNode temp = this.head;
for (int i = 0; i < index; i++) {
temp = temp.next;
}
if (size - 1 == index){
temp.next = null;
size--;
}else {
temp.next = temp.next.next;
size--;
}
//print();
}
public void print(){
ListNode temp = this.head;
while (temp.next != null){
temp = temp.next;
System.out.println("ppppp"+temp.val);
}
System.out.println("ppppp------------------");
}
}
标准答案 of 代码随想录
//单链表
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 = 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;
}
}
学习时长
8:55 ~ 10:40 解题失败,放弃
16:30 ~ 16:50 + 17:40~19:45 看视频题解、写博客
今日收获
1. 类实例的初始化不要忘记“new”关键字,ListNode newNode = new ListNode(val, temp.next);。
2. java里“this.para”指的是引用本类的全局变量,应该在类里定义,而不是在构造函数里定义。
3. 链表赋值的时候,node.next 可以暂时为空?
如果node.next为null,可以用在赋值号的右侧代替“null”,但是不能用在赋值号的左侧作为变量。此外,node.next为null时,node.next.next也会报错,因为null不能作为变量(但是可以作为被赋予的值)。