代码随想录算法训练营第三天打卡| Leetcode 203、Leetcode 707、Leetcode 206

代码随想录算法训练营第三天打卡| Leetcode 203、Leetcode 707、Leetcode 206

Leetcode 203 移除链表元素

题目连接
关键点:

  1. 移除链表元素的逻辑&虚拟头节点的方案
    逻辑上移除链表元素只需要让前一个节点的指针指向下一个节点(注意内存释放),由此引申出移除节点的两种类型:

    1. 非头节点:前一个节点指向下一个节点
    2. 头节点:头节点指针后移【头节点没有前一个节点】

    对头节点特殊的原因做出分析之后,发现在头节点前引入一个虚拟头节点dummy head就可以统一操作

  2. 移除元素是while操作
    在链表中找到target后,有可能后面几点节点都是相同的元素,因而移除元素是一个持续不断的过程,使用while循环

  3. 单向列表移除元素时如果不额外使用pre指针记录上一个节点,那么cur指针要从头节点开始(判断:cur.next== target;移除:cur.next = cur.next.next)【不然怎么寻找上一个节点呢】

  4. 不能用头节点进行遍历,需要使用临时指针遍历;最后需要return原先列表的头节点

  5. 避免操作空指针:在取值前先判断节点是否为空

虽然要注意的点很多,但是实际实现还是很简单的。

解答

class Solution {
    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;		//原先的头节点可能被移除,新的头节点已经不是head
    }
}

Leetcode 707 设计链表

题目链接【节点从0开始计算,0 为头节点】
关键点:

  1. 获取第n个节点的值:注意题目中的n从0开始还是从1开始
  2. 头部插入节点:注意节点插入的顺序;注意当插入位置为链表后一位时仍然插入
  3. 尾部插入节点:cur.next == null到达尾部
  4. 第n个节点前插入节点:插入节点需要知道前一个节点,因而在第n个节点前插入节点时cur指向第n-1个节点,cur.next指向第n个节点
  5. 删除第n个节点:删除节点需要知道前一个节点,因而在第n个节点前插入节点时cur指向第n-1个节点,cur.next指向第n个节点
  6. 要考虑到index不合法的输入
  7. 具体循环次数的确定:举例法

解答

// 单链表
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 {
    int size;
    ListNode head;  //虚拟头节点

    public MyLinkedList() {
        this.size = 0;
        head = new ListNode(0);
    }
    
    public int get(int index) {
        // 如果index非法,返回-1
        if (index < 0 || index >= size)
            return -1;
        ListNode cur = head;
        // 从0开始编号,从虚拟头节点开始查找第index+1个节点要index+1次循环
        while(index >= 0){
            cur = cur.next;
            index--;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        ListNode addedNode = new ListNode(val);
        // 考虑到链表当中可能还没有节点,cur统一初始化为虚拟头节点
        ListNode cur = head;
        addedNode.next = cur.next;
        cur.next = addedNode;
        size++;
//        printList();
    }
    
    public void addAtTail(int val) {
        ListNode cur = head;
        for (int i = size; i > 0; i--){
            cur = cur.next;
        }
        ListNode addedNode = new ListNode(val);
        cur.next = addedNode;
        size++;
//        printList();
    }
    
    public void addAtIndex(int index, int val) {
        // 如果index非法,返回
        if (index < 0 || index > size)
            return;
        ListNode cur = head;
        // 在n插入,cur在n-1处,循环n次
        while(index > 0){
            cur = cur.next;
            index--;
        }
        ListNode addedNode = new ListNode(val);
        addedNode.next = cur.next;
        cur.next = addedNode;
        size++;
//        printList();
    }
    
    public void deleteAtIndex(int index) {
        // 如果index非法,返回
        if (index < 0 || index >= size)
            return;
        ListNode cur = head;
        // 删除第n个节点,cur要在n-1处,循环n次
        while(index > 0){
            cur = cur.next;
            index--;
        }
        cur.next = cur.next.next;
        size--;
//        printList();
    }

    public void printList(){
        ListNode cur = head;
        for (int i = 0; i < size; i++) {
            System.out.printf("%d ", cur.next.val);
            cur = cur.next;
        }
        System.out.println();
    }
}

/**
 * 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);
 */

Leetcode 206 反转链表

题目链接
方法?:遍历链表,每个节点都插入在头节点上(虽然本质上是构造了一个ReverseList的结构,并没有对原链表进行反转,但是能过测试lmao)

解答?

class Solution {
	ListNode dummy = new ListNode(0);;

    public ListNode reverseList(ListNode head) {
		ListNode cur = head;
		while (cur != null){
			addAtHead(cur.val);
			cur = cur.next;
		}
		return dummy.next;
    }

	public void addAtHead(int val){
		ListNode addedNode = new ListNode(val);
		ListNode cur = dummy;
		addedNode.next = cur.next;
		cur.next = addedNode;
	}
}

方法1:双指针法。在改变指向的过程中为了避免断链需要用临时的
temp指针记录下一个节点(即pre->cur->temp)
注意点:

  1. cur的初始值为head,pre指向cur前一个节点,初始值为null
  2. 反转过程的顺序是固定的:记录cur下一个节点->反转cur->更新pre->更新cur
  3. 反转链表后的新头节点为pre

解答1

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;
    }
}

方法2:写成递归的形式。和双指针法相同逻辑但是写法更加简洁

解答2

注意点:

  1. 完全依照双指针法的更新逻辑进行递归参数的确定
class Solution {
    public ListNode reverseList(ListNode head) {
		return reverse(null, head);	// pre和cur的初始值
    }

	public ListNode reverse(ListNode pre, ListNode cur){
		if (cur == null){
			return pre;
		}
		ListNode temp = cur.next;
		cur.next = pre;
		return reverse(cur, temp);	// 依据双指针法的逻辑代入参数
	}
}

第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值