算法入门-递归2

第四部分:递归

231. 2的幂 (简单)

题目:给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false

如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

示例 1:

输入:n = 1
输出:true
解释:20 = 1

示例 2:

输入:n = 16
输出:true
解释:24 = 16

第一种思路:

还是往递归方面靠,结果官方又是没有给出递归的解。。。

思路应该还是没有问题,就是会超限。

思路总结:

  1. 基本知识

    • 2 的幂的特性是:只有 1、2、4、8、16 等能被表示为 2n2n 的数。

    • 通过逐步翻倍,可以判定一个数是否是 2 的幂。因为我的想法是如果用从除法会要考虑一下计算机除数取整的问题。

  2. 递归检查

    • 首先检查基准条件。

      • 如果 n 为 0,直接返回 false。

      • 如果 n 是 1 或 2,返回 true,因为它们是 2 的幂。

      • 如果 n 小于当前的检查值 (pro),返回 false,说明 n 不是 2 的幂。

      • 如果 n 等于 pro,返回 true,说明 n 是 2 的幂。

  3. 自我递增

    • 如果以上条件都不满足,将 pro 值翻倍,然后再次调用 isPowerOfTwo 方法,继续检查。

  4. 终止条件

    • pro 大于 n 而且 n 也没有在之前的检查中被认定为 2 的幂时,最终会返回 false。

class Solution {  
    // 初始化 pro 为 4,每次递归调用中 pro 会乘以 2  
    int pro = 4;   
    
    public boolean isPowerOfTwo(int n) {  
        // 如果 n 等于 0,返回 false,因为 0 不是 2 的幂  
        if (n == 0) return false;  
        // 如果 n 等于 1 或 2,返回 true,因为 1 和 2 是 2 的幂  
        else if (n == 1 || n == 2) return true;   
        // 如果 n 小于 pro,返回 false,因为 n 不能是大于 2 的幂  
        else if (n < pro) return false;   
        // 如果 n 等于 pro,返回 true,因为 n 是 2 的幂  
        else if (n == pro) return true;  

        // 将 pro 倍增,以便检查下一个 2 的幂  
        pro = pro * 2;   
        // 递归调用,以判断 n 是否是 2 的幂  
        return isPowerOfTwo(n);  
    }  
}

第二种思路:

这种是官方给出的解题思路,我觉得有必要了解并掌握,以前确实没有想过这个问题。

一个数 n 是 2 的幂,当且仅当 n 是正整数,并且 n 的二进制表示中仅包含 1 个 1。

因为二进制是逢二进一。

所有考虑使用位运算,将 n 的二进制表示中最低位的那个 1 提取出来,再判断剩余的数值是否为 0 即可。

有两种常见的与「二进制表示中最低位」相关的位运算技巧。

第一个技巧是:

n & ( n - 1 )

其中 & 表示按位与运算。该位运算技巧可以直接将 n 二进制表示的最低位 1 移除.

原理直接用LeetCode的解释了,我觉得他讲的听清楚的,其实也就是为了判断想与后为0即可。

解释:

因此,如果 n 是正整数并且 n & (n - 1) = 0,那么 n 就是 2 的幂。

class Solution {
    public boolean isPowerOfTwo(int n) {
        return n > 0 && (n & (n - 1)) == 0;
    }
}
第二个技巧是:

n & ( - n )

其中 −nn 的相反数,是一个负数。该位运算技巧可以直接获取 n 二进制表示的最低位的 1。

由于负数是按照补码规则在计算机中存储的,−n 的二进制表示为 n 的二进制表示的每一位取反再加上 1,

原理:

因此,如果 n 是正整数并且 n & (-n) = n,那么 n 就是 2 的幂。

class Solution {
    public boolean isPowerOfTwo(int n) {
        return n > 0 && (n & -n) == n;
    }
}

234.回文链表(简单)

题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

示例 1:

输入:head = [1,2,2,1]
输出:true

第一种思路:

这道题一开始没想到用递归怎么做,所以没继续往哪方面思考,然后想到之前不是写过数组的回文序列嘛,化未知为已知,所以可以先把链表中的数值先取出来放在LIst数组中。

  1. 创建列表:首先创建一个 ArrayList 来存储链表中所有节点的值。这一步是为了方便后续进行比较。

  2. 遍历链表:通过 while 循环遍历链表,将每个节点的值依次添加到 list 中,直到链表的末尾(即 head 变为 null)。

  3. 双指针比较

    • 使用两个指针,i 从列表的开头开始,j 从列表的末尾开始。

    • 在一个 for 循环中,同时向中间移动这两个指针。

    • 每次比较 list.get(i)list.get(j) 的值。

    • 如果发现有不相等的值,则返回 false,表示链表不是一个回文。

  4. 返回结果:如果所有对应位置的值都相等,那么链表就是回文,函数返回 true

/**
 * 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 boolean isPalindrome(ListNode head) {  
        // 创建一个动态数组,用于存储链表的值  
        List<Integer> list = new ArrayList<>();  
        
        // 遍历链表,将节点的值存入数组  
        while (head != null) {  
            list.add(head.val);  // 将当前节点的值添加到列表  
            head = head.next;     // 移动到下一个节点  
        }  
        
        // 使用双指针方法检查列表是否为回文  
        for (int i = 0, j = list.size() - 1; i <= j; i++, j--) {  
            // 如果对应位置的值不相等,返回 false  
            if (!list.get(i).equals(list.get(j))) {  
                return false; // 当前值不相等,说明不是回文  
            }  
        }  
        
        // 如果所有值都匹配,返回 true  
        return true; // 链表是回文  
    }  
}  

第二种思路:

这种递归的思路核心点是递归在处理节点的顺序是相反的。

理解递归
  • 基本情况:这个递归函数持续执行,直到 currentNodenull。这表示它已经到达了链表的末尾。当 currentNodenull 时,函数不会执行 if 语句的主体,而是直接返回 true

  • 递归调用:当调用 recursivelyCheck(currentNode.next) 时,实际上是移步到下一个节点。这个过程会继续递归,直到到达链表的最后一个节点。这时,currentNode 将会是最后一个节点,之后再调用 currentNode.next 就会返回 null

逻辑分析
  • 当链表的最后一个节点被访问时,currentNode 仍然是一个有效的节点。在执行 recursivelyCheck 时,它会递归访问链表的所有节点,并最终在到达末尾时返回 true,然后逐层向上返回。

  • 每层递归都会检查当前节点的值与 frontPointer 的值,如果不相等则返回 false。只有在完成所有递归调用并且检查都通过的情况下,才会返回 true

class Solution {  
    // 用于指向链表的前半部分的指针  
    private ListNode frontPointer;  

    // 递归检查链表是否为回文  
    private boolean recursivelyCheck(ListNode currentNode) {  
        // 如果当前节点不为空  
        if (currentNode != null) {  
            // 递归检查下一个节点  
            if (!recursivelyCheck(currentNode.next)) {  
                return false; // 如果下一个节点不满足条件,返回 false  
            }  
            // 比较当前节点的值与前指针指向的值  
            if (currentNode.val != frontPointer.val) {  
                return false; // 如果不相等,返回 false  
            }  
            // 移动前指针到下一个节点  
            frontPointer = frontPointer.next;  
        }  
        // 如果所有节点都匹配,返回 true  
        return true;  
    }  

    // 主方法,检查链表是否为回文  
    public boolean isPalindrome(ListNode head) {  
        // 初始化前指针为链表的头节点  
        frontPointer = head;  
        // 开始递归检查链表  
        return recursivelyCheck(head);  
    }  
}

  • 29
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值