第二章 链表part01 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表

第三天| 第二章 链表part01 链表理论基础 203.移除链表元素 707.设计链表 206.反转链表

链表理论基础

  • 建议:了解一下链接基础,以及链表和数组的区别
  • 文章链接:https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

203.移除链表元素

  • 题目链接:https://leetcode.cn/problems/remove-linked-list-elements/description/

  • 文章讲解:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

  • 题目介绍:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

  • 解法:(因为涉及到是否删除头节点,所以有以下两种解法)

    • 第一种:直接删除

      • 先判断head是否为空,空的话直接返回head即可。

      • 因为头节点的删除与其他中间节点不同,所以需要单独讨论。

        • 如果头节点不为空,并且头节点中的值是目标值的话,就将头节点向他的next位置移动,即head = head.next;
      • 其他中间节点,需要指定一个指针cur,cur指向头节点,通过cur指针的移动遍历整个链表,因为要判断中间节点的val是否与目标值相等,因此需要保证cur.next != null; 。如果cur.next.val是目标值,就将这个节点删除掉。

        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sHwzSyay-1691778284104)(C:\Users\LENOVO\AppData\Roaming\Typora\typora-user-images\image-20230811235113322.png)]
      • 代码如下:

        • class Solution {
              public ListNode removeElements(ListNode head, int val) {
                  ListNode cur = head;
                  if (head == null) {
                      return head;
                  }
                  while (head != null && head.val == val) {
                      head = head.next;
                  }
                  while (cur.next != null) {
                      if (cur.next.val == val) {
                          cur.next = cur.next.next;
                      } else {
                          cur = cur.next;
                      }
                  }
                  return head;
              }
          }
          
    • 第二种:虚拟头节点:

      • 思路:给现有的链表加一个虚拟的头节点,那么原来的链表头节点就成为一个中间节点,可以统一处理了。如果cur.next.val与目标值相等,那么和解法一一样,将cur.next赋值为cur.next.next,否则就让指针向后移一位。

      • 代码:

        • class Solution {
              public ListNode removeElements(ListNode head, int val) {
                  ListNode dummyHead = new ListNode(-1, head);
                  ListNode cur = dummyHead;
                  while (cur.next != null) {
                      if (cur.next.val == val) {
                          cur.next = cur.next.next;
                      } else {
                          cur = cur.next;
                      }
                  }
                  return dummyHead.next;
              }
          }
          
        • 注意:

          • 最终返回的一定是**dummyHead.next**,不管之前的头节点是否存在,虚拟节点之后的第一个节点就是新链表的头节点

707.设计链表

  • 题目链接:https://leetcode.cn/problems/design-linked-list/

  • 文章讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

  • 题目介绍:

    • 你可以选择使用单链表或者双链表,设计并实现自己的链表。
    • 单链表中的节点应该具备两个属性: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 的节点。
  • 代码:

    • //单链表
      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 MyLinkedList {
          //size存储链表元素的个数
          int size;
          //虚拟头节点
          ListNode dummyHead;
          //头节点
          ListNode head = null;
      
          //初始化链表
          public MyLinkedList() {
              size = 0;
              dummyHead = new ListNode(-1, head);
          }
      
          // 获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
          public int get(int index) {
              // 获取一个遍历指针
              ListNode cur = dummyHead;
              // 如果index非法,返回-1
              if (index >= 0 && index <= size - 1) {
                  //从虚拟节点开始寻找,要找到第index的位置,所以是<=
                  for (int i = 0; i <= index; i++) {
                      cur = cur.next;
                  }
                  return cur.val;
              } else {
                  return -1;
              }
          }
      
          // 在链表最前面插入一个节点,等价于在第0个元素前添加
          public void addAtHead(int val) {
              addAtIndex(0, val);
          }
      
          // 在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
          public void addAtTail(int val) {
              addAtIndex(size, val);
          }
      
          // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
          // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
          // 如果 index 大于链表的长度,则返回空
          // 如果 index 小于0,把index赋值为0,在链表头部插入
          public void addAtIndex(int index, int val) {
              if (index > size) {
                  return;
              }
              if (index < 0) {
                  index = 0;
              }
              // 从虚拟节点开始寻找,要找到第index的位置的前一个,所以是<
              // 从前一个开始插数据
              ListNode cur = dummyHead;
              for (int i = 0; i < index; i++) {
                  cur = cur.next;
              }
              ListNode toAdd = new ListNode(val);
              toAdd.next = cur.next;
              cur.next = toAdd;
              size++;
          }
      
          // 删除第index个节点
          // 如果 index 小于0或者大于链表长度,直接终止
          // 如果 index 等于0,
          public void deleteAtIndex(int index) {
              if (index < 0 || index >= size) {
                  return;
              }
              // 从虚拟节点开始寻找,要找到第index的位置的前一个,所以是<
              // 从前一个开始删数据
              // 与插入数据保持高度统一,完美!!
              ListNode cur = dummyHead;
              for (int i = 0; i < index ; i++) {
                  cur = cur.next;
              }
              cur.next = cur.next.next;
              size--;
          }
      }
      

206.反转链表

  • 题目链接:https://leetcode.cn/problems/reverse-linked-list/

  • 文章讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html

  • 题目介绍:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

  • 解法:

    • 解法一:双指针

      • 思路:

        • 先提供一个空指针pre和一个指向head的指针cur,如图所示。循环终止的条件是cur找到了原顺序的最后一个null值,在一次循环的过程中完成的操作是:
          • 首先,把**原来顺序的链路径cur.next**保存在一个temp中(我为了方便理解,给它起名为链路径,实际没有这种叫法)(temp = cur.next;)
          • 其次,让cur.next指向pre(cur.next = pre;)
          • 然后,移动pre到cur现在的位置(pre = cur;)
          • 最后,把cur移动原来顺序的下一个位置(cur = temp;)
        • 返回值是新链表的头节点,即pre;
      • 代码:

        • class Solution {
              public ListNode reverseList(ListNode head) {
                  ListNode cur = head;
                  ListNode pre = null;
                  ListNode temp = null;
                  while (cur != null) {
                      temp = cur.next;
                      cur.next = pre;
                      pre = cur;
                      cur = temp;
                  }
                  return pre;
              }
          }
          
    • 解法二:递归

      • 思路:递归的思路很简单,就是把最后两步的赋值通过递归函数实现。

      • 代码:

        • class Solution {
              public ListNode reverseList(ListNode head) {
                  // 初次赋值:
                  // ListNode cur = head;
                  // ListNode pre = null;
                  return reverse(head, null);
              }
          
              public ListNode reverse(ListNode cur, ListNode pre) {
                  if (cur == null) {
                      return pre;
                  }
                  ListNode temp = null;
                  temp = cur.next;
                  cur.next = pre;
                  return reverse(temp, cur);
              }
          }
          

你可能感兴趣的:(leetcode,算法)