代码随想录Day03|移除链表元素 设计链表 反转链表
1.题目链接:移除链表元素 - 力扣
文章讲解/视频讲解:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html
状态:已完成
题目描述:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1
输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7
输出:[]
解题思路:
这道题只需要掌握基本的链表知识,知道链表的定义以及分类就能够很好的写出这道题。关于链表的定义与分类比较熟悉,这里就不赘述了。
删除元素也就是使该元素的上一个结点的指针域指向该元素的下一个结点。这个时候就需要思考两种情况,一种是被删除的元素在中间或者最后,另一种则是被删除的元素是头结点。如果遇到的是头结点的话,那就没有前一个元素了。这样就会涉及到链表的两种操作方式:1.直接使用原来的链表来进行删除 2.设置一个虚拟头结点再进行删除操作
1.直接使用原来的链表来进行删除操作
需要留意到删除头结点的元素和其它的元素的方法是不一样的。删除头结点的话将指针往后移就可以了,其它的则是前一个结点指向后一个结点。
下面是代码示例:
class Solution {
public ListNode removeElements(ListNode head, int val) {
//删除头结点
while(head!= null && head.val==val){
head=head.next;
}
//已经为空,提前退出
if(head==null){
return head;
}
//删除非头结点
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;
}
}
2.设置一个虚拟头结点
给链表添加一个虚拟头结点,这样每个结点的删除操作都是一样的,但要记得返回的时候需要返回虚拟头结点的下一个结点,这个结点才是新的头结点。还需要注意的是,设置虚拟头结点的val要在题目所给的数值之外。
下面是代码示例:
class Solution2{
public ListNode removeElements(ListNode head, int val){
if (head==null){
return head;
}
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;
}
}
总结:
对链表的基础知识比较熟悉的话,整体的操作不算特别难。理清楚基本概念之后比较简单。就是需要注意细节,需要删除的结点为头结点时和其它非头结点的操作不太一样,要么就是分情况写,要么就是增加一个虚拟头结点,使得两者的情况一样。
2.题目链接:设计链表 - 力扣
文章讲解/视频讲解:
状态:已完成
题目描述:你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性: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
的节点。
示例:
输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
解题思路:
这是一道覆盖了很多链表基本操作的一道题,掌握了链表的基本操作后,细心一点就能够写出来
下面是代码示例:
class ListNode2{
int val;
ListNode2 next;
ListNode2(){}
ListNode2(int val){
this.val=val;
}
}
class MyLinkedList {
//size存储链表元素的个数
int size;
//虚拟头结点
ListNode2 head;
public MyLinkedList() {
size=0;
head=new ListNode2(0);
}
public int get(int index) {
//取到等于是因为有一个虚拟结点在
if(index<0||index>=size){
return -1;
}
ListNode2 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);
}
public void addAtIndex(int index, int val) {
if(index>size){
return;
}
if(index<0){
index=0;
}
size++;
//找到要插入的头结点
ListNode2 pre=head;
for(int i=0;i<index;i++){
pre=pre.next;
}
ListNode2 toAdd=new ListNode2(val);
toAdd.next=pre.next;
pre.next=toAdd;
}
public void deleteAtIndex(int index) {
if(index<0||index>=size){
return;
}
size--;
if(index==0){
head=head.next;
return;
}
ListNode2 pre=head;
for(int i=0;i<index;i++){
pre=pre.next;
}
pre.next=pre.next.next;
}
}
总结:
链表的基本操作,整体不算太难,但需要仔细思考。不要忽略细节,要思考到正确的区间,有清晰的思路再逐渐去推敲。
3.题目链接:反转链表 - 力扣
文章讲解/视频讲解:
状态:已完成
题目描述:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
解题思路:
反转链表不需要开辟一个新的链表,只需要改变next指针的方向就可以实现反转。首先需要定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。首先要把cur.next结点用tmp指针保存一下。然后就开始改变指针的指向,将cur.next指向pre,然后按照这个思路往后即可。
下面是代码示例:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre=null;
ListNode cur=head;
ListNode temp=null;
while(cur!=null){
temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
}
总结:
反转链表的递归方法还暂未学习,双指针的方法反转链表已经熟练的掌握了。遇到不太会的时候可以画图去理解。我认为画图推导对于小白来讲是一个比较好理解代码的方法。
体会:
今天主要学习了链表的知识,因为之前有学习过,相当于是复习一遍。总体来说是比较简单的,但还是有很多细节的地方需要注意。希望自己能够坚持学习下去!