代码随想录-数组-移除元素

注意

本篇内容来自代码随想录leetcode,仅供自己记录与学习使用,如有侵权,请立即联系我。
感谢码农前辈们的付出。

移除元素

给你一个数组nums和一个值val,原地移除所有数值等于val的元素。
元素的顺序可能发生变化(什么变化了?)
返回nums中与val不同的元素的数量。

假设nums中不等于val的元素数量为k
更改nums数组,使nums的前k个元素包含不等于val的元素。nums的其他元素和nums的大小并不重要。
返回k。

提示
1. 0 < = n u m s . l e n g t h < = 100 0<=nums.length<=100 0<=nums.length<=100
2. 0 < = n u m s [ i ] < = 50 0<=nums[i] <=50 0<=nums[i]<=50
3. 0 < = v a l < = 100 0<=val<=100 0<=val<=100

我看

通过遍历计数得出多少不同的值 很容易
同时数组的大小也可以改变,要求原地移除,那就不构建新的数组了
找到对应val值的元素之后,如何将其移动到数组末尾?最保险的是移动,依次赋值使数组向前移动。不要求次序,那么就直接置换吧。
如果想从数组中删除val值对应的元素,应该要是用vector的相关方法吧,如果能使用的话应该超快吧。
先试试吧,
明天再搞吧,感觉身体还是很重要的,手被自己划破之后,我有点理解安乐死了,希望大家一定要身体健康,就是这手上的一道口子,都让我呲牙咧嘴,那么多病人多么痛苦,多么无助。在爱里来,经历了这么多,想好好的走,希望到时候社会可以成全我。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int j = 0;
        int k = nums.size();
        for(int i = 0 ; i < k; ++i){
            if(nums[i] == val){
                j++;
                nums[i] = nums[nums.size() - j];
                nums[nums.size() - j] = val;
                k--;
                i--;
            }
        }
        return nums.size() - j;
    }
};

竟然过了,也没有那么难,总体思路,发现等于val值的元素,则将其与数组最后的元素进行替换。对替换回来的元素再进行判断,如果其依旧等于val,则将其与倒数第二个元素进行替换。使用j来计数数组中等于val的元素个数,最后返回nums.size()-j。即为数组中不同于val值的元素数量。

利用迭代器,用一下这个erase方法试试嘞。
在这里插入图片描述

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        for(auto it = nums.begin(); it != nums.end(); ++it){
            if(*it == val){
                it = nums.erase(it);
                --it;
            }
        }
        return nums.size();
    }
};

这样些许奇怪,检索earse方法得到如下
在这里插入图片描述
故而我们有

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        for(auto it = nums.begin(); it != nums.end();){
            if(*it == val){
                it = nums.erase(it);
            }
            else{
                ++it;
            }
        }
        return nums.size();
    }
};

在这篇博客里还发现了,试一下嘞
在这里插入图片描述

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        nums.erase(remove(nums.begin(), nums.end(), val), nums.end());
        return nums.size();
    }
};

不再深入研究了,感觉这样使用vector的方法来解题有点没意思了。
我这雨下的好大啊。

看看代码随想录吧

  • 数组的元素在内存中地址是连续的,不能单独删除其中的某个元素,只能覆盖。

暴力解法

两层for循环,一个for循环遍历数组元素,第二个for循环移动更新数组。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for(int i = 0 ; i < size; ++i){
            if(nums[i] == val){ // 发现需要移除的数组元素,就将数组集体向前移动一位
                for(int j = i+1; j < size; ++j){
                    nums[j-1] = nums[j];
                }
                --i;  // 因为下标i之后的元素都向前移动了一位,所以i也向前移动一位
                --size;   // 此时数组大小-1 
            }
        }
        return size;
    }
};

快慢指针法

通过一个快指针和一个慢指针在一个for循环下完成两个for循环的工作。
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新 新数组下标的位置

我觉得就是在for循环中为两个指针设置不同的更新条件,来互相配合,完成记录或者是相遇,以此来破除内部的另一循环。

// 依旧优雅
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            // fastIndex指向新数组元素的位置,所以只要其走完一个数组的长度就可以
            if(nums[fastIndex] != val){
                nums[slowIndex++] = nums[fastIndex]
            }
        }
        // fastIndex 一直走,slowIndex遇到val就停住
        // 没有遇到val的话,就一起走
        return slowIndex;
    }
};

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1) 原地执行

相比于替换,代码随想录的实现方法没有改变元素的相对位置。

删除有序数组中的重复项

一个非严格递增排列的数组,原地删除重复的元素,使每个元素只出现一次,返回删除后数组的新长度。
元素的相对顺序应该保持一致。

我就想说有没有这道题,把一个数组变成集合set。但是我们现在这个数组是非严格递增的,就较为简单了,只用判断下一个元素与上一个元素是否相同,就可以确定是否是重复元素了。
那么如果不严格递增呢,记得在印象中python是有set的。用这个可以吗?

class Solution(object):
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums = list(set(nums))
        return len(nums)

感觉行,但是没通过,有没有大佬看到帮我看下是什么问题,呜呜呜
好像不行?出去玩去,回来再研究吧。

有点蒙,没关系,看个例子。

nums = {1,1,2,3,3}
返回新的长度3,nums = {1,2,3} ,
函数应该返回新的长度3,并且nums数组中的前三个元素应该被修改为1,2,3。不需要考虑数组中超出新长度后面的元素。

刚学过双指针,试一哈

先定义一下:
快指针:指向新数组中的元素,即不重复的下一个元素
慢指针:指向重复的元素,即准备被替换的元素
这样我感觉写不出来啊,哭哭, 感觉要是替换的话好写一点
这样算不算投机取巧

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            if(nums[slowIndex] != nums[fastIndex]){
                ++slowIndex;
                nums[slowIndex] = nums[fastIndex];          
            }
        }
        return slowIndex+1;
    }
};

看看前辈Max的题解

首先发现数组是有序的,重复的元素一定会相邻
要求删除重复元素,实际上就是将不重复的元素移动到数组的左侧

双指针解法思路与我一致
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

在此基础上,Max提出了优化策略,

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            if(nums[slowIndex] != nums[fastIndex]){
                ++slowIndex;
                if(fastIndex - slowIndex >= 1){
                    nums[slowIndex] = nums[fastIndex];
                }          
            }
        }
        return slowIndex+1;
        // 考虑数组[0,1,2,3,4,5]
        // 在本解题思路下,重新幅值了一遍,所以可以添加一个判断
        // 当fastIndex - slowIndex >= 1时,进行赋值
    }
};

看看评论区

哈哈哈哈 神奇淘宝 哥们属实是把日子过明白了
在这里插入图片描述

移动零

给定一个数组,把所有的零移动到数组的末尾,同时保持非零元素的相对顺序。
需要原地完成操作。

移动0,这不是就是把移除元素那道题中的值定为0就行吗?那么快慢指针肯定可以,再写一遍再想想有没有什么幺蛾子吧

快慢指针

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            if(nums[fastIndex]){
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        cout << nums.size() - slowIndex; // slowIndex对应其中非零元素个数
        // nums.size() - slowIndex 为数组中零元素个数

        // 将数组中末尾元素置为0
        for(int i = 0; i < nums.size() - slowIndex; ++i)
            nums[nums.size()-i-1] = 0;
    }
};

做出来了,有点曲折

想想其他办法

输入: nums = [0, 1, 0, 3, 12]
输出: [1, 3, 12, 0, 0] 保持非零元素的相对位置,在不复制数组的情况下进行操作。
当然像上面一样的暴力搜索可行,但不是更好的解决办法

看看王尼玛的题解吧

在这里插入图片描述

两次遍历

这个思路与我写的快慢指针一致。看看他的分析吧
创建两个指针i(快指针)和j(慢指针),第一次遍历的时候使用指针j来记录当前有多少个非零元素。
遍历时i每遇到一个非零元素就将其往数组左边移动,第一次遍历完,j就指向了第一个零元素的下标。
第二次遍历的时候,起始位置就从j开始到结束,将这段区域中的元素全部置为0.

不错,在我写的时候就觉得对于置零区间的描述太绕了,不想再绕回来了。结合王尼玛的描述,更清晰了。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            if(nums[fastIndex]){
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        for(int i = slowIndex; i < nums.size(); ++i)
            nums[i] = 0;
    }
};

一次遍历

参考了快排的思想,快速排序首先要确定一个待分割的元素作为中间点x,然后把所有小于等于x的元素放到x的左边,大于x的元素放到x的右边。
这里我们可以把零当做这个中间点,把不等于0的元素放到中间点的左边,等于0的放到中间点的右边。
这里的中间点就是0本身,所以实现起来要比快速排序简单很多,我们使用两个指针(相比于我在移除元素是使用的首尾交换,使用双指针进行替换不会改变元素的相对顺序),只要快指针nums[i]!=0,我们就交换nums[i] 与nums[j]的值。

吃完饭来写


class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slowIndex = 0;
        for(int fastIndex = 0; fastIndex < nums.size(); ++fastIndex){
            if(nums[fastIndex] != 0)   
            {
                int temp = nums[fastIndex];
                
                nums[fastIndex] = nums[slowIndex]; 
                // 此处不能将fastIndex对应下标赋(置换)为0,考虑情况nums=[1]
                // 结果会为[0]
                nums[slowIndex++] = temp;
            }
        }
    }
};

刚开始,我也是这样想的,但是 nums[fastIndex] = nums[slowIndex]; 在这一步时,我将fastIndex下标对应的元素按照置换的想法置为0了,导致对只具有单一元素的数组产生错误。
参看评论区,mit00搬运了解法二的优化
在这里插入图片描述
对的,如果加上fastIndex>slowIndex这一条件判断,确实可以帮忙避免开头元素为非零元素,将其置为0的情况,very nice!

比较含退格的字符串

给定s和t两个字符串,当它们分别被输入到空白的文本编辑器,如果两者相等,则返回true。#代表退格字符。

如果对空文本输入退格字符,文本继续为空。

非常喜欢这个场景哈哈哈,虽然一头魔战,先看几个例子吧
例:
s = “ab#c” t = “ad#c” 返回true
s= “a#c” t = “b” 返回false

提示: 1 <= s.length, t,length <= 200
s与t只含有小写字母和#

是否可以用 O ( n ) O(n) O(n)的时间复杂度和 O ( 1 ) O(1) O(1)的空间复杂度解决该问题?

我说

既然在是移除元素的相关题,那咱们肯定得删点吼
这样发现#就把它前面的字符和#本身移动到字符串的最后去

不好搞啊,没啥想法?

将#看成是删除的标志,像前面的值相等nums[fastIndex] = val一样
累了,看答案吧,想不出来

看看御三五佬的题解

相信大家看到该题的第一反应是使用栈,或者直接删除字符串来解决(栈确实使一个非常好的思路方法,如何直接删除字符串呢,啊,我没有第一反应,哭哭),但是这样做的话,空间复杂度为n+m(使用了额外的空间吗)。

双指针是一种常量级空间复杂度的解法

由于#只会消除左边的字符,所以对右边的字符无影响,所以我们选择从后往前遍历S,T字符串。(我一闪而过的念头里面有)

我之前的思路一直都是分别对两个字符串进行处理,处理后对有效位数进行比较。
佬的思路是在遍历的过程中对二者就进行比较了。

思路解析非常清楚
在这里插入图片描述
但是如何具体安排逻辑? return true放在最后吧。
当当前字符不能被抵消时,就跳出一个字符串的循环判断,进入另一个字符串的循环判断。
退出了两个字符串的个体循环判断后,进入大循环判断,对比两个字符串当前字符是否一致,

有些许复杂啊,但是确实能处理两个##相连的情况。

Q1:有一个问题,这样,当s = “rrr” ,t = "r"的时候,如何判断?代码末尾已解决
Q2:

if(s[i] == "#")

当比较s[i]与"#"时报错
Line 9: Char 25: warning: result of comparison against a string literal is unspecified (use an explicit string comparison function instead) [-Wstring-compare]
9 | if(s[i] == “#”){
| ^ ~~~
Line 9: Char 25: error: comparison between pointer and integer (‘value_type’ (aka ‘char’) and ‘const char *’)
9 | if(s[i] == “#”){
| ~~~~ ^ ~~~
Line 20: Char 25: warning: result of comparison against a string literal is unspecified (use an explicit string comparison function instead) [-Wstring-compare]
20 | if(t[j] == “#”){
| ^ ~~~

s[i]的类型是char吗?实验一下
在这里插入图片描述
所以无法比较string和char类型吗?看不懂!
在这里插入图片描述
在这里插入图片描述
所以说string这个类型也叫做std::basic_ostream<char, std::char_traits> 吗,有点意思吼?

// OK不错好得很,只是我感觉最后的逻辑判断有点慌乱
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int i = s.size() - 1, j = t.size() - 1;
        // 从后向前比遍历字符
        int skipS = 0, skipT = 0;
        while(i >= 0 || j >= 0){
            while(i >= 0){
                if(s[i] == '#'){
                    ++skipS, --i;
                }
                else if(skipS != 0){
                    --skipS, --i;
                }
                else{
                    break;
                }
            }
            while(j >= 0){
                if(t[j] == '#'){
                    ++skipT, --j;
                }
                else if(skipT != 0){
                    --skipT, --j;
                }
                else{
                    break;
                }
            }
            // 此处i,j即有成为无效索引的风险了,若要毕竟比较二者是否一致
            // 首先需要对下标的合理性进行一个判断
            if(i >= 0 && j >= 0){
                if(s[i] != t[j]){
                    return false;
                }
            }
            else{   // 就是i和j有一个无效了
                if(i >= 0 || j >= 0){     // 也即排除同时无效的情况
                // 若一个下标无效,同时另一个下标有效
                // 则意味着两个字符串有效长度不同
                    return false;
                }
            }
            --i,--j;
        }
        return true;
    }
};

时间复杂度: O ( N + M ) O(N+M) O(N+M),其中N和M分别为字符串s和字符串t的长度,我们需要遍历他们各一遍。
空间复杂度: O ( 1 ) O(1) O(1),没有使用额外的存储空间,对于每个字符串,我们只需要多定义一个索引指针和#计数器即可。

官方题解 重构字符串

官方题解中的双指针方法与御三五佬一致,来学习一下官方题解中使用栈的方法

最容易想到的方式是将给定的字符串中的退格符和应当被删除的字符都去除,还原字符串的一般形式,再进行比较

具体的,使用栈来处理遍历过程,每遍历到一个字符
如果它是退格符,我们则将栈顶弹出
如果它为普通字符,我们则将其压入栈中

// 太优雅了
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        return build(s) == build(t);
    }
private:
    string build(string str){
        string rest;
        for(char ch:str){
            if(ch != '#'){
                rest.push_back(ch);
            }
            else if(!rest.empty()){   
                rest.pop_back();
            }
            // 其余情况即,rest为空,当前字符为#,则不做任何处理
        }
        return rest;
    }
};

太优雅了,非空的判断逻辑原来设计的这么好记 !rest.empty()

时间复杂度: O ( m + n ) O(m+n) O(m+n) 但是这个肯定比双指针慢,这个遍历是一定要遍历的,但是却少了许多判断,好奇判断语句消耗的时间
空间复杂度: O ( m + n ) O(m+n) O(m+n) 对于每个string都使用了额外的存储栈

拿一个string当栈,我脑子里想到的栈是stack 是不是这样建立的呀,我去搜搜
就是这样
那我也可以用vector当栈吧,只要有push和pop就行,不错厉害好得很。
在这里插入图片描述
突然发现这不加空格也可以 太可怕了
所以a.size和sizeof(a)有什么区别呢` 先不研究了,饿了,去杭州转转去啦啦啦,杭州真不错啊!感觉很发达。
在这里插入图片描述

有序数组的平方

一个非递减顺序排序的整数数组nums,返回每个数组平方组成的新数组,也按照非递减顺序排列
怎么感觉旁边有人我就紧张的很哪,哪来的那么多虚荣心,哪有那么多条条框框,无所谓啦!

提示: 1 < = n u m s . l e n g t h < = 1 0 4 1 <=nums.length <= 10^4 1<=nums.length<=104 长度10000,就是拿int也可以放下吗?
1 0 − 4 < = n u m s [ i ] < = 1 0 4 10^{-4}<=nums[i] <= 10^4 104<=nums[i]<=104

我说

感觉没什么思路,能直接用sort吗哈哈,但是我其实也不经常用sort啊,sort怎么用,不管了让我先用个sort吧

关于vector的初始化我怎么就记不住啊,哈哈
感觉都是跟数组搞混了,呜呜呜
在这里插入图片描述

// 嘿嘿嘿 不错哦
class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int& a:nums){
            a = a*a;
        }
        sort(nums.begin(), nums.end());
        return nums;    
    }
};

时间复杂度: O ( N l o g n ) O(Nlogn) O(Nlogn) 估计跟sort的时间复杂度走了
空间复杂度: O ( 1 ) O(1) O(1) 没使用额外的空间吧

再想想,感觉还是全部平方之后再进行排序来的逻辑比较顺,如果所有元素都是负数,那么去找正负分界线就无法找到了。

感觉我觉得问题的突破口在于,利用已经排序的正数序列(如果有),将负数插入进去。
时间复杂度为 O ( n ) O(n) O(n),就是移动估计也不行,使用双指针进行替换可能是解决办法

要求设计时间复杂度为 O ( n ) O(n) O(n)的算法来解决,但是没有对空间复杂度进行要求,感觉使用大小与nums一致的额外空间是能解决问题的,试试吧。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {

        if(nums.size() < 2)
            return {nums[0]*nums[0]};
        int fastIndex = 0;
        for(; fastIndex < nums.size() ; ++fastIndex){
            if(nums[fastIndex] >= 0){
                break;
            }
        }
        for(int& num:nums){
            num = num * num;
        }
        if(!fastIndex) return nums;
        vector<int> result;
        int slowIndex = fastIndex - 1;
        while(slowIndex >= 0 || fastIndex <= nums.size() - 1){
            if(slowIndex >= 0 && fastIndex <= nums.size() - 1){
                if(nums[fastIndex] <= nums[slowIndex]){
                    result.push_back(nums[fastIndex]);
                    ++fastIndex;
                }
                else{
                    result.push_back(nums[slowIndex]);
                    --slowIndex;
                }
            }
            else if(slowIndex >= 0){
                result.push_back(nums[slowIndex]);
                --slowIndex;
            }
            else{
                result.push_back(nums[fastIndex]);
                ++fastIndex;
            } 
        }
        return result;
    }
};

这样搞出来了,不错,大概思想就是,先找到正负分界线(如果有),利用两个指针分别向左向右前进,并且进行比较,按照大小以此pushback进额外的vector数组中。

时间复杂度: O ( n ) O(n) O(n) 只遍历了两遍数组,也是 O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n) 使用了nums数组大小的额外空间

下回再看大佬的解答吧,最近在培训企业文化,真折磨人。

再想一下有没有解决办法吧,感觉心有点乱。别想那么多!我拖了好长时间的博客了,呜呜呜
看一下官方题解吧!

看一下官方题解吧

直接排序

这个思路和我投机取巧的思路一样诶,但是这里的空间复杂度为什么是 O ( l o g n ) O(logn) O(logn)
在这里插入图片描述
这个时间复杂度不是明显是 O ( n ) O(n) O(n)吗? 这里有一个问题:sort的快排实现使用了 O ( l o g n ) O(logn) O(logn)的栈空间吗? 这个等之后联系快排的时候再解答吧!

双指针

方法一种没有用到数组nums已经升序排列这个条件。
如果能够找到数组nums中正数和负数的分界线,那么就可以使用类似归并排序的方法。

思路和我是一样的,只是归并排序是咋搞的?呜呜呜,不知道

找到负数和非负数的分界线,经过平方后,我们就得到了两个已经有序的子数组,进而就可以使用归并的方法进行排序。具体的使用两个指针分别指向分界线左右的两个元素,每次比较两个指针对应的元素,选择较小的放入数组中。当某一指针移动到边界后,将另一指针还未遍历到的树依次放入数组中。

官方还有另外一种双指针的解法
选择两个指针分别指向位置0和n-1,比较两个指针对应元素的大小,将较大的元素按照逆序放入数组中。
这种算法无须处理某一指针移动到边界这种情况。

这样的话是否依旧没有用到已经非递减排列这个条件,用到了。
例如现在有一个无序数组 nums = {-3, -4, -1, 1, 2, 5 } 逆序进入顺序为 25 9 16 4 1 1
可以看到已经不是一个非递减的序列了,非递减排列的条件保证了,中间部分即,双指针的汇集方向值最小,不错,very nice!

再考虑例 nums = {-4, -3, -2, 1, 2} 进入顺序为 16 9 4 4 1,ok
与上面的双指针的思路相反,上面是正序,先放小的,所以两个指针需要从绝对值最小的中间部分开始,向外部扩展。而此处是向内汇合,那么两个指针的汇合也应作为判断是否结束循环的条件。
那么其实我感觉pos>=0应该也可以作为终止条件,试一下吧,是的不错,感觉这个终止条件以后也可以用一下。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);        // 建立大小为n的数组来盛放结果,
                                   // 因为需要逆序放入数组,所以需要首先固定好数组的大小
        int slowIndex = 0, fastIndex = n - 1;
        for(int pos = n - 1; pos >= 0 ; --pos){      
        // for(int pos = n - 1; slowIndex <= fastIndex ; --pos){      
            // 当slowIndex = fastIndex是,指向的是最后一个需要存储的元素,也是绝对值最小的元素
            if(nums[slowIndex]*nums[slowIndex] >= nums[fastIndex]*nums[fastIndex]){
                ans[pos] = nums[slowIndex] * nums[slowIndex];
                ++slowIndex;
            }
            else{
                ans[pos] = nums[fastIndex] * nums[fastIndex];
                --fastIndex;
            }
            // -pos;    // 不管是哪种情况,都向pos中存入了值
        }
        cout << ans[0] << endl;
        return ans;
    }
};

在时间复杂度上,感觉会比第一种双指针快,因为不需要再去找分界线了,少了这样一次遍历。
其次在代码上,无须对指针到边界的情况进行判断和接续处理了,在代码上也更为简洁了。
在这里插入图片描述
东波佬的这个理解方式可以,nums数组对应x的值,我们现在关心 y = x 2 y = x^2 y=x2曲线上的点,双指针从两侧向中间不断遍历两个y值点,并进行比较,最终到达曲线的谷点,也是折线的最低点。

代码随想录的下一道题也是这个,一并看下吧

暴力搜索的时间复杂度是 O ( n + n l o g n ) O(n+nlogn) O(n+nlogn),可以说是 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度, O ( n l o g n ) O(nlogn) O(nlogn)肯定就是快排的时间复杂度了
Q: O ( n l o g n ) O(nlogn) O(nlogn)这一复杂度在 l o g n logn logn在<1的时候复杂度低于 O ( n ) O(n) O(n)吗?还是不应该较真, O ( n l o g n ) O(nlogn) O(nlogn)就是比 O ( n ) O(n) O(n)更高的时间复杂度?(好冷,下回再研究吧)

数组其实是有序的,只不过平方之后负数可能成为最大值了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。

此时的时间复杂度为O(n),相对于暴力排序的解法O(n + nlog n)还是提升不少的。

这里还是说一下,大家不必太在意leetcode上执行用时,打败多少多少用户,这个就是一个玩具,非常不准确。
做题的时候自己能分析出来时间复杂度就可以了,至于leetcode上执行用时,大概看一下就行,只要达到最优的时间复杂度就可以了
一样的代码多提交几次可能就击败百分之百了…

参考文献

vector相关:https://blog.csdn.net/Flag_ing/article/details/123380655
https://blog.csdn.net/Vcrossover/article/details/106243627
栈相关:https://blog.csdn.net/qq_20366761/article/details/70053813
代码随想录:https://www.programmercarl.com/
Leetcode:https://leetcode.cn/

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值