杂碎算法题

移动零

题目

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = [0,1,0,3,12]输出: [1,3,12,0,0]

示例 2:

输入: nums = [0]输出: [0]

提示:

  • 1 <= nums.length <= 10(4)

  • -2(31) <= nums[i] <= 2(31) - 1

进阶:你能尽量减少完成的操作次数吗?

题目思路

核心思想:双“指针”(此指针的意思为为数组下标)

题目代码

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int dest=-1,cur=0;
        for(int i=0;i<nums.size();i++){
            if(nums[cur]==0) cur++;
            else{
                swap(nums[dest+1],nums[cur]);
                dest++;
                cur++;
            }
        }
    }
};
#include <bits/stdc++.h>
using namespace std;
int main(){
        int x;
        vector<int> a;
        while(scanf("%d",&x)!=EOF){
                a.push_back(x);
        }
        
        int dest=-1,cur=0;
        for(int i=0;i<a.size();i++){
                if(a[cur]==0) cur++;
                else{
                        swap(a[dest+1],a[cur]);
                        dest++;
                        cur++;
                }
        }
        for(int i=0;i<a.size();i++){
                cout<<a[i];
                if(i!=a.size()-1) cout<<" ";
        } 
        return 0;
}

复写零

题目

给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例 1:

输入:arr = [1,0,2,3,0,4,5,0]
输出:[1,0,0,2,3,0,0,4]
解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]

示例 2:

输入:arr = [1,2,3]
输出:[1,2,3]
解释:调用函数后,输入的数组将被修改为:[1,2,3]

提示:

  • 1 <= arr.length <= 10(4)

  • 0 <= arr[i] <= 9

代码一:

#include <bits/stdc++.h>
using namespace std;
int main(){
        int x;
        vector<int> a;
        while(cin>>x){
                a.push_back(x);
        }
        int n=a.size();
        for(int i=0;i<a.size();i++){
                if(a[i]==0){
                        a.insert(a.begin()+i,0);
                        i++;
                }
        }
        for(int i=0;i<n;i++){
                cout<<a[i]<<" ";
        }
        return 0;
}

题目思路

  • 用双指针,从前往后?从后往前?

    • 从前往后,会覆盖掉掉原有的值

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 考虑从后向前

      • 怎么把两个指针移动后后面呢?(第一步)

        • 1.看cur位置的值

          • 不是0——dest后移一位

          • 是0——dest后移两位

        • 2.判断dest是否到最后

          • 是——结束,此时cur指向规定长度内最后一位有值的数字

          • 不是——cur++

      • 判断dest是否出界(第1.5步)

        • 如果出界:

          • a[n-1]=0;

          • dest-=2;

          • cur–;

      • 从后向前操作(第二步)

        • cur位置非0——cur的值赋值到dest位置

          • 两个指针各自向前一位
        • cur位置为——dest位置为0,向前一位,然后再赋值为0

          • cur–

题目代码

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur=0,dest=-1;
        int n=arr.size();
        while(cur<n){
            if(arr[cur]) dest++;
            else dest+=2;
            if(dest>=n-1) break;
            cur++;
        }

        if(dest==n){
            arr[n-1]=0;
            cur--;
            dest-=2;
        }

        while(cur>=0){
            if(arr[cur]){
                arr[dest--]=arr[cur--];
            }
            else{
                arr[dest--]=0;
                arr[dest--]=0;
                cur--;
            }
        }
    }
};
#include <bits/stdc++.h>
using namespace std;
int main(){
        int x;
        vector<int> a;
        while(cin>>x){
                a.push_back(x);
        }
        int cur=0,dest=-1;
        int n=a.size();
        
        //1.指针后移
        while(cur<n){
                if(a[cur]) dest++;
                else dest+=2;
                
                if(dest>=n-1) break;
                cur++;
        }
        //2.越界判断
        if(dest==n){
                a[n-1]=0;
                dest-=2;
                cur--;
        }
        
        //3.从后向前操作
        while(cur>=0){
                if(a[cur]) a[dest--]=a[cur--];
                else{
                        a[dest--]=0;
                        a[dest--]=0;
                        cur--;
                }
        }
        
        for(int i=0;i<n;i++){
                cout<<a[i];
                if(i!=n-1) cout<<" ";
        }
        return 0;
}

变式

如果复写n次呢?

现在考虑更一般的情况:如果题目要求将每个零复写 n 次(即每个零变成 n+1 个零,包括原始的那个零),思路需要做以下调整:

调整后的思路
  1. 第一步:指针后移阶段

    • 如果 cur 位置的值不是 0,dest 后移 1 位(非零元素仍占 1 个位置)。

    • 如果 cur 位置的值是 0,dest 后移 (n+1) 位(因为每个零要复写 n 次,加上原始的 1 个零,共 n+1 个位置)。

    • 当 dest 达到或超过 n-1 时,停止遍历。

  2. 第1.5步:越界判断

    • 如果 dest 等于 n:

      • 将 a[n-1] 设置为 0(占用最后一个位置)。

      • dest 回退 (n+1) 位(因为一个零占用 n+1 个位置)。

      • cur 回退 1 位。

  3. 第二步:从后向前操作

    • 如果 cur 位置的值不是 0,将 a[cur] 赋值给 a[dest],dest 和 cur 各向前移动 1 位。

    • 如果 cur 位置的值是 0,从 dest 开始的 (n+1) 个位置都赋值为 0,dest 向前移动 (n+1) 位,cur 向前移动 1 位。

关键变化
  • 空间需求增加:每个零从占用 2 个位置变为 (n+1) 个位置,因此 dest 的移动步幅和填充范围都需要根据 n 调整。

  • 代码调整

    • 指针后移时,dest += 2 改为 dest += (n+1)。

    • 越界时,dest -= 2 改为 dest -= (n+1)。

    • 填充时,从赋值 2 个零改为赋值 (n+1) 个零。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int x, n;
    vector<int> a;
    cin >> n; // 输入复写次数 n
    while (cin >> x) {
        a.push_back(x);
    }
    int cur = 0, dest = -1;
    int m = a.size();

    // 1. 指针后移
    while (cur < m) {
        if (a[cur]) dest++;
        else dest += (n + 1);
        if (dest >= m - 1) break;
        cur++;
    }

    // 2. 越界判断
    if (dest == m) {
        a[m-1] = 0;
        dest -= (n + 1);
        cur--;
    }

    // 3. 从后向前操作
    while (cur >= 0) {
        if (a[cur]) a[dest--] = a[cur--];
        else {
            for (int i = 0; i <= n; i++) {
                a[dest--] = 0;
            }
            cur--;
        }
    }

    // 输出结果
    for (int i = 0; i < m; i++) {
        cout << a[i];
        if (i != m - 1) cout << " ";
    }
    return 0;
}

快乐数

题目

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。

  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。

  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
1(2) + 9(2) = 82
8(2) + 2(2) = 68
6(2) + 8(2) = 100
1(2) + 0(2) + 0(2) = 1

示例 2:

输入:n = 2
输出:false

提示:

  • 1 <= n <= 2(31) - 1
posted @ 2025-07-25 21:37  dearbi  阅读(0)  评论(0)    收藏  举报  来源