Leetcode刷题 由浅入深之数组——977:有序数组的平方

 目录

(一)排序算法的C++实现

写法一(插入排序)

写法二(冒泡排序)

写法三(数组指针排序)

写法四(快速排序)

(二)复杂度分析

时间复杂度

空间复杂度

(三)总结


【题目链接】977.有序数组的平方 - 力扣(LeetCode)

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104

  • -104 <= nums[i] <= 104

  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

(一)排序算法的C++实现

解题思路

        由题目可知,整数数组有序,但有正有负。排序之前,可以先循环遍历数组,分别将对应位置上的元素变为自己的平方。之后再按照非递减顺序(递增但可以有相同元素)排序。

写法一(插入排序)

排序思路

        双层for循环实现插入排序。从第二个元素开始(外层for循环),依次与它前面所有的元素进行比较(内层for循环),如果该元素小于它前面的元素,则插入它前面的元素之前(通过交换位置实现)。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i<nums.size(); i++){
            nums[i] = nums[i] * nums[i];
        }
        int temp = 0;
        for(int i = 1; i<nums.size(); i++){    //遍历数组的所有元素
            for(int j = i; j>0; j--){    //遍历当前元素之前的所有元素
                if(nums[j] < nums[j-1]){
                    temp = nums[j];
                    nums[j] = nums[j-1];
                    nums[j-1] = temp;
                }
            }
        }
        return nums;
    }
};

写法二(冒泡排序)

排序思路

        双层for循环实现冒泡排序。从第一个元素开始(外层for循环),依次将整个数组中未放的最大的数移到最后一个可放的位置(内层for循环),如果该元素小于它后面的元素,则交换位置。

        将数组看作nums.size()个位置,依次将倒数最大的数放在最后的位置上,已经放过的位置则不能再次放入。(从前往后找,大的放后面)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i<nums.size(); i++){
            nums[i] = nums[i] * nums[i];
        }
        int temp = 0;
        for(int i = 0; i<nums.size()-1; i++){    //遍历数组的所有元素
            for(int j = 0; j<nums.size()-i-1; j++){    //遍历数组除倒数第i+1大的所有元素
                if(nums[j] > nums[j+1]){
                    temp = nums[j+1];
                    nums[j+1] = nums[j];
                    nums[j] = temp;
                }
            }
        }
        return nums;
    }
};

写法三(数组指针排序)

排序思路

        利用题意:因为题目给定的初始数组是有序的(非递减的),则平方后最大值一定出现在两端,依次比较两端的值即可逐步找到整个数组未放元素的最大值。

        单层循环实现双指针排序。

(1)定义两个数组指针,指向原始数组的首尾元素;定义一个新的数组存放排序后的结果。

(2)如果左指针所指元素大于右指针,则将左指针元素放入结果数组中,并且左指针右移;

如果右指针所指元素大于左指针,则将右指针元素放入结果数组中,并且右指针左移。

(3)重复第(2)步,将平方后数组两端最大值依次放入结果数组的最后可放入的位置(在结果数组中从后往前放)。(从两端找,大的放后面)

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i<nums.size(); i++){
            nums[i] = nums[i] * nums[i];
        }
        std::vector<int> resultList(nums.size());
        int i = 0, j = nums.size()-1;
        int k = nums.size()-1;
        while(k>=0){
            if(nums[i] > nums[j]){
                resultList[k] = nums[i];
                i++;
            }else{
                resultList[k] = nums[j];
                j--;
            }
            k--;
        }
        return resultList;
    }
};

写法四(快速排序)

排序思路

        递归实现快速排序。

(1)定义两个数组指针,指向原始数组的首尾元素;定一个基准key,即右指针所指元素。

(2)每次将比key小的数放在它的左边,将比key大的数放在它的右边(partsort函数实现)。

(3)递归基准两侧,分别看作两个新数组,重复步骤(2)(递归调用partsort函数实现)。

(4)若左指针与右指针重合或越过,则说明子数组中的元素顺序已经排好,调用结束(quicksort函数实现)。

void change(int &a, int &b){
            int temp = 0;
            temp = a;
            a = b;
            b = temp;
        }
        
int partsort(vector<int>& a, int left, int right){
    int key = right;
    while(left < right){
        while(left < right && a[left] <= a[key]){
            left++;
        }
        while(left < right && a[right] >= a[key]){
            right--;
        }
        change(a[left], a[right]);
    }
    change(a[left], a[key]);
    return left;
}

void quicksort(vector<int>& a, int left, int right)
{
    if(left >= right){
        return;
    }
    int keyi = partsort(a, left, right);
    quicksort(a, left, keyi - 1);
    quicksort(a, keyi + 1, right);
}

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i<nums.size(); i++){
            nums[i] = nums[i] * nums[i];
        }
        quicksort(nums,0,nums.size()-1);
        return nums;
    }
};

(二)复杂度分析

时间复杂度(平均)空间复杂度(平均)
插入排序O\left ( n^{2} \right )O\left ( 1 \right )
冒泡排序O\left ( n^{2} \right )O\left (1 \right )
数组指针排序O\left ( n \right )O\left ( n \right )
快速排序O\left ( n\log n \right )O\left ( \log n\right )

时间复杂度

        对数组元素进行平方操作的时间复杂度为O\left ( n \right )

        插入排序、冒泡排序都是用两层for循环,时间复杂度为O\left ( n^{2} \right )。数组指针法只需要遍历一次数组,一层while循环,时间复杂度为O\left ( n \right )。快速排序在平均情况下的时间复杂度为 O\left ( n\log n \right ),但在最坏情况下(例如数组已经有序)时间复杂度为O\left ( n^{2} \right )

空间复杂度

        插入排序、冒泡排序仅使用常数级的额外空间时,空间复杂度为O\left (1 \right )。数组指针排序使用额外数组空间时,空间复杂度为O\left ( n\right )。快速排序的递归调用会使用栈空间,平均情况下空间复杂度为O\left ( \log n\right ),但最坏情况下的空间复杂度为O\left ( n\right )

(三)总结

(1)冒泡排序与数组指针排序解题思想相似,都是将数组当作位置,每次将整个数组中第n大的数放在倒数第n的位置上。但数组指针的排序方法仅适用于此题有序数组平方后的排序,利用了题意(具体见写法三的排序思路)。

(2)插入排序、冒泡排序、快速排序适用于任何数组的排序,是通用经典的排序方法。快速排序较难理解,但也是重点方法,且综合效率较高。其他排序方法见下图,目前还未尝试,进一步学习可参考优质博客(原文链接:https://blog.youkuaiyun.com/Edward_Asia/article/details/121419975)。

(3)函数递归可以看作入栈和出栈操作,快速排序思想类似于树的排序,找到基准当作根节点,再对左右子树分别进行排序。

(4)就本题来说,从时间复杂度来看,数组指针法效率最高,而插入排序和冒泡排序效率较低。在空间复杂度方面,插入排序和冒泡排序最节省空间。

学习中,诚挚希望有心者指正和交流,经验或者方法都可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值