Leecode初级算法C++题解(数组篇)

数组篇

1.从排序数组中删除重复项(leecode第26题)

题目要求:给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
算法说明:简单题,因为要求空间复杂度O(1),不能额外创建数组,可以创建个变量res保存不一样的数的长度。

1.1利用vector类的方法(个人的)
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()==0)
            return 0;
        int temp=nums[0];
        int res=1;
        vector<int>::iterator itor;
        for(itor=nums.begin()+1;itor!=nums.end();)
        {
            if(*itor==temp)
                itor=nums.erase(itor);
            else
            {
                temp=*itor;
                itor++;
                res++;
            }
        }
        return res;    
    }
        
}

这里主要是学习使用了vector类的迭代器iterator和删除函数erase(iterator it),erase返回的是删除的元素的下一个元素迭代器位置。
具体查看这里C++ vector容器简介.
也可以参考别人的初级算法题解

1.2双指针法

数组完成排序后,我们可以放置两个指针 i 和 j,其中 i是慢指针,而 j 是快指针。只要 nums[i]=nums[j],我们就增加 j 以跳过重复项。
当我们遇到 nums[j]≠nums[i] 时,跳过重复项的运行已经结束,因此我们必须把它 nums[j] 的值复制到 nums[i+1]。然后递增 i,接着我们将再次重复相同的过程,直到 j到达数组的末尾为止。

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

2.买卖股票的最佳时机II(leecode第122题)

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

2.1暴力法

这种情况下,我们只需要计算与所有可能的交易组合相对应的利润,并找出它们中的最大利润。利用迭代的方法

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        return calculate(prices,0);
    }
    int calculate(vector<int>& prices,int s){
        if(s>=prices.size())
            return 0;
        int max=0;
        for(int start=s;start<prices.size();start++)
        {
            int maxprofit=0;
            for(int end=start+1;end<prices.size();end++)
            {
                if(prices[start]<prices[end])
                {	//	迭代求出profit为每一个组合的利润
                   int profit=calculate(prices,end+1)+prices[end]-prices[start];
                   if(profit>maxprofit)
                       maxprofit=profit;
                }
            }
            if(maxprofit>max)
                max=maxprofit;
        }
    return max;
    }
    
};

该方法运行时间过长,迭代次数太多了,时间复杂读O(nn)

2.2贪心法

算法:只要i+1天比i天价格高就卖出,累加就能得出结果了

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        
        int n=prices.size();
        int maxprofit=0;
        for(int i=0;i<n-1;i++)
            if(prices[i+1]>prices[i])
                maxprofit+=prices[i+1]-prices[i];
        return maxprofit;
        
    }
};

3.旋转数组(leecode第189题)

题目:给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]

3.1循环交换法

循环k次,每次都将数组向右移动一次

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
    	k%=nums.size();
        for(int i=0;i<k;i++)
        {
            int temp=nums[nums.size()-1];
            for(int j=nums.size()-1;j>0;j--)
                swap(nums[j],nums[j-1]);
            nums[0]=temp;
            
        }
    }
};

程序中用到c++提供的swap函数用于交换, swap 包含在命名空间std 里面,还能交换结构体。

3.2反转法

该思想是将整个数组反转一次,再将1-k反转一次,k-n反转一次就能得到结果
举例如k=3,[1,2,3,4,5,6,7]---->[7,6,5,4,3,2,1]
[7,6,5]–>[5,6,7]
[4,3,2,1]–>[1,2,3,4]结果就变成[5,6,7,1,2,3,4]
不过该方法要写一个反转函数

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int n=nums.size();
        k=k%n;
        if(n==1)
            return;
        inverse(nums,0,n-1);
        inverse(nums,0,k-1);
        inverse(nums,k,n-1);
     }
    void inverse(vector<int>& nums,int start,int end)
    {
        while(start<end)
        {
            swap(nums[start],nums[end]);
            start++;
            end--;
        }
    }
};

该方法时间复杂度O(n),空间复杂度O(1).

4.存在重复数(leecode第217题)

题目:给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

4.1 set容器法

算法:这题很简单,刚开始我直接暴力法解决的,但是测试用例较大运行时间过长,没通过就不写了,这里学习使用了c++ STL模板库中set关联容器,把数组的元素insert到set容器,insert前判读容器中是否已经存在,存在的话返回true,所有元素insert完返回false。
也有人用的是unordered_set 容器是c++ 11新加入容器,set是通过RB-tree实现,unordered_set是通过哈希表实现的
不懂set的用法可以参考set的具体用法
程序

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        int n=nums.size();
        set<int> temp;
        for(int i=0;i<n;i++)
            if(temp.count(nums[i])==0)
                temp.insert(nums[i]);
            else
                return true;
        return false;    
    }
};

5 只出现一次的数(leecode第136题)

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
本题是面试原题,难度在于不能用额外空间和线性时间复杂度(就是时间复杂度为O(n)),一般使用的是异或的方法
异或(^):1与1,0与0异或为0(相同即为假,不同则为真)
注:

  • A.相同的数异或为0,
  • B.任何数与0异或为任何数
  • C.异或满足交换律
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int m=0;
        for(int i=0;i<nums.size();i++)
            m=m^nums[i];
        return m;
 }
};

6 两个数组的交集II(leecode第350题)

题目:给定两个数组,编写一个函数来计算它们的交集。
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致,我们可以不考虑输出结果的顺序。
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

6.1排序法

如果已经排好序,我们可以通过用一个for循环从小到大来比较,如果其中一个数组的数比另一个小了,将这个数组下标+1再判断,如果两个值相同,下标都加一,且将该数字保存到结果数组中。

class Solution {//还有点问题,运行不了
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        sort(nums1.begin(),nums2.end());
        sort(nums2.begin(),nums2.end());
        vector<int> res;
        int n1=nums1.size();
        int n2=nums2.size();
        for(int i=0,j=0;i<n1 && j<n2;)
        {
            if(nums1[i]>nums2[j])
                j++;
            else if (nums1[i]<nums2[j])
                i++;
            else if(nums1[i]==nums2[j])
            {   
                res.push_back(nums1[i]);
                i++;
                j++;               
            }
        }
        return res;            
    }
};
6.2 set交集法

C++ STL 提供求交集的函数 set_intersection( ) 、求集合差的函数 set_difference( ) 和合并两个集合的函数 set_union( ) 。这里使用了set_intersection()求两个数组交集,线性时间复杂度,set_intersection( ) 函数用法链接

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int>res;
        //set_intersection 传入的容器必须是有序的
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
set_intersection(nums1.begin(),nums1.end(),nums2.begin(),nums2.end(),back_inserter(res)); 
            return res;
    }
};
6.3哈希法

用map映射容器建立哈希表的方法,先用nums1数组建立哈希表,然后用nums2数组来查找重复的数,输出到结果中,并将个数减一

map<int,int> stable;//第一个int为数组的值,第二个为值的个数

程序:

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        map<int,int> table;
        vector<int> res;
        for(int i=0;i<nums1.size();i++)//建立哈希表
            table[nums1[i]]++;
        for(int i=0;i<nums2.size();i++)
            if(table[nums2[i]]>0) {res.push_back(nums2[i]);table[nums2[i]]--;}
        return res;
    }
};

7 加一(leecode第66题)

题目:给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储一个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

7.1 转换法

将数组表示的值算出存在单个int变量中,加一后再将各位数字拆分放在结果数组中,但是该方法所能表示的数不能超过int的存储范围,在leecode上未通过由于测试用例数据太大。

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int n=digits.size();
        int value=0,i=0;
        int end,start;
        vector<int> res;
        while(n>0)
        {
            value+=(int)(digits[i]*pow(10,n-1));
            i++;n--;
        }
        value++;
        while(value>10)
        {
            res.push_back(value%10);
            value/=10;
        }
        res.push_back(value);
        end=res.size()-1;
        start=0;
        while(start<end)
        {
            swap(res[start],res[end]);
            start++;
            end--;
        }
        return res;
    }
};
7.2 进制法

该方法是设置一个进位标志count,加一,所以count初始化为1,遍历数组每一个元素,如果count=1,将该位加一,且加一是否大于10来改变count值,否则不处理,,如果最后一位count还为1,必须新push_back一个1到首位,将改变的digits输出到结果里。

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int n=digits.size();
        vector<int> res;
        int count=1;
        for(int i=n-1;i>=0;i--)
            if(count==1)
                if(digits[i]+1>9)
                {
                    digits[i]=0;
                    count=1;
                }
                else
                {
                    digits[i]=digits[i]+1;
                    count=0;
                }
        if(count==1)
            res.push_back(1);
        for(int i=0;i<n;i++)
            res.push_back(digits[i]);          
        
    return res;
    }
};

8 移动零(leecode第283题)

题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
算法:该方法是将不是零的数放在前面,后面补零。

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

9.两数之和(leecode第1题)

题目:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

9.1暴力法

直接用所有元素遍历查找就能得出结果

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> res;
        for(int i=0;i<nums.size();i++)
            for(int j=i+1;j<nums.size();j++)
                if(target==nums[i]+nums[j])
                {
                    res.push_back(i);
                    res.push_back(j);
                    break;
                }     
        return res;
    }
};

注:返回其实可以不用向量,直接返回。

if(target==nums[i]+nums[j])
               return {i,j};
 return {i,j};

9.2两遍哈希法

可以通过哈希表来查找,减少时间复杂度,本程序通过map容器建立哈希表,key存数组值,元素存数组位置。利用map的count函数判断是否有这个元素。时间复杂度,空间复杂度都是O(n)。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int,int> table;
        vector<int> res;
        for(int i=0;i<nums.size();i++)
            table[nums[i]]=i;
        for(int i=0;i<nums.size();i++)
            if(table.count(target-nums[i])>0 && table[target-nums[i]]!=i )
            {
                res.push_back(i);
                res.push_back(table[target-nums[i]]);
                break;
            }
        return res;
    }
};

其实如果哈希表建立好后,可以用iterator迭代器返回位置。

map <int,int>:: iterator it;
it=table.find(target-nums[i]]);//it 保存的是元素位置,find没找到会返回end的位置
            if(it!=table.end() && it->second!=i)
            {
                res.push_back(i);
                res.push_back(table[target-nums[i]]);
                break;
            }

注:it->second 是元素的值,it->first 是key的值。

9.3一遍哈希法

其实上个方法相似,时间复杂度,空间复杂度也都是O(n)。只不过在创建哈希表的时候就把结果查找出来。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int,int> table;
        vector<int> res;
        for(int i=0;i<nums.size();i++)
        {
            if(table.count(target-nums[i])>0)
            {                
                res.push_back(table[target-nums[i]]);
                res.push_back(i);                
                break;
            }
            table[nums[i]]=i;
        }
        return res;
    }
};

10.有效的数独(leecode第36题)

题目:判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。

  • 数字 1-9 在每一行只能出现一次。
  • 数字 1-9 在每一列只能出现一次。
  • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
  • 空白格用 ‘.’ 表示。

示例 1:

输入:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
输出: true
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 ‘.’ 。
给定数独永远是 9x9 形式的。

10.1集合法

解题思路:我是用set容器通过集合的方式解决的,首先创建行,列,块各九个集合,对81个元素进行遍历,如果字符不是’ . ',就将字符插入对应容器,插入前先判断是否已经存在该字符,存在返回false,否则插入。遍历完后返回true。
行列集合好分类,块的分类参考了官方的题解,如下:

block=(row/3)*3+column/3;

程序:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        set<char> row[9],column[9],block[9];
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)
            {
                if(board[i][j]!='.')
                {
                    if(row[i].count(board[i][j])==0)	
                        row[i].insert(board[i][j]);
                    else
                        return false;
                    
                    if(column[j].count(board[i][j])==0)
                        column[j].insert(board[i][j]);
                    else
                        return false;
                    
                    if(block[(i/3)*3 +j/3].count(board[i][j])==0)
                        block[(i/3)*3 +j/3].insert(board[i][j]);
                    else
                        return false;
                }
           }
        return true;
    }
};

11.旋转图像(leecode第48题)

题目:给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

11.1 整体反转法

解题思路:

  • 先将方阵按(\)左上到右下对角的反转,再垂直反转,或者
  • 先垂直反转,再将方阵按(/)左下到右上对角的反转,或者
  • 先水平反转,再将反正按(\)左上到右下对角的反转,或者
  • 先将方阵按(/)左下到右上对角的反转,再水平反转。

这四种方式都可以得到顺时针旋转90°的效果,不太理解可以用个方片在手里模拟一下。
程序:

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();
        for(int i=0;i<n;i++)
            for(int j=0;j<i;j++)
                swap(matrix[i][j],matrix[j][i]);
        for(int i=0;i<n;i++)
            for(int j=0;j<n/2;j++)
                swap(matrix[i][j],matrix[i][n-j-1]);        
    }
};
11.2分圈旋转法

解题思路:可以从外圈到内圈一圈圈旋转,每一圈可以看成四个边组成,每个边有n-1个数。

a b----> d a		//	a,b,c,d分别代表每个边
c d----> c b

a和b换,a和d换,a和c换,就是顺时针旋转90°。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size();
        for(int i=0;i<n/2;i++)
            for(int j=i;j<n-1-i;j++)
            {
                swap(matrix[i][j],matrix[j][n-i-1]);
                swap(matrix[i][j],matrix[n-i-1][n-j-1]);
                swap(matrix[i][j],matrix[n-j-1][i]);
            }                         
    }
};

数组篇到此结束。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值