leetcode之数组类之数组的旋转与分治类-----OJ 189/33/81/153/154 数组旋转 旋转数组搜索 88 有序数组合并 4 两个有序数组寻找第K个元素/中位数 35 寻找插入位置

本文解析了一系列数组操作的经典题目,包括数组旋转、搜索旋转数组、合并有序数组等,通过实例详细介绍了每种题目的解题思路及代码实现。

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

1、OJ189 rotate array

数组旋转,如给定数组[1,2,3,4,5,6],给定k,如k=2,则结果为[5,6,1,2,3,4],倒数K个节点排在数组前边

数组旋转的方法是:三次旋转

步骤:

1、对倒数K个数先逆序,结果为[1,2,3,4,6,5]

2、对前边的数也做逆序,结果为[4,3,2,1,6,5]

3、再整体逆序,结果为[5,6,1,2,3,4]

OJ189代码:

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        if (nums.empty()) {
            return;
        }
        
        k = k % nums.size();
        
        int st = 0, ed = nums.size() - 1;
        while (st < ed) {
            int tmp = nums[st];
            nums[st] = nums[ed];
            nums[ed] = tmp;
            ++st;
            --ed;
        }
        
        st = 0;
        ed = k - 1;
        while (st < ed) {
            int tmp = nums[st];
            nums[st] = nums[ed];
            nums[ed] = tmp;
            ++st;
            --ed;
        }
        
        st = k;
        ed = nums.size() - 1;
        while (st < ed) {
            int tmp = nums[st];
            nums[st] = nums[ed];
            nums[ed] = tmp;
            ++st;
            --ed;
        }
    }
};

2、OJ33 search in rotate sorted array

如一个已经选择了的数组[4,5,6,1,2,3],寻找5,数组内元素无重复;

本题是考察:数组类题一个重要方法的运用,局部有序情况下如何使用二分查找;

如4、5、6、1、2、3,寻找5,;

理解局部二分就是理解如下点:

0、局部二分的核心思想是:虽然当前是局部,但力图找到确定的查找范围,如果能找到当然好,不能找到也能找到哪一边肯定不对,那么就去找另一边

1、旋转数组左半部分,是数组值较大的部分;

2、二分后,如果中点就等于目标值,那就直接找到了;

      如果中点值大于最左边的值,说明mid是落在旋转数组的部分,即"4、5、6"的部分里,此时应查看,目标值是否在4-6这个区间内

如果目标值就在这个范围内,那么说明就找到了确定的查找范围,在4-6的范围内查找

如果不在这个范围,那么至少说明这个范围不对,换mid以后的范围即[mid+1,right]接着做局部二分查找

      如果中点值小于最左边的值,说明mid落在了未旋转的部分,即"1、2、3"的部分里,此时应查看,目标值是否在1-3区间内

如果在,说明找到了确定的查找范围,在1-3内查找

如不在,说明不是这个范围,在[left, mid-1]接着做局部二分查找

OJ33代码:

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.empty()) {
            return -1;
        }
        
        //局部有序如何使用二分: 或者说如何在二分查找中, 不断确定正确的查找范围;
        //如果mid命中在属于左半部的有序区间(旋转的部分), 这时, 如果目标指介于mid和最左值之间, 则范围确定为最左到mid-1之间;
        //                                                 如果不是这样, 说明目标值至少不在最左到mid-1内, 则在mid+1到最右去查;
        //如果mid落在未旋转的部分即右半部, 这时, 如果目标值介于mid和最右值之间, 则范围确定为mid+1到最右之间;
        //                                    如果不是这样, 说明目标值至少不在mid+1到最右内, 则在left到mid-1去查;
        //旋转数组查找的局部二分的思想是: 设法找到目标值明确所在的范围, 实现完全二分查找
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right)/2;
            if (target == nums[mid]) {
                return mid;
            }
            
            if (nums[mid] >= nums[left]) {
                if (target >= nums[left] && target <= nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (target >= nums[mid] && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        
        return -1;
    }
};

3、OJ81 search in rotate sorted array II

与OJ33区别是,数组内可能有重复的元素

措施为:当发现mid的值与最左和最右都相同,说明是类似[3,3,3,1,2,3,3,3,3,3,3,3,3,3,3,3]这样的情况,需要缩小最左到最右的距离;OJ81比OJ33多出的部分,就针对类似这种情况的处理;处理方式是:最左向右移,最右向左移;

OJ81代码:

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if (nums.empty()) {
            return false;
        }
        
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right)/2;
            if (nums[mid] == target) {
                return true;
            }
            
            //对付类似[3,3,3,1,2,3,3,3,3,3,3,3,3,3,3,3]这样的情况
            if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
                ++left;
                --right;
                continue;
            }
            
            if (nums[mid] >= nums[left]) {
                if (nums[left] <= target && target <= nums[mid]) {
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            } else {
                if (nums[mid] <= target && target <= nums[right]) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        
        return false;
    }
};

4、OJ153 find minimum in rotated sorted array

在旋转过的有序数组中(无重复元素),寻找最小值,如[4,5,6,1,2,3],需要找到最小值1

有OJ33的基础,很容易想到此题的思路:
1、旋转数组的最小值做哪?在右半部分的头

2、所以需要找到右半部分,逐渐消除掉左半部分查找区域

如发现mid的value大于最左,说明处于右半部分,接下来再[mid+1,right]查找

如已经落在右半部分,则right向左移,并不断判断更新最小值

OJ153代码:

class Solution {
public:
    int findMin(vector<int>& nums) {
        if (nums.empty()) {
            return -1;
        }
        
        //最小值在数组右半部分的头, 所以必须设法找到右半部分, 并再找最小值
        int left = 0, right = nums.size() - 1;
        int min = INT_MAX;
        while (left <= right) {
            int mid = (left + right)/2;
            if (min > nums[left]) {
                min = nums[left];
            }
            
            if (nums[mid] >= nums[left]) {
                left = mid + 1;
            } else {
                if (min > nums[mid]) {
                    min = nums[mid];
                }
                right = mid - 1;
            }
        }
        
        return min;
    }
};

5、OJ154 find minimum in rotated sorted array II

和OJ153区别是,数组内可能有重复元素。

有了OJ81的基础后,很清晰该怎么做:当发现mid和最左和最右的value都相同,则说明是[3,3,3,1,2,3,3,3,3,3,3,3,3,3,3,3]这样的情况,令最左和最右往相遇方向压缩

OJ154代码:

class Solution {
public:
    int findMin(vector<int>& nums) {
        if (nums.empty()) {
            return -1;
        }
        
        int left = 0, right = nums.size() - 1, min = INT_MAX;
        while (left <= right) {
            int mid = (left + right)/2;
            if (min > nums[left]) {
                min = nums[left];
            }
            
            if (nums[mid] == nums[left] && nums[mid] == nums[right]) {
                ++left;
                --right;
                continue;
            }
            
            if (nums[mid] >= nums[left]) {
                left = mid + 1;
            } else {
                if (min > nums[mid]) {
                    min = nums[mid];
                }
                right = mid - 1;
            }
        }
        
        return min;
    }
};

6、OJ88 merge sorted array

两个有序数组A和B,合并到A内,A有足够的空间容纳A和B的全部元素

本题考察的是:数组操作的时间复杂度掌握,如果是插入排序式的方式,B的每一个元素插入位置后的A的其他元素都需要移位,移位操作非常多,时间复杂度可近似认为是O(M * N);

在A空间足够的前提下,因为A与B均有序,则不断比较均为从后向前的A与B的元素大小,从A的尾部向头部方向的插入,时间复杂度降为O(M + N);

注意A或B任意一个为空的情况的处理;

OJ88代码:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        while (n) {
            if (m > 0 && nums1[m - 1] > nums2[n - 1]) {
                nums1[m + n - 1] = nums1[m - 1];
                --m;
            } else {
                nums1[m + n - 1] = nums2[n - 1];
                --n;
            }
        }
    }
};

7、OJ4 median of two sorted arrays

求两个有序数组的中位数,或者衍生题:求两个有序数组的第K个元素;要求原地查找,O(N)时间复杂度;

注:本题不愧是hard,而且似乎表面看能搞一搞,但如没有见过没有准备,基本上没可能搞定;


所以,得记下思路和解法:

两个有序数组的第K个数(中位数,按OJ4的题意,如果两个数组元素和为奇数,则为两个数组统一排序后的中间那个元素,如为偶数,则为中间那两个元素的和除以2.0,所以求中位数相当于求中间的两个数组统一排序后的那一两个元素,属于求第K个数),有这样一个规律:

0、由A的前m个数,和B的前n个数,m+n = k,如果:

1、A[m - 1] == B[n - 1],即说明A和B的最小的K个数,最大值是A[m - 1]或B[n - 1](它们俩相等),这本身就已经是第K个数了

2、如果A[m - 1] < B[n - 1],说明A的前m个数都小于B[n - 1],而已知m + n = k,

这说明什么?说明A的前m个数不可能出现第K个数

应该怎么办?缩小包围圈,A去掉前m个数的部分;

3、如果A[m - 1] > B[n - 1],和2同理,说明B的前n个数不可能出现第K个数了,去掉这个范围


去掉了A或B的肯定不存在第K个数的范围后,A或B的剩余部分,加上对方上一轮的部分,继续执行第0步

两个问题点:

1、m和n怎么得来的?答案:按比例,比如A的元素个数为10,B为5,则m = K * (10/(10 + 5)),n = k - m,即A的前m个数,范围为[0, m - 1],B的范围为[0, n-1]

原因:目前描述不太透彻,待续

2、为什么对方的范围不变,还是上一轮的范围,如已知A[m - 1] < B[n - 1],为什么笃定第K个数肯定落在A[m, A.size() - 1]和B[0, n-1]两个区间以内?为什么不会出现在B[n, B.size() - 1]范围呢?

原因:已知m + n = K,已经扣除了A的m个,现在相当于求扣除A的m个后的两个数组的第K-m - 1 = n - 1个数;B的第n - 1后边的数都比B[n - 1]大,A的第m-1后边的都比A[m - 1]大,他们的个数和大于当前要求的第n - 1,所以第n - 1个数肯定存在于这两个部分内;这个一定思考清楚;


具体到代码,边界细节也很多;要考虑:

1、某一方数组已经空了;

2、只需要取剩余两数组的第1个即最小的;

3、另外,必须是数组长度大的数组,求idx = size(大)/(size(大) + size(小)),长度小的数组的idx = k - idx(大)

这个是上面的问题点1


OJ4代码:

class Solution {
public:
    int helper (vector<int> v1, int sz1, vector<int> v2, int sz2, int kth) {
        if (!sz1) {
            return v2[kth - 1];
        }
        if (!sz2) {
            return v1[kth - 1];
        }
        if (kth == 1) {
            if (v1.empty()) {
                return v2[0];
            } else if (v2.empty()) {
                return v1[0];
            }
            return (v1[0] < v2[0])?v1[0]:v2[0];
        }
        
        if (sz1 > sz2) {
            return helper(v2, sz2, v1, sz1, kth);
        }
        
        int idx1 = (kth - 1) * sz1 / (sz1 + sz2), idx2 = kth - idx1 - 2;
        if (v1[idx1] == v2[idx2]) {
            return v1[idx1];
        } else if (v1[idx1] < v2[idx2]) {
            vector<int> newv1, newv2;
            for (int i = idx1 + 1; i < sz1; i++) {
                newv1.push_back(v1[i]);
            }
            for (int i = 0; i <= idx2; i++) {
                newv2.push_back(v2[i]);
            }
            return helper(newv1, newv1.size(), newv2, newv2.size(), kth - idx1 - 1);
        } else {
            vector<int> newv1, newv2;
            for (int i = 0; i <= idx1; i++) {
                newv1.push_back(v1[i]);
            }
            for (int i = idx2 + 1; i < sz2; i++) {
                newv2.push_back(v2[i]);
            }
            return helper(newv1, newv1.size(), newv2, newv2.size(), kth - idx2 - 1);
        }
    }
    
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.empty() && nums2.empty()) {
            return -1;
        } else if (nums1.empty()) {
            if (nums2.size() % 2) {
                return nums2[nums2.size()/2];
            } else {
                return (nums2[nums2.size()/2 - 1] + nums2[nums2.size()/2])/2.0;
            }
        } else if (nums2.empty()) {
            if (nums1.size() % 2) {
                return nums1[nums1.size()/2];
            } else {
                return (nums1[nums1.size()/2 - 1] + nums1[nums1.size()/2])/2.0;
            }
        }
        
        int m = nums1.size(), n = nums2.size();
        if ((m + n) % 2) {
            return helper(nums1, m, nums2, n, (m + n)/2 + 1);
        } else {
            int left = helper(nums1, m, nums2, n, (m + n)/2);
            int right = helper(nums1, m, nums2, n, (m + n)/2 + 1);
            return (left + right)/2.0;
        }
    }
};


8、OJ35 search insert position

在一个无重复有序数组中,如[1,2,3,4,5,],给定K,如K = 0或K=3或k=10,寻找k应该插入在哪个索引处,数组依然还是有序

本题考察二分查找运用

OJ35代码:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        if (nums.empty()) {
            return 0;
        }
        
        if (target <= nums[0]) {
            return 0;
        }
        
        int st = 0, ed = nums.size() - 1;
        while (st <= ed) {
            int mid = (st + ed)/2;
            if (nums[mid] == target) {
                return mid;
            }
            
            if (nums[mid] > target) {
                ed = ed - 1;
            } else {
                st = st + 1;
            }
        }
        
        return st;
    }
};



内容概要:该论文探讨了一种基于粒子群优化(PSO)的STAR-RIS辅助NOMA无线通信网络优化方法。STAR-RIS作为一种新型可重构智能表面,能同时反射和传输信号,传统仅能反射的RIS不同。结合NOMA技术,STAR-RIS可以提升覆盖范围、用户容量和频谱效率。针对STAR-RIS元素众多导致获取完整信道状态信息(CSI)开销大的问题,作者提出一种在不依赖完整CSI的情况下,联合优化功率分配、基站波束成形以及STAR-RIS的传输和反射波束成形向量的方法,以最大化总可实现速率并确保每个用户的最低速率要。仿真结果显示,该方案优于STAR-RIS辅助的OMA系统。 适合人群:具备一定无线通信理论基础、对智能反射面技术和非正交多址接入技术感兴趣的科研人员和工程师。 使用场景及目标:①适用于希望深入了解STAR-RISNOMA结合的研究者;②为解决无线通信中频谱资源紧张、提高系统性能提供新的思路和技术手段;③帮助理解PSO算法在无线通信优化问题中的应用。 其他说明:文中提供了详细的Python代码实现,涵盖系统参数设置、信道建模、速率计算、目标函数定义、约束条件设定、主优化函数设计及结果可视化等环节,便于读者理解和复现实验结果。此外,文章还对比了PSO其他优化算法(如DDPG)的区别,强调了PSO在不需要显式CSI估计方面的优势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值