20201023:算法题

本文介绍了两种使用双指针和递归解决的算法问题:一是如何原地移除数组中的指定元素,二是如何判断链表是否为回文。对于数组问题,通过双指针技巧可以在O(1)额外空间下完成;对于回文链表,利用递归实现高效检查。同时,文章对比了不同解法的空间和时间复杂度,提供了官方的动画解法来深入理解递归思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法题一:移除元素

1、给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

2、不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。

示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。

这个题目和之前清楚相同元素类似,都可以使用双指针解决:

class Solution {
    public int removeElement(int[] nums, int val) {
        int length = nums.length;
        int i = 0;
        for (int j = 0; j < length; j++) {
            if (nums[j] != val) {
                nums[i] = nums[j];
                i++;
            }
        }

        //System.out.println(Arrays.toString(nums));

        return i;
    }
}

【思路】:
1、首先定义一个指针i,指向当前数组中最后一个有效的元素的后一位;
2、另外一个指针j用于遍历数组元素,查找与val不同的值;
3、如果遇到了这个值,让当前的i指针指向的元素等于这个值,然后i指针挪动一位;
4、最终返回i索引

算法题二:回文链表

  • 请判断一个链表是否为回文链表。
示例 1:
输入: 1->2
输出: false

示例 2:
输入: 1->2->2->1
输出: true

自己的做法:

class Solution {
    public boolean isPalindrome(ListNode head) {
        List<Integer> list = new ArrayList();
        while (head != null) {
            list.add(head.val);
            head = head.next;
        }

        int length = list.size();
        boolean flag = true;
        for (int i = 0; i < length / 2; i++) {
            if ((int) list.get(i) != (int) list.get(length - i - 1)) {
                flag = false;
                break;
            }
        }

        return flag;
    }
}

【思路】:

1、while循环遍历整个链表,将其中所有的值存储在list集合中;
2、for循环遍历list/2次;
3、如果当前位置i上的元素与length - i - 1位置的元素不相等,循环终止,返回false;
4、否则返回true;
这个解法理解起来容易,但是它的时间和空间开销较大;

官方的解法:

我录制了官方给出来的动画解法:递归

在这里插入图片描述

代码:

class Solution {
    private ListNode frontPointer;

    private boolean recursivelyCheck(ListNode currentNode) {
        if (currentNode != null) {
            if (!recursivelyCheck(currentNode.next)) {   //语句1
                return false;
            }
            if (currentNode.val != frontPointer.val) {
                return false;
            }
            frontPointer = frontPointer.next; //语句2
        }
        
		//如果currentNode结点为空,返回true之后,语句1此时也执行完了,这个方法就弹栈了
		//这个currentNode指向的结点也就被释放资源了
		//而此时的这个currentNode是刚才为null的结点的前一个结点
        return true;
    }

    public boolean isPalindrome(ListNode head) {
        frontPointer = head;
        return recursivelyCheck(head);
    }
}

【总结】:

1、给定两个变量,一个是递归函数外面的指针,一个是方法的形参;
2、首先使用递归的做法,使得currentNode指向链表中的最后一个结点;
3、此时frontPointer指针指向的是链表的第一个结点;
4、frontPointer结点依次向后指,而currentNode指针随着方法的弹栈,依次逆向指向结点;
5、如果两个指针指向的结点这时候的值不相等,返回false,整个方法弹栈;
6、语句1方法的进栈弹栈规定了currentNode指针的指向,语句2则规定了递归函数外的frontPointer指针指向;
7、currentNode在每次方法弹栈之后都会向前挪动一位,但是frontPointer指针只有在两个值相等的时候才挪动一次;
8、这个最终返回的结果是由所有次的对比之后的共同结果,如果当前判断中,前面递归方法返回false,整个方法直接返回false;如果前面返回false,但是这一次值不相等,也返回false;
9、也就是说,只有满足回文特征的链表最终返回true;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值