目录
203.移除链表元素
1.解题思路
链表中的删除是跳过那个值
普通方法:(不用虚拟头节点)
要把满足val的节点的前一个链表中指向下一个的引用改变为val中的引用,但如果是头节点就没有前一个,所以首先判断元素是否为头节点。定义一个临时cur不直接操作head是因为要返回head。
虚拟头节点:
虚设一个头节点ListNode dummy = new ListNode(),这个头节点的next指向head,cur指向dummy,cur在else出才改变他的指向,在此之前cur一直指向dummy指向的实例及现在cur.next改变dummy.next也会改变,直到cur.next.val != val,然后此时cur.next改变dummy.next不会改变。
这里不返回head是因为head可能被删除了(跳过了)。
2.遇到的问题
1.在用普通方法时,判断进入循环条件一直不理解为什么还要判断cur.next!=null,以为无法判断最后一个val的值,虽然指针指着倒数第二个,最后一个不能进入循环,但cur.next.val==val判断了最后一个ListNode的val。
2.不清楚什么cur的指向,在解题思路中已经给出。
3.实现代码
普通方法
public ListNode removeElements(ListNode head, int val) {
while (head!=null&&head.val==val){//这里使用while是防止下一个head的val也是val
head=head.next;
}
ListNode cur=head;
while (cur!=null&&cur.next!=null){//因为是判断cur.next.val的值所以指针停在倒数第二个就行了,因为cur.next.val已经判断了下一个的val
if (cur.next.val==val){
cur.next=cur.next.next;
}else {
cur=cur.next;
}
}
return head;
}
虚拟头节点:
public ListNode removeElements(ListNode head, int val) {
// 设置一个虚拟的头结点
ListNode dummy = new ListNode();
dummy.next = head;
ListNode cur = dummy;
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
} else {
cur = cur.next;//这里才改变cur的指向
}
}
return dummy.next;
}
4.题目总结
虚拟头节点法可以有效地从列表中删除具有指定值的所有节点,同时借助虚拟节点优雅地处理边缘情况,及可以统一处理边缘的方法。
707.设计链表
代码实现
在代码注释里面包含了解释,思路,重点
在增加,删除操作时,一定要知道前一个的数据,注意遍历方法,将虚拟head看成最前面的节点,NULL看成最后一个节点,增加删除可以用同一种方法遍历,与增加。
单链表:
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 MyLinkedList {
//size储存链表元素个数
int size;
//虚拟头节点
ListNode head;
//初始化链表
public MyLinkedList(){
size=0;
head=new ListNode(0);
}
//获取链表下标为index的节点的值,如果无效返回-1
public int get(int index) {
if (index<0||index>=size){
return -1;
}
ListNode cur=head;//cur指向下标为-1的位置
while (index>0){//用cur来遍历链表,cur遍历到index-1位置
cur=cur.next;
index--;
}
return cur.next.val;
}
//将一个值为val的节点插入到链表的第一个节点
// 新节点会成为链表的第一个节点
public void addAtHead(int val) {
ListNode newNode=new ListNode(val);
//先是指向后面,因为head.next后面要边
// 如果是第一个,相当于在head,NULL之间加一个节点
newNode.next=head.next;
head.next=newNode;
size++;
}
//将一个值追加到链表中作为链表最后一个元素
public void addAtTail(int val) {
ListNode newNode=new ListNode(val);
ListNode cur=head;
int n=size;
while (n>0){//遍历到最后一个下标位置,及size-1
cur=cur.next;
n--;
}
newNode.next=cur.next;
cur.next=newNode;
size++;
}
//将一个val值为val的节点插入到1链表中下标为index的节点之前
//如果index的等于链表长度,那么就追加到末尾
//大于size则不会插入链表
public void addAtIndex(int index, int val) {
if (index>size){
return;
}
if (index<0){
index=0;
}
ListNode newNode=new ListNode(val);
ListNode cur=head;
while (index>0){//cur遍历到index-1位置
cur=cur.next;
index--;
}
newNode.next=cur.next;
cur.next=newNode;
size++;
}
public void deleteAtIndex(int index) {
if (index<0||index>=size){
return;
}
ListNode cur=head;
while (index>0){//cur遍历到index的前一个
cur=cur.next;
index--;
}
cur.next=cur.next.next;
size--;
}
}
206.反转链表
代码实现
双指针:
//双指针方法
public ListNode reverseList(ListNode head) {//传一个链表的头节点进来
ListNode pre=null;//定义一个前置虚拟头节点
ListNode cur=head;
while (cur!=null){//直到cur==null时停止遍历
//用来临时储存cur的下一个,因为后面cur的下一个会改变,变为指向pre
ListNode temp=cur.next;
//pre要变为改变后的cur,然后cur指向下一个
cur.next=pre;
pre=cur;
cur=temp;
}
//最后cur指向null,pre指向最后一个节点
return pre;
}
递归:
//递归解法
public static ListNode reverse(ListNode cur,ListNode pre){
if (cur==null){
return pre;
}
ListNode temp=cur.next;
cur.next=pre;
return reverse(temp,cur);
}
public ListNode reverseList(ListNode head){
return reverse(head,null);
}