目录
1 今日学习的文章
链表理论基础
移除链表元素
看到题目的第一想法
看到代码随想录之后的想法
1 不带虚拟头节点的做法(使用pre指针)
2 带虚拟头节点的做法(使用pre指针)
3 不带虚拟头节点的做法(不使用pre指针)
自己实现过程中遇到的困难
设计链表
看到题目的第一想法
看到代码随想录之后的想法
自己实现过程中遇到的困难(待补充,代码注释里有标注)
1单向
2 双向
反转链表
看到题目的第一想法
看到代码随想录之后的想法
自己实现过程中遇到的困难(待补充,代码注释里有标注)
写代码的时候最好对照图像的逻辑来写,可以画个图,模拟,不会太乱
建议:了解一下链接基础,以及链表和数组的区别
文章链接:代码随想录
学习收获
重点关注了下链表的存储方式
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::代码随想录
这不就是正常的链表遍历然后删除么,很简单的题目,但是实现起来还是有不少坑。。
代码随想录提出了三种办法
1,先要处理头节点相关的与val相同的数据,记住这里是while循环来处理,当head不为val时再考虑之后的数据
2,使用一个pre指向该节点的前置节点,当该节点删除时,pre指向该节点的后置节点,记住这个时候pre是不需要向后移的
3,如果该节点不为val,则pre和该节点一起向后移动
1,如果有虚拟头节点,那么就不需要额外处理head节点了,但是得注意最后的返回应该是虚拟头节点的next节点
2,处理逻辑和上述类似
1,需要额外处理头节点
2,没有pre 那么得做判断,删除的是该节点的next节点,连接的是该节点的next.next,可以把while循环里的当前节点视为pre节点
1,不加上head节点,操作会比较麻烦,比如我在不带虚拟头节点的做法中处理head节点的val元素时,使用的时if而不是while导致报错
2,处理单链表的删除,要记住该节点的上一个节点的位置
3,while循环中,当该节点删除时,pre指向该节点的后置节点,记住这个时候pre是不需要向后移的和pre=pre.next为if else的关系
/**
* 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; }
* }
*/
/*
未添加虚拟节点的做法 需要单独处理head
class Solution {
public ListNode removeElements(ListNode head, int val) {
//删除列表中的val []-->[] -->NULL 如果该节点存在val值,则该节点的上一个节点应该指向该节点的下一个节点
//该节点
//处理头节点(记住这里是while循环)
while(head!=null&&head.val == val){
head = head.next;
}
if(head == null){
return head;
}
ListNode listNode = head.next;
//上一个节点
ListNode preNode = head;
while(listNode!=null){
if(listNode.val==val){
//让前一个节点指向该节点的下一个节点
preNode.next = listNode.next;
}else{
//之前这里忘记加else而是统一在while循环之后preNode=preNode.next是错误的,此时preNode应该不变
preNode = preNode.next;
}
listNode = listNode.next;
}
return head;
}
}*/
//添加虚拟节点的做法
/*class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode newHead = new ListNode();
newHead.next = head;
ListNode listNode = newHead.next;
ListNode preNode = newHead;
while(listNode!=null){
if(listNode.val==val){
preNode.next = listNode.next;
}else{
preNode = preNode.next;
}
listNode = listNode.next;
}
return newHead.next;
}
}*/
//不添加虚拟节点和pre的做法
class Solution {
public ListNode removeElements(ListNode head, int val) {
while(head!=null&&head.val == val){
head = head.next;
}
if(head == null){
return head;
}
ListNode listNode = head;
while(listNode!=null){
//这里是使用while循环
while(listNode.next!=null&&listNode.next.val==val){
listNode.next = listNode.next.next;
}
listNode = listNode.next;
}
return head;
}
}
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:代码随想录
感觉实现比较简单,结果有点无从下笔
1 需要自己定义一个ListNode节点的数据结构
2 在LinkList中需要添加size变量且初始化为0,来控制节点数的数量,方便操作,同时定义一个head变量为虚拟头节点
3 构造函数需要让head初始化以免出现空指针
4 双向链表需要定义head和tail两个节点控制头尾,方便查找,在构造函数中需要用head.next = tail.prev,tail.next = head.prev 以免出现空指针
1 get是如何实现的
我的思路是一个for循环,到index位置(但是需要考虑index的初始值,不能>=元素的个数,以及不能小于0)
2 addAtHead
直接头插法,记得要size++
3 addAtTail
走到最后,然后再插入,记得size++
4addAtIndex
记得讨论index的大小,由于是插入,index的大小可以=元素的个数,相当于在最后插入,不能>元素的个数,找到index所在位置的前置,直接进行插入,记得size++
5deleteAtIndex
记得讨论index的大小,不能大于等于size,我的思路找到前置进行删除,记得size--
class ListNode{
public ListNode next;
public int val;
public ListNode(){
}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class MyLinkedList {
private ListNode head;
private int val ;
private int size = 0;
public MyLinkedList() {
head = new ListNode(0,null);
}
//获取下标为index节点的值
public int get(int index) {
//listNode为虚拟头节点
if(index>=size){
return -1;
}
ListNode list = head.next;
for(int i=0;isize){
return;
}
if(index==size){
this.addAtTail(val);
return;
}
//找到index节点直接插入到index即可
ListNode list = new ListNode(val,null);
ListNode listNode = head.next;
ListNode preNode = head;
for(int i =0;i=size
if(index<0||index>=size){
return;
}
if(size==0){
return;
}
//找到index节点直接删除到index即可
ListNode listNode = head.next;
ListNode preNode = head;
for(int i =0;i
1 get是如何实现的
我的思路是一个for循环,到index位置(但是需要考虑index的初始值,不能>=元素的个数,以及不能小于0)
2 addAtHead
直接addAtIndex(0)
3 addAtTail
直接addAtIndex(size)
4addAtIndex
记得讨论index的大小,由于是插入,index的大小可以=元素的个数,相当于在最后插入,不能>元素的个数,找到index所在位置的前置,直接进行插入,记得size++
5deleteAtIndex
记得讨论index的大小,不能大于等于size,我的思路找到前置进行删除,记得size--
//使用双链表模式实现
class ListNode{
ListNode next,prev;
int val;
public ListNode(){
}
public ListNode(int val){
this.val = val;
}
}
class MyLinkedList {
private ListNode head,tail;
int size = 0;
public MyLinkedList() {
head = new ListNode();
tail = new ListNode();
head.next = tail;
tail.prev = head;
}
//获取index下表个元素(看哪个半边比较快)
public int get(int index) {
if(index<0||index>=size){
return -1;
}
//从头到尾 查index下
ListNode node = head.next;
for(int i=0;i=size有bug,index是可以=size的
if(index>size||index<0){
return;
}
size++;
ListNode oldNode = head.next;
for(int i=0;i=size||index<0){
return;
}
ListNode list = head.next;
for(int i=0;i
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:代码随想录
以前接触过,用头插法
卡哥提出了双指针
建议结合图像来实现,比较清晰
1 头插法出现的问题 卡顿的原因把题目想复杂了本来是想着用newHead先指向head,再头插(newHead.next=head)这样实现困难,其实只需要把newHead=null视为一个新的头节点的空链表,然后再对它挨个进行头插就行了
2 卡哥的方法,双指针,要记得head元素的pre应该指向null,同时循环里需要用一个临时引用来定位断开的位置的后置
/**
* 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 Solution {
public ListNode reverseList(ListNode head) {
//对每个节点使用头插法最后返回原head节点
ListNode newHead = new ListNode();
//添加一个虚拟头节点
//对这个虚拟节点进行头插,先用一个节点定位前一个节点,
//不需要用前一个节点连接后续节点,只需要一个指针找到旧的节点就行了
//卡顿的原因把题目想复杂了本来是想着用newHead先指向head,再头插(newHead.next=head)这样实现困难
newHead.next = null;
ListNode node = head;
ListNode temp = new ListNode();
while(node!=null){
temp = node.next;
node.next = newHead.next;
newHead.next = node;
node = temp;
}
return newHead.next;
}
}*/
//卡哥书上的思路实现
class Solution {
public ListNode reverseList(ListNode head) {
//双指针,当往前走时,每次用后一个指针指向前一个指针
//建议画一张草图,根据草图的思路来判断要怎么走
ListNode pre = null;
ListNode cur = head;
ListNode temp;
while(cur!=null){
temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
return pre;
}
}