剑指offer 数组 持续更新中...

1. 数组中重复的数字

1.1 问题描述

LCR 120. 寻找文件副本 - 力扣(LeetCode)

题目描述:在一个长度为n的数组中所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,

也不知道每个数字重复了几次。请找出一个重复的数字。

示例 1:

输入:nums = [2, 3, 1, 0, 2, 5, 3]
输出:2 或 3

1.2 方法1: 排序

class Solution {
public:
    int findRepeatDocument(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        size_t n = nums.size();
        for (int i = 1; i < n; ++i) {
            if (nums[i - 1] == nums[i])
                return nums[i - 1];
        }
        return -1;
    }
};

1.3 方法2: 哈希表

用哈希表统计次数,空间复杂度是O(n)

class Solution {
    std::unordered_map<int, int> hash;
public:
    int findRepeatDocument(vector<int>& nums) {
        int n = nums.size();
        for(int i = 0; i < n; ++i) {
            if(hash[nums[i]] >= 1)     return nums[i];
            else hash[nums[i]]++;
        }
        return -1;
    }
};

1.4 方法3: 原地交换

注意到数组中的数字都在0~n-1的范围内,如果这个数组中没有重复的数字,那么当数组排序之后数字i将会出现在下标为i的位置。具体见剑指offer 39页

class Solution {
public:
    int findRepeatDocument(vector<int>& nums) {
        int n = nums.size();
        for(int i = 0; i < n; ++i) {
            while(i != nums[i]) {
                // printN();
                if(nums[i] == nums[nums[i]])    return nums[i];
                swap(nums[i], nums[nums[i]]);
            }
        }
        return -1;
    }

    void printN(vector<int>& nums, int n)
    {
        for(int i=0;i<n;++i) 
            std::cout << nums[i] << ' ';
        std::cout <<'\n';
    }
};

虽然有一个双重循环,大师每个数字最多只要交换两次就能找到属于它自己的位置,时间复杂度是O(n)

2. 寻找重复数

2.1 问题描述

287. 寻找重复数 - 力扣(LeetCode)

题目描述:给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

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

2.2 方案1,使用辅助数组

开一个n+1长度的数组,如果原数组的数字是m,那么把它复制到下标为m的位置。时间复杂度和空间复杂度都是O(n)

class Solution {
    size_t n;

public:
    int findDuplicate(vector<int>& nums1) {
        n = nums1.size();
        vector<int> nums2(n + 1);
        for (int i = 0; i < n; ++i) {
            int m = nums1[i];
            if (nums2[m] == m)
                return m;
            nums2[m] = m;
        }
        return -1;
    }
};

2.3 方案2,使用二分

1~n的数字从中间的数字m分为两部分,前半部分为1~m,后半部分为m+1~n。如果前半部分数字的数目超过m,那么这一半的区间里一定包含重复的数字。(抽屉原理)。一直分下去,直到找到重复的数字。时间复杂度是O(logN),用时间换空间。该题解很不错

class Solution {
    size_t n;

public:
    int findDuplicate(vector<int>& nums) {
        n = nums.size();
        // 长度为n+1,数字范围都在[1,n]
        int left = 1, right = n - 1;
        while (left <= right) {
            int mid = (right - left) / 2 + left;
            // std::cout << mid << ' ';
            int cnt = countNum(nums, left, mid);
            if (left == right) {
                // 找到了
                return left;
            }
            if (cnt > mid - left + 1)
                right = mid;
            else
                left = mid + 1;
        }
        return -1;
    }

    // 扫描整个数组,统计数字范围在[left, right]的每个数字 在数组中出现的次数
    int countNum(const vector<int>& nums, int left, int right) {
        int cnt = 0;
        for (int i = 0; i < n; ++i) {
            if (nums[i] >= left && nums[i] <= right)
                cnt++;
        }
        return cnt;
    }
};

3. 二维数组的查找

##3.1 问题描述

240. 搜索二维矩阵 II - 力扣(LeetCode)

题目描述:编写一个高效的算法来搜索 *m* x *n* 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例1

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

image-20250130220928183

3.2 方案1,贪心

以上图为例子,从左下角18数字开始分析,由于18是这一行中最小的数字,而5(target)比它还要小,证明5在第5行之上。
同理,可以去除第4行。此时3是第1列中最大的数字,而5比3还要大,证明5在第1列之左。
此时6是第3行中最小的数字,而5比6还要小,证明5在第3行之上。
这样就找到了该数字

class Solution {
    int m, n, now;

public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        m = matrix.size() - 1, n = matrix[0].size() - 1, now = -1;
        int x = m, y = 0;
        while (x >= 0 && y <= n) {
            now = matrix[x][y];
            if (now > target) {
                x--;
            } else if (now < target) {
                y++;
            } else {
                return true;
            }
        }
        return false;
    }
};

时间复杂度O(m+n)

4. 合并两个有序数组

4.1 问题描述

88. 合并两个有序数组 - 力扣(LeetCode)

问题描述:有两个按非递减顺序排列的整数数组的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2。实现一个函数。把A2中的所有数字插入在A1中,并且所有的数字是排序的

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

4.2 方案1,从前向后替换

替换空格_牛客题霸_牛客网类似,可以从头到尾替换数字

class Solution 
{
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) 
    {
        int left = 0, right = 0;
        while (left < m && right < n) {
            if (nums2[right] < nums1[left]) {
                // 把nums1[left]~nums1[m-1]所有的数字向后移动一位
                for (int i = m - 1; i >= left; --i)
                    nums1[i + 1] = nums1[i];
                m++;
                nums1[left] = nums2[right];
                right++;
            } else {
                left++; 
            }
        }
        if (left == m) {
            // nums1走完了, 把nums2剩下的全部拷贝过来
            for (int i = right; i < n; ++i)
                nums1[left++] = nums2[i];
        }
    }
};

4.2 方案2, 从后向前替换

class Solution 
{
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) 
    {
        int left = m - 1, right = n - 1, end = m + n - 1;
        while (left >= 0 && right >= 0) {
            // if (nums1[left] > nums2[right]) {
            //     nums1[end--] = nums1[left--];
            // } else {
            //     nums1[end--] = nums2[right--];
            // }
            nums1[end--] = nums1[left] > nums2[right] ? nums1[left--] : nums2[right--];
        }
        if (left < 0) {
            // nums1走完了, 把nums2剩下的全部拷贝过来
            for (int i = right; i >= 0; --i)
                nums1[end--] = nums2[i];
        }
        // nums2走完了, nums1没走完, 无所谓, 不用特殊处理, 因为nums1就是我们要返回的数组
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值