代码随想录day03| 203. 移除链表元素、707. 设计链表、206. 反转链表

目录

203  移除链表元素

707  设计链表

206  反转链表


203  移除链表元素

题目链接

看到题目的第一想法:刷过的题,看到后仍旧感觉无从下手,特别是虚拟头节点的定义,虚拟头节点dummy从-1开始,head从0开始

看完代码随想录之后的想法:卡哥提供的代码简洁明了,虚拟头节点的出现,是为了统一处理各个节点,不需要单独对head节点进行特殊处理。虚拟头节点如何手动书写,用来操作的cur、pre节点,cur用来表示当前正在操作的节点,pre表示cur的前一个节点。

代码实现:

  • 虚拟头节点的方式
  • while循环的条件是cur != null,cur一直向后移动,等其指向于null时,pre也到了最后一个val值不等于val的节点~
/**
 * 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 removeElements(ListNode head, int val) {
        if (head == null) {
            return head;
        }
        //使用虚拟头结点的方式,是删除操作统一化,不需要单独处理删除节点是头节点的问题
        //虚拟头节点的定义,有一些印象,但虚拟头结点的定义不了解
        ListNode dummy = new ListNode(-1, head);
        //pre与cur分别指向虚拟头结点和头结点,用来进行操作,链表会进行删减,dummy用来最后的处理与指向
        //定义pre节点
        ListNode pre = dummy;
        //定义cur节点
        ListNode cur = head;
        //while的循环条件是cur!=null,等于null时说明已到末尾,用cur代替了head,因为cur会变化
        //而head经处理后不能一直变化~
        while (cur != null) {
            if (val == cur.val) {
                //pre节点指向cur的下一个节点,由此来看pre和cur都是动态变化的值
                pre.next = cur.next;
            }else {
                //cur的值与val不相等时,将cur赋值给pre?
                pre = cur;
            }
            //cur向后移动一个节点
            cur = cur.next;

        }
        //返回值时dummy的下一个节点,相当于head,指针的相关赋值和指向还是不太了解。。要再深入看看
        return dummy.next;
    }
}
  • 不使用虚拟头节点的方式(只需要对head节点进行特殊处理,其他与采用虚拟头节点的方式一致)
/**
 * 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 removeElements(ListNode head, int val) {
        //不采用虚拟头结点的方式
        while (head != null && head.val == val) {
            head = head.next;
        }
        if (head == null) {
            return head;
        }
        ListNode pre = head;
        //此时head.val != val
        ListNode cur = head.next;
        while (cur != null) {
            if(cur.val == val) {
                pre.next = cur.next;
            }else {
                pre = cur;
            }
            cur = cur.next;
        }
        return head;     
    }
}
  • 不使用虚拟头节点和pre节点的方式(对head进行特殊处理,那为什么还要加pre节点呢???后续待补充)
/**
 * 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 removeElements(ListNode head, int val) {
        while(head != null && head.val == val) {
            head = head.next;
        }
        if (head == null) {
            return head;
        }
        ListNode cur = head;
        while (cur != null) {
            //下方应该使用while而非if,即将与val相等的值都移除
            while (cur.next != null && cur.next.val == val) {
                cur.next = cur.next.next;
            }
            cur = cur.next;
        }
        return head;
    }
}

实现过程中遇到哪些困难:

  • 虚拟头节点dummy定义的代码书写,循环终止条件的确定,最终返回值的确定(return head?还是dummy.next,或者pre?)

707  设计链表

题目链接

看到题目的第一想法:题目已经看到好几遍了,但是还是没有思路,使用增删查还行,设计的话确实没有思路。

看完代码随想录之后的想法:按部就班、递进的写,就能把一个复杂的问题慢慢解决掉。所以不要怕困难,逃避解决不了任何问题,那些逃避掉的困难,总有一天会更加凶猛的找上你!

  • 我这辈子就是吃了逃避的大亏了!

代码实现:

  1. 先提供一个单链表实体类,而后设置链表元素的个数size,虚拟头节点head,而后使用构造方法进行初始化,此处虚拟头节点设置的是-1,卡哥解法里给的是0 ,感觉不太好理解。
  2. 当不确定while循环的终止条件时,使用边界值验证一下,但后续也要加强理解,若面试时不能在边界值浪费太多时间。
  3. 当插入、或者删除某个节点时,应该让cur.next执行要处理的节点,cur指向处理节点的前一个节点,并且链表的长度要相应的增加或者减少;新增节点时要创建节点,给节点赋值后再新增!
class ListNode{
    int val;
    ListNode next;
    public ListNode(){

    }
    public ListNode(int val) {
        this.val = val;
    }
}

//要保证操作的节点n是cur.next节点,这样才能方便处理
//先指向后面的边,再指前面的边
class MyLinkedList {
    //存储链表元素的个数
    int size;
    //虚拟头结点
    ListNode head;

    //初始化链表
    public MyLinkedList() {
        size = 0;
        //此处将虚拟头节点的index设置为-1了!!!
        head = new ListNode(-1);
    }
    
    //0是第一个节点
    public int get(int index) {
        if (index < 0 || index >= size) {
            return -1;
        }
        //因为链表包含了虚拟头结点,故去index+1对应的值
        //虚拟头结点的位置从-1开始算时
        ListNode cur = head;
        cur = cur.next;
        while (index > 0) {
            cur = cur.next;
            index--;
        }
        return cur.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 < 0) {
            index = 0;
        }
        if (index > size) {
            return;
        }
        size++;
        //包含了虚拟头节点?
        ListNode cur = head;
        //找到插入节点的前缀
        for (int i = 0; i < index; i++) {
            //让前一个节点为cur,下一个节点为要操作的位置
            cur = cur.next;
        }
        ListNode tmp = new ListNode(val);
        tmp.next = cur.next;
        cur.next = tmp;
    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        //链表长度-1
        size--;
        //保证要操作的元素为cur.next,cur为前一个元素
        ListNode cur = head;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.next = cur.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);
 */

实现过程中遇到哪些困难

  • 虚拟头节点的定义,从-1开始
  • 保证要操作的元素为cur.next,cur为前一个元素
  • 以节点的插入为例,先tmp.next = cur.next,而后再cur.next = tmp;即先后面的边,而后前面的边,认真理解~
  • 双链表的设计还未处理,后面周末补上~

206  反转链表

题目链接

看到题目的第一想法:看到题后没有思路,自己也不敢写写试试,这是大忌,行不行都可以试试!

看完代码随想录之后的想法:双指针法简单且清晰,递归法不是很理解~

代码实现:

  • 双指针法(cur.next先指向pre,而后再给pre赋值,最后给cur赋值,cur要赋的值之前由tmp保存着)
/**
 * 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) {
        //看到题目后完全没有思路。。
        ListNode pre = null;
        ListNode cur = head;
        ListNode tmp = null;

        //先将cur.next的值用tmp保存下来,因为断开指针会丢失;而后将cur->pre,在将cur赋值给pre
        //而后将tmp赋值给cur;cur始终快pre一步,cur==null时,pre正好为最后一个非空节点
        while (cur != null) {
            //tmp存放cur的下一个节点值
            tmp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = tmp;
        }
        return pre;
    }
}
  • 递归法(对递归不太了解,此处只做了两件事,保存下一个节点的值和指向前一个节点)
/**
 * 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) {
        //对递归的写法不太了解
        return reverse(null, head);
    }

    public ListNode reverse(ListNode pre, ListNode cur) {
        if (cur == null) {
            return pre;
        }
        ListNode tmp = null;
        //保存下一个节点
        tmp = cur.next;
        //反转
        cur.next = pre;

        return reverse(cur, tmp);
    }
}

实现过程中遇到哪些困难:

  • 有点思路,又感觉无从下手,然后就不敢写了,去翻答案了。。
    • Just do it !
  • 对递归不熟悉,后面debug练习练习

今日收获,记录一下自己的学习时长

  • 算法处理约4h,博客编写约2h
  • 了解了链表的基本操作,链表类、增加、删除、查询、链表元素移除、链表反转操作等,这块仍不如数组熟练,应该是链表没有数组结构清晰明了的原因,应当多加练习!
  • 贵在坚持,加油! 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值