初级算法-数组

本文介绍了如何在排序数组中删除重复元素的原地算法,探讨了买卖股票获取最大利润的贪心策略,以及如何实现数组的整体旋转。同时,还讨论了判断数组是否存在重复元素、找到两个数组的交集和解决数学问题如加一、移动零、两数之和的有效方法。
摘要由CSDN通过智能技术生成

数组:

删除排序数组中的重复项
题意:

一个升序数组nums,要求使用原地算法,要求返回数组内有几数字

若有K种,需要保证原数组处理完[0,K-1]以原序存储每种数字各一个

解:

由于要求使用原地算法,所以直接对原数组操作,使用一个指针L表示所指向数字的第一个,默认指向0

遍历数组,如果遇到一个和指针L指向的数字不同的数字ElseNum,就将ElseNum放在L+1的位置,然后L指向ElseNum表示ElseNum这种数字已经找到了第一个,可以找下一种了

代码:
#include<bits/stdc++.h>
using namespace std;
int removeDuplicates(vector<int>& nums)
{
    int l=0,k=1,lg=nums.size();
    for(int i=1;i<lg;i++)
    {
        if(nums[i]!=nums[l])
        {
            swap(nums[i],nums[++l]);
            k++;
        }
    }
    return k;
}
int main()
{
    vector<int>nums;int temp;
    while(cin>>temp)
    {
        nums.push_back(temp);
    }
    int ans=removeDuplicates(nums);
    cout<<ans<<endl;
    return 0;
}
买卖股票的最佳时机 II
题意:

给定一个整数数组,代表每天的股票价格,最多只能持有一股,求最大利润

解:

贪心,有的赚先赚。任意两个数字[A,B],A表示今天的价格,B表示你预知明天的价格,如果A<B,则明天涨价,那就在今天买明天卖;如果B<A,则明天跌落,今天先卖明天再买可以用更低的成本持有一股。

由于核心思路是通过看下一天的价格来考虑今天是否持有一股(初始没有持有),所以对任意一段求最大利润不需要依赖其他部分。

代码:
#include<bits/stdc++.h>
using namespace std;
int maxProfit(vector<int>& prices)
{
    int lg=prices.size(),ans=0;
    for(int i=1;i<lg;i++) ans=ans+max(0,prices[i]-prices[i-1]);
    return ans;
}
int main()
{
    vector<int>prices;int temp;
    while(cin>>temp)
    {
        prices.push_back(temp);
    }
    int ans=maxProfit(prices);
    cout<<ans<<endl;
    return 0;
}。
旋转数组
题意:

给一个整数数组,要求整体右移k个位置,超出数组的元素按原序填补到前面空出来的位置

解:

1、新数组,先装k到lg-1,然后再装0到k-1

2、bool数组标记处理过的数字,从i开始处理i+k、i+2k直到回到i

3、每移动lg次相当于没移动,所以k=k%lg,最后结果相当于把最后k个数字原序移动到开头,可以先翻转整个数组,然后分别翻转[0,k-1][k,lg-1]原地算法

代码:
#include<bits/stdc++.h>
using namespace std;
inline void reserve(vector<int>& nums,int l,int r)
{
    while(l<r) swap(nums[l++],nums[r--]);
}
void rotate(vector<int>& nums, int k)
{
    int lg=nums.size();k%=lg;
    reserve(nums,0,lg-1);
    reserve(nums,0,k-1);
    reserve(nums,k,lg-1);
}
int main()
{
    vector<int>nums;int temp,k;
    cin>>k;
    while(cin>>temp)
    {
        nums.push_back(temp);
    }
    rotate(nums,k);
    for(auto num:nums) cout<<num<<ends;
    cout<<endl;
    return 0;
}
存在重复元素
题意:

给一个整数数组,如果存在重复的数字返回true

解:

数字范围很大,不能bool数组,可以map或者set这种log级别的查找

先排序再遍历也可以,C++11 std::sort能稳定在O(NlogN)左右

代码:
#include<bits/stdc++.h>
using namespace std;
bool containsDuplicate(vector<int>& nums)
{
    int lg=nums.size();
    sort(nums.begin(),nums.end());
    for(int i=1;i<lg;i++)
    {
        if(nums[i]==nums[i-1]) return true;
    }
    return false;
}
int main()
{
    vector<int> nums;int temp;
    while(cin>>temp)
    {
        nums.push_back(temp);
    }
    bool ans=containsDuplicate(nums);
    cout<<ans<<endl;
    return 0;
}
两个数组的交集 II
题意:

字面意思,求两个数组交集

解:

先排序,然后直接intersection函数求交集,或者双指针处理

T1-免排序

T2-按T3,map方式(数量少,免排序)

T3-内存有限,map或者bool数组(num[i]很小)存个数,然后遍历nums2

代码:

intersection:

vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
{
    vector<int>ans;
    sort(nums1.begin(),nums1.end());
    sort(nums2.begin(),nums2.end());
    set_intersection(nums1.begin(),nums1.end(),nums2.begin(),nums2.end(),inserter(ans,ans.begin()));
    return ans;
}

T3-内存限制:

#include<bits/stdc++.h>
using namespace std;
vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
{
    vector<int>book(1001);
    vector<int>ans;
    for(auto num:nums1) book[num]++;
    for(auto num:nums2)
    {
        if(book[num])
        {
            ans.push_back(num); 
            book[num]--;
        }
    }
    return ans;
}
int main()
{
    vector<int> nums1,nums2;int temp;
    while(cin>>temp)
    {
        nums1.push_back(temp);
    }
    cin.clear();
    while(cin>>temp)
    {
        nums2.push_back(temp);
    }
    
    vector<int> ans=intersect(nums1,nums2);
    for(auto a:ans) cout<<a<<ends;
    cout<<endl;
    return 0;
}
加一
题意:

用vector容器存储了一个非负整数,求计算这个整数+1后的值用vector存储并返回

解:

类似字符串大整数加法,从尾部遍历,一个bool存储进位即可

代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> plusOne(vector<int>& digits)
{
    int lg=digits.size(),add=0;
    
    digits[lg-1]++;
    for(int i=lg-1;i>=0;i--)
    {
        digits[i]+=add;
        add=digits[i]/10;
        digits[i]%=10;
    }
    vector<int>ans;
    if(add) ans.push_back(add); 
    for(auto &digit:digits) ans.push_back(digit);
    return ans;
} 
int main()
{
    vector<int> digits;int temp;
    while(cin>>temp)
    {
        digits.push_back(temp);
    }
    vector<int>ans=plusOne(digits);
    for(auto a:ans) cout<<a<<ends;
    cout<<endl;
    return 0;
}
移动零
题意:

给定一个数组,将零全部移动到后面去,其他元素要保持原序

解:

O(2n)双指针,一个指向最靠前面的0,一个指向最靠前面的非0数字,如果0在非零前面,交换

O(n)由于第N位非0数字应该在第N位,所以index存储目前有几个非0数字,遇到非零数字和对应位置交换(下标偏差)

代码:
#include<bits/stdc++.h>
using namespace std;
void moveZeroes(vector<int>& nums)//N 
{
    int lg=nums.size(),index=-1; 
    for(int i=0;i<lg;i++) if(nums[i]!=0) swap(nums[++index],nums[i]);
    /* //Check
    for(auto num:nums) cout<<num<<ends;
    cout<<endl;*/
}
/*
void moveZeroes(vector<int>& nums)//2N
{
    int l=-1,r=-1,lg=nums.size();
    while(true)
    {
        while(++l<lg && nums[l]!=0);//指向最近的0
        while(++r<lg && nums[r]==0);//指向最近的非0
        if(r==lg || l==lg) break;
        //cout<<l<<" "<<r<<endl;
        if(l<r) swap(nums[l],nums[r]);
        else l--;
    }
     //Check
    for(auto num:nums) cout<<num<<ends;
    cout<<endl;
}*/
int main()
{
    vector<int> nums;int temp;
    while(cin>>temp)
    {
        nums.push_back(temp);
    }
    moveZeroes(nums);
    return 0;
}
两数之和
题意:

给定一个数组nums和整数K,求当i != jnums[i]+nums[j]==K,返回任意顺序的 i 和 j

解:

map,第一遍遍历存数字A第一次出现的位置,第二遍遍历对于每个A找K-A是否存在(特判A==K-A)

优化:只需要一遍遍历,处理A的时候先不把A加入map,直接查找K-A是否已经存在,就能解决A==K-A的特判

unordered_map:操作快,但不稳定,空间占用大部分情况下大于map

代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> twoSum(vector<int>& nums, int target)
{
    map<int,int>mp;
    int lg=nums.size();
    for(int i=0;i<lg;i++)
    {
        if(mp.count(target-nums[i])) return {mp[target-nums[i]],i};
        else mp[nums[i]]=i;
    } 
    return {0,1};
}
int main()
{
    vector<int> nums;int t,temp;cin>>t;
    while(cin>>temp)
    {
        nums.push_back(temp);
    }
    vector<int>ans=twoSum(nums,t);
    for(auto a:ans) cout<<a<<ends;
    cout<<endl; 
    return 0;
}
有效的数独
题意:

给一个9*9的char数组,表示一个数独,检查每一行,每一列和每个小九宫格里是否有重复的数字

解:

三次遍历,行检查、列检查、小九宫格检查,int使用位运算替换数组进行更快的取值判断和初始化

应该有比官方题解使用更少空间(book进行标记,num可以替换,starts可以换成9段循环或者一大段if条件)

官方题解:使用多个数组存储每行、每列、每个小九宫格里的数字数量,遍历一次,通过位置加入数组

代码:
#include<bits/stdc++.h>
using namespace std;
bool isValidSudoku(vector<vector<char>>& board)
{
    int book,num;
    for(int i=0;i<9;i++)
    {
        book=0;
        for(int j=0;j<9;j++)
        {
            if(board[i][j]=='.') continue;//忽略.
            else
            {
                num=board[i][j]-'0';
                if(book & (1<<num)) return false;
                else book=book|(1<<num); 
            }
        }
    }
    
    for(int i=0;i<9;i++)
    {
        book=0;
        for(int j=0;j<9;j++)
        {
            if(board[j][i]=='.') continue;//忽略.
            else
            {
                num=board[j][i]-'0';
                if(book & (1<<num)) return false;
                else book=book|(1<<num); 
            }
        }
    }
    int starts[9][2]={ {0,0},{3,0},{6,0},{0,3},{3,3},{6,3},{0,6},{3,6},{6,6} }; 
    for(auto start:starts)
    {
        book=0;
        for(int i=start[0];i<start[0]+3;i++)
        {
            for(int j=start[1];j<start[1]+3;j++)
            {
                if(board[i][j]=='.') continue;//忽略.
                else
                {
                    num=board[i][j]-'0';
                    if(book & (1<<num)) return false;
                    else book=book|(1<<num); 
                }
            }
        }
    }
    return true; 
}
int main()
{
    vector<vector<char>> board;int row;cin>>row;
    while(row--)
    {
        vector<char>temp;char ctemp;
        while(cin>>ctemp)
        {
            temp.push_back(ctemp);  
        }
        board.push_back(temp); 
    }
    bool ans=isValidSudoku(board);
    cout<<ans<<endl;
    return 0;
}
旋转图像
题意:

一个N*N数组,顺时针旋转90度,要求原地算法

解:

因为是旋转90度,所以每四个格子相互关联,推一下下标变换就可以了,处理时注意第一行是(0,lg-2)第二行就是(1,lg-3)因为每次旋转完成相当于少一层,行列长度均-2(头尾),同时每行最后一个是由第一个变换来的,再推一下关系

还有一个官方题解两次翻转,上下翻转+对角线翻转,挺妙的,了解一下就行

代码:
#include<bits/stdc++.h>
using namespace std;
void rotate(vector<vector<int>>& matrix)
{
    int lg=matrix.size();
    for(int i=0;i<lg/2;i++)
    {
        for(int j=0;j<lg;j++)
        {
            swap(matrix[i][j],matrix[lg-i-1][j]);
        }
    }
    for(int i=0;i<lg;i++)
    {
        for(int j=i;j<lg;j++)
        {
            swap(matrix[i][j],matrix[j][i]);
        }
    }
    
}
/*
void rotate(vector<vector<int>>& matrix)
{
    int lg=matrix.size();
    for(int i=0;i<lg-1;i++)
    {
        for(int j=i;j<lg-i-1;j++)
        {
            int temp=matrix[i][j];
            swap(temp,matrix[j][lg-i-1]);
            swap(temp,matrix[lg-i-1][lg-j-1]);
            swap(temp,matrix[lg-j-1][i]);
            swap(temp,matrix[i][j]);
        }
    }
}*/
int main()
{
    vector<vector<int>> matrix;int row;cin>>row;
    while(row--)
    {
        vector<int>temp;int itemp;
        while(cin>>itemp)
        {
            temp.push_back(itemp);
        }
        matrix.push_back(temp);
        cin.clear(); 
    }
    rotate(matrix);
    for(auto row:matrix)
    {
        for(int col:row) cout<<col<<ends;
        cout<<endl;
    }
    return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值