数组篇一

编程实战一:转置矩阵

思路:因为矩阵转置后不能确保行数与列数与原先相同,故而定义一个二维矩阵B,把A矩阵元素A[i][j]赋值给B[j][i]即可,若相同可以实现原地转置。

 vector<vector<int>> transpose(vector<vector<int>>& A) {
        int row=A.size(),col=A[0].size();
        vector<vector<int>>B(col,vector<int>(row,0));
        for(int i=0;i<row;i++)
            for(int j=0;j<col;j++)
            {
                B[j][i]=A[i][j];
            }
        return B;
    }

编程实战二:寻找众数

题目描述:数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。
思路:摩尔投票,设置一个计算次数的变量cnt=0,遍历数组元素nums[i],如果cnt为0,那么major=nums[i],cnt=1否则的话,cnt++,不相等,cnt–。最后如果cnt>0,遍历数组验证major是否为众数,否则没有众数返回-1

int majorityElement(vector<int>& nums) {
        int cnt=0,major;
        for(auto&i:nums)
        {
            if(cnt==0)
            {
                major=i;
                cnt=1;
            }
            else
            {
                if(major!=i)
                {
                    cnt--;
                }
                else
                {
                    cnt++;
                }
            }
        }
        if(cnt>0)
        {
            cnt=0;
            for(auto&i:nums)
            {
                if(major==i)
                {
                    cnt++;
                }
            }
            if(cnt>nums.size()/2)
                return major;
        }
        return -1;
    }

编程实战三:有序数组平方

题目:给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。
输入:[-4,-1,0,3,10]
输出:[0,1,9,16,100]
思路:观察数组特点发现非递减顺序,因此平方后会出现左半边递减,右半边递增,可以用双指针,分别从头尾0和n-1开始遍历,判断哪个数大,就把大的数放到res[k]中,当k从n-1至0中。

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

编程实战四:所有奇数长度子数组之和

题目:给你一个正整数数组 arr ,请你计算所有可能的奇数长度子数组的和。

示例:
输入:arr = [1,4,2,5,3]
输出:58
解释:所有奇数长度子数组和它们的和为:
[1] = 1
[4] = 4
[2] = 2
[5] = 5
[3] = 3
[1,4,2] = 7
[4,2,5] = 11
[2,5,3] = 10
[1,4,2,5,3] = 15
我们将所有值求和得到 1 + 4 + 2 + 5 + 3 + 7 + 11 + 10 + 15 = 58

题解一:暴力求解,从第一个数开始遍历,依次把子数组长度从1开始延伸,每次跨度加2,长度i+size依次就是1,3,5,7…直到大于n为止。

int sumOddLengthSubarrays(vector<int>& arr) {

        int res = 0;
        for(int i = 0; i < arr.size(); i ++)
            for(int sz = 1; i + sz - 1 < arr.size(); sz += 2)
                res += accumulate(arr.begin() + i, arr.begin() + i + sz, 0);
        return res;

题解二:其实可以遍历数组一次就能获得结果,那就是考虑数组中每一个元素在子数组中出现的次数times[i],然后把每一个arr[i]*times[i]的结果累加起来就是最终答案。问题现在就变成了考虑每一个元素出现的次数,对于arr[i]来说,左边可选择0,1,2,3…个数,右边可选择0,1,2,3…个数,所以left=i+1种可能,right=n-i种可能,比如i=0时,左边可以选择0个数,所以只有一种可能,left=1,右边同理。接下来计算左边有多少种奇数情况,多少种偶数情况,右边多少种奇数,右边多少种偶数。原因在于当一个数已经确定后,要以这个数构成奇数长度子数组,无非两种情况,1:左边取偶数个,右边取偶数个,总的个数一定为奇数。2.左边取奇数个,右边取奇数个,总的个数一定为奇数。

 int sumOddLengthSubarrays(vector<int>& arr) {

        int res = 0;
        for(int i = 0; i < arr.size(); i ++){
            int left = i + 1, right = arr.size() - i,
                left_even = (left + 1) / 2, right_even = (right + 1) / 2;//偶数情况
                left_odd = left / 2, right_odd = right / 2;//奇数情况
            res += (left_even * right_even + left_odd * right_odd) * arr[i];
        }
        return res;
    }

编程实战五:重复的数II

题目:给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。
思路:用一个哈希表,遍历数组过程中顺便记录其下标,一旦出现重复,就比较下标,满足条件返回true,否则更新下标

bool containsNearbyDuplicate(vector<int>& nums, int k) {
         unordered_map<int,int>mp;
        for(int i=0;i<nums.size();i++){
            if(!mp[nums[i]]){
                mp[nums[i]]=i+1;
            }
            else{            
                if(i-mp[nums[i]]+1<=k)return true;
                else mp[nums[i]]=i+1;
            } 
        }
        return false;
    }

编程实战六:汇总区间

题目:给定一个无重复元素的有序整数数组 nums 。返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。列表中的每个区间范围 [a,b] 应该按如下格式输出:
“a->b” ,如果 a != b
“a” ,如果 a == b

示例:
输入:nums = [0,1,2,4,5,7]
输出:["0->2","4->5","7"]
解释:区间范围是:
[0,2] --> "0->2"
[4,5] --> "4->5"
[7,7] --> "7"

思路:准备一个字符串变量str,用一个start和end用来标记起始数字和终止数字,如果是逐一递增就更新end值,否则说明start和end可以储存在str中,放入vector中了,此时注意会出现两种情况,第一种,start与end值相等,第二种start与end不相等。处理完后就可以更新start,end以及str。当所有元素遍历完毕后要注意此时还有最后一段数据也得存放到vector里面,同样分两种情况,就不再赘述。

vector<string> summaryRanges(vector<int>& nums) {
        vector<string> res;
        if(nums.size()==0)
        {
            return res;
        }
        string str="";
        int start=-666,end=start;
        for(int i=0;i<nums.size();i++)
        {
            if(start==-666)
            {
                start=nums[i];
                end=start;
            }
            else
            {
                if(nums[i]-1==end)
                {
                    end=nums[i];
                }
                else
                {
                    if(start!=end)
                    {
                    str=to_string(start)+"->"+to_string(end);
                    }
                    else{
                        str=to_string(start);
                    }
                     res.push_back(str);
                    str="";
                    start=-666;
                    i--;
                }
            }
        }
        if(start!=-666&&start!=end)
        {
           str=to_string(start)+"->"+to_string(end);
        }
        else if(start!=-666&&start==end)
        {
            str=to_string(start);  
        }
        res.push_back(str);
        return res;
    }

编程实战七:种花问题

题目:假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True

思路:遍历数组,只要flowerbed[i]为0,并且左边右边都为0,那么这个位置可以种花,count++,最后判断count是否大于n

bool canPlaceFlowers(vector<int>& flowerbed, int n) {
        int count =  0;
        for(int i = 1; i < flowerbed.size()-1; i++)
        {
            if (flowerbed[i] == 0 && (i == 0 || flowerbed[i - 1] == 0)&&(i==flowerbed.length - 1 || flowerbed[i + 1] == 0))
            {
                flowerbed[i] = 1;
                count++;
            }
        }
        return n <= count;

    }

编程实战八:反转图像

题目:给定一个二进制矩阵 A,我们想先水平翻转图像,然后反转图像并返回结果。水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。反转图片的意思是图片中的 0 全部被 1 替换, 1 全部被 0 替换。例如,反转 [0, 1, 1] 的结果是 [1, 0, 0]。
思路:直接暴力模拟就行了

 vector<vector<int>> flipAndInvertImage(vector<vector<int>>& A) {
        for(auto &i:A)
            for(int j=0,temp=0,n=i.size();j<(n+1)/2;++j)
            {
                temp=i[j]^1;
                i[j]=i[n-j-1]^1;
                i[n-j-1]=temp;
            }

编程实战九:找出井字棋获胜者

题目:A 和 B 在一个 3 x 3 的网格上玩井字棋。井字棋游戏的规则如下:
玩家轮流将棋子放在空方格 (" ") 上。
第一个玩家 A 总是用 “X” 作为棋子,而第二个玩家 B 总是用 “O” 作为棋子。
“X” 和 “O” 只能放在空方格中,而不能放在已经被占用的方格上。
只要有 3 个相同的(非空)棋子排成一条直线(行、列、对角线)时,游戏结束。
如果所有方块都放满棋子(不为空),游戏也会结束。
游戏结束后,棋子无法再进行任何移动。
给你一个数组 moves,其中每个元素是大小为 2 的另一个数组(元素分别对应网格的行和列),它按照 A 和 B 的行动顺序(先 A 后 B)记录了两人各自的棋子位置。
如果游戏存在获胜者(A 或 B),就返回该游戏的获胜者;如果游戏以平局结束,则返回 “Draw”;如果仍会有行动(游戏未结束),则返回 “Pending”。
你可以假设 moves 都 有效(遵循井字棋规则),网格最初是空的,A 将先行动。

示例:
输入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
输出:"A"
解释:"A" 获胜,他总是先走。
"X  "    "X  "    "X  "    "X  "    "X  "
"   " -> "   " -> " X " -> " X " -> " X "
"   "    "O  "    "O  "    "OO "    "OOX"

思路:考虑到井字棋只有八种赢的可能性,所以可以用一个二维数组保存每一种获胜可能,把九个空格编号0,1,2,3,4,5,6,7,8,然后遍历moves数组把A和B走的路径分别保存下来,最后与所有的获胜可能比较,只要相等,就能知道谁获胜了,如果两个人都没赢,就是平局或者未结束了。

优化:按照原有思路,其实可以只考虑最后一个落子的是谁就可以知道了,比如A最后落子,那么B绝对不可能获胜,因此可以不必考虑B的路径,只需要考虑A的路径,同理B最后落子情况类似

class Solution {
public:
    bool checkwin(unordered_set<int>& S, vector<vector<int>>& wins) {
        for (auto win: wins) {
            bool flag = true;
            for (auto pos: win) {
                if (!S.count(pos)) {
                    flag = false;
                    break;
                }
            }
            if (flag) {
                return true;
            }
        }
        return false;
    }
    string tictactoe(vector<vector<int>>& moves) {
        vector<vector<int>> wins = {
            {0, 1, 2},
            {3, 4, 5},
            {6, 7, 8},
            {0, 3, 6},
            {1, 4, 7},
            {2, 5, 8},
            {0, 4, 8},
            {2, 4, 6}
        };
        unordered_set<int> A, B;
        for (int i = 0; i < moves.size(); ++i) {
            int pos = moves[i][0] * 3 + moves[i][1];
            if (i % 2 == 0) {
                A.insert(pos);
            }
            else {
                B.insert(pos);
            }
        }
        if (checkwin(A, wins)) {
                    return "A";
                }
        if (checkwin(B, wins)) {
                    return "B";
                }
        return (moves.size() == 9 ? "Draw" : "Pending");
    }
};

编程实战十:两数之和

题目:给出一个整数数组,请在数组中找出两个加起来等于目标值的数,
你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的
假设给出的数组中只存在唯一解
例如:
给出的数组为 {20, 70, 110, 150},目标值为90
输出 index1=1, index2=2
思路:用哈希表保存遍历过的元素,key为元素值,value为index(注意题目中的下标含义),由于是两数之和,例如a和b,假如数组中存在a+b==target,那么,那么肯定a和b有先后,先找到的a或者b肯定已经在哈希表当中了。所以根据这个特点就可以写出代码了。

vector<int> twoSum(vector<int>& numbers, int target) {
        // write code here
        unordered_map<int,int> ss;
        vector<int> res(2);
        for(int i=0;i<numbers.size();i++)
        {
            
            auto iter=ss.find(target-numbers[i]);
            if(iter!=ss.end()&&iter->second!=i+1)
            {
                res[0]=iter->second;
                res[1]=i+1;
                break;
            }
            else
                ss[numbers[i]]=i+1;
        }
        return res;
    }

编程实战十一:买卖股票最好时机

题目描述
假设你有一个数组,其中第\ i i 个元素是股票在第\ i i 天的价格。
你有一次买入和卖出的机会。(只有买入了股票以后才能卖出)。请你设计一个算法来计算可以获得的最大收益。
思路:由于只有一次买入和卖入,所以可以直接暴力求解,双重for循环,时间复杂度为O(n2),这里主要想给出一个优化方案,时间复杂度优化至O(n),一次遍历,min为第一个元素,profit表示收益,如果遇到元素比min大,更新收益profit,否则,更新min值

int maxProfit(vector<int>& prices) {
        // write code here
        if(prices.size()<2)
        {
            return 0;
        }
        int min=prices[0],profit=0;
        for(int i=1;i<prices.size();i++)
        {
            if(prices[i]>min)
            {
            profit=prices[i]-min>profit?prices[i]-min:profit;
            }
            else
            min=prices[i];
            
        }
        return profit;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值