练习题(2024/5/17)

1按奇偶排序数组 II

给定一个非负整数数组 nums,  nums 中一半整数是 奇数 ,一半整数是 偶数 。

对数组进行排序,以便当 nums[i] 为奇数时,i 也是 奇数 ;当 nums[i] 为偶数时, i 也是 偶数 。

你可以返回 任何满足上述条件的数组作为答案 。

示例 1:

输入:nums = [4,2,5,7]
输出:[4,5,2,7]
解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。

示例 2:

输入:nums = [2,3]
输出:[2,3]

提示:

  • 2 <= nums.length <= 2 * 104
  • nums.length 是偶数
  • nums 中一半是偶数
  • 0 <= nums[i] <= 1000

思路:

  1. 创建一个与输入数组相同大小的空数组 result 用于存放排序后的结果。
  2. 初始化两个变量 evenIndex 和 oddIndex 分别表示结果数组中偶数下标和奇数下标的起始位置,分别为 0 和 1。
  3. 遍历输入数组 nums,对于每个元素:
    • 如果当前元素是偶数,将其放到 result 数组的偶数下标位置 evenIndex,然后将 evenIndex 加 2。
    • 如果当前元素是奇数,将其放到 result 数组的奇数下标位置 oddIndex,然后将 oddIndex 加 2。
  4. 返回排序后的结果数组 result

代码:

class Solution {
public:
    vector<int> sortArrayByParityII(vector<int>& nums) {
        vector<int> result(nums.size()); // 存放结果的数组
        int evenIndex = 0;  // 偶数下标
        int oddIndex = 1;   // 奇数下标
        for (int i = 0; i < nums.size(); i++) {
            // 如果当前数字是偶数
            if (nums[i] % 2 == 0) {
                result[evenIndex] = nums[i]; // 将偶数放到偶数下标位置
                evenIndex += 2; // 更新偶数下标
            }
            // 如果当前数字是奇数
            else {
                result[oddIndex] = nums[i]; // 将奇数放到奇数下标位置
                oddIndex += 2; // 更新奇数下标
            }
        }
        return result; // 返回排序好的结果数组
    }
};

2回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为

回文链表

。如果是,返回 true ;否则,返回 false 。

示例 1:

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

示例 2:

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

提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

思路:

  1. 创建一个空数组 result 用于存放链表中各节点的数值。
  2. 从头节点开始,遍历链表,并将每个节点的数值加入数组 result 中。
  3. 使用双指针的方法判断数组 result 中的数值是否构成回文:
    • 使用两个指针 i 和 j 分别指向数组的头部和尾部。
    • 比较 result[i] 和 result[j] 是否相等,如果不相等则直接返回 false,表示不是回文。
    • 否则,将 i 右移、j 左移,继续比较直到两指针相遇或交叉。
  4. 如果循环结束后两指针没有发生交叉,即全部对比都相等,返回 true,表示链表是回文链表。

代码:

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int> result; // 存放链表节点值的数组
        ListNode* cur = head; // 当前节点指针指向头节点
        // 遍历链表,将节点值存入数组中
        while (cur) {
            result.push_back(cur->val);
            cur = cur->next;
        }
        // 使用双指针判断是否回文
        for (int i = 0, j = result.size() - 1; i < j; i++, j--) {
            if (result[i] != result[j]) return false; // 不相等则不是回文
        }
        return true; // 循环结束仍相等,是回文
    }
};

3重排链表

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

提示:

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

思路:

  1. 创建一个空数组 vec 用于存放链表中的所有节点指针。
  2. 遍历链表,将每个节点的指针存入数组 vec 中。
  3. 初始化指针 cur 指向链表的头节点,同时判断链表是否为空,若为空则直接返回。
  4. 初始化两个指针 i 和 j,分别指向数组 vec 的起始位置和末尾位置。
  5. 使用计数器 count 来判断当前取的是起始位置还是末尾位置的节点。
  6. 循环遍历数组 vec,依次连接链表中的节点,直到 i 不小于等于 j 为止:
    • 如果 count % 2 == 0,则将末尾位置的节点连接到当前节点,并将 j 往前移动;
    • 如果 count % 2 != 0,则将起始位置的节点连接到当前节点,并将 i 往后移动;
    • 每次连接完节点后,将 cur 指向下一个节点,并更新计数器 count
  7. 最后将链表中最后一个节点的 next 指针设为 nullptr,以断开链表。

代码:

class Solution {
public:
    void reorderList(ListNode* head) {
        vector<ListNode*> vec; // 用于存放链表节点指针的数组
        ListNode* cur = head; // 当前节点指针指向头节点
        if (cur == nullptr) return; // 如果链表为空,直接返回
        // 将链表节点指针存入数组中
        while (cur != nullptr) {
            vec.push_back(cur);
            cur = cur->next;
        }
        cur = head; // 重新将当前节点指针指向头节点
        int i = 1; // 指向数组的起始位置
        int j = vec.size() - 1; // 指向数组的结束位置
        int count = 0; // 计数器,用于判断是取末尾节点还是起始节点
        // 重新连接链表节点
        while (i <= j) {
            if (count % 2 == 0) {
                cur->next = vec[j]; // 连接末尾节点
                j--;
            } else {
                cur->next = vec[i]; // 连接起始节点
                i++;
            }
            cur = cur->next;
            count++;
        }
        cur->next = nullptr; // 将最后一个节点的next指针设为nullptr,断开链表
    }
};

4 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

快慢指针的方式来判断一个链表是否存在环。快慢指针分别以不同的速度遍历链表,如果链表中存在环,最终快指针会追上慢指针。

具体解题思路如下:

  1. 初始化两个指针 fast 和 slow,都指向链表的头节点。
  2. 使用一个循环迭代,循环条件为 fast != NULL && fast->next != NULL,这是为了保证快指针可以继续向前走。
  3. 在循环内部,慢指针 slow 每次向前移动一步,快指针 fast 每次向前移动两步。
  4. 如果存在环,快慢指针最终会相遇,此时退出循环,并返回 true 表示链表中存在环。
  5. 如果快指针 fast 或者 fast->next 为 NULL,说明已经到达链表尾部,链表中不存在环,返回 false

代码:

class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode* fast = head; // 初始化快指针指向头节点
        ListNode* slow = head; // 初始化慢指针指向头节点
        // 使用快慢指针判断链表是否有环
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next; // 慢指针移动一步
            fast = fast->next->next; // 快指针移动两步
            // 如果快慢指针相遇,说明链表有环
            if (slow == fast) return true;
        }
        return false; // 如果循环结束后没有相遇,说明链表无环
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值