LeetCode 數組 題目總結

2020.2.16
☟☟☟

(1)1.兩數之和

題目:給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。

你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。

示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

暴力法:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
       // int i,j;
        int len=nums.size();
        for(int i=0;i<len-1;i++){
            for(int j=i+1;j<len;j++){
                if(nums[i]+nums[j]==target){
                    return{i,j};//如果找到,就返回
                }
            }
        }
        return {};//沒有找到返回空數組
    }
};

Hash表

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int,int> a;//提供一對一的hash
        vector<int> res;//用來承載結果
        for(int i=0;i<nums.size();i++)
        {   //先找再存,這樣有先後關係,在已經存的元素中去找可以湊成2sum對的元素,防止同一個數被使用兩次
            if(a.count(target-nums[i]))
            {
                res.push_back(a[target-nums[i]]);
                res.push_back(i);
                break;
            }
            a[nums[i]]=i;//反過來放入map中,用來獲取結果下標
        }
        return res;
    };
};

☟☟☟

(2)167 兩數之和||-輸入有序數組

       167 Two Sum II - Input array is sorted

給定一個已按照升序排列 的有序數組,找到兩個數使得它們相加之和等於目標數。

函數應該返回這兩個下標值 index1 和 index2,其中 index1 必須小於 index2。

說明:

返回的下標值(index1 和 index2)不是從零開始的。
你可以假設每個輸入只對應唯一的答案,而且你不可以重複使用相同的元素。
示例:

輸入: numbers = [2, 7, 11, 15], target = 9
輸出: [1,2]
解釋: 2 與 7 之和等於目標數 9 。因此 index1 = 1, index2 = 2 。
思路:雙指針,因爲是有序的。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        //vector<int> res;//用vector保存也行
        int l=0;
        int r=numbers.size()-1;
        while(l<r){//從兩邊開始掃描
            if(numbers[l]+numbers[r]<target){
                l++;
            }
            else if(numbers[l]+numbers[r]>target){
                r--;
            }
            else{
                return {l+1,r+1};
            }
        }
        return {};
    }
};

hash,不快

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

☟☟☟

(3)15.三數之和

給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。

注意:答案中不可以包含重複的三元組。

示例:

給定數組 nums = [-1, 0, 1, 2, -1, -4],

滿足要求的三元組集合爲:
[
[-1, 0, 1],
[-1, -1, 2]
]
思路:排序之後用雙指針,轉化爲兩數之和問題。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int>tmp;
        int len=nums.size();
        if(len<=2) return res;
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<len-2;i++){
            if(nums[i]>0){
                return res;
            }
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            int left=i+1,right=len-1;
            while(left<right){
                if(nums[i]+nums[left]+nums[right]==0){
                    tmp.push_back(nums[i]);
                    tmp.push_back(nums[left]);
                    tmp.push_back(nums[right]);
                    res.push_back(tmp);
                    tmp.clear();
                        while(left<right && nums[left]==nums[left+1]) left++;
                        while(left<right && nums[right]==nums[right-1]) right--;
                    //left++;
                    right--;
                }
                else if(nums[i]+nums[left]+nums[right]<0){
                    left++;
                }
                else{
                    right--;             
                }
            }
        }
        return res;
    }
};

☺☺☺

(4)16.最接近的三數之和

給定一個包括 n 個整數的數組 nums 和 一個目標值 target。找出 nums 中的三個整數,使得它們的和與 target 最接近。返回這三個數的和。假定每組輸入只存在唯一答案。

例如,給定數組 nums = [-1,2,1,-4], 和 target = 1.
與 target 最接近的三個數的和爲 2. (-1 + 2 + 1 = 2).

思路:雙指針。排序後! 掃描a[i],後面在用left和right首尾兩指針掃描

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        int len=nums.size();
        if(len<3) return 0;
        sort(nums.begin(),nums.end());
        int sum=nums[0]+nums[1]+nums[2];
        for(int i=0;i<len-2;i++){
            int left=i+1,right=len-1;
            while(left<right){
                int temp=nums[i]+nums[left]+nums[right];
                if(abs(temp-target)<abs(sum-target)){
                    sum=temp;
                }
                if(temp<target){
                    left++;
                }
                else {
                    right--;
                }
            }
        }
        return sum;
    }
};

☺☺☺

(5)18.四數之和

給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。

注意:
答案中不可以包含重複的四元組。

示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
滿足要求的四元組集合爲:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路:排序,雙指針;參照三數之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        int len=nums.size();
        if(len<4) return res;
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<len-3;i++){
            if(i>0 && nums[i]==nums[i-1]) continue;  //重複
            for(int j=i+1; j<len-2;j++){
                if(j>i+1 && nums[j]==nums[j-1]) continue;  //重複
                int left=j+1, right=len-1;
                while(left<right){
                    if(nums[i]+nums[j]+nums[left]+nums[right]==target){
                        res.push_back({nums[i],nums[j],nums[left],nums[right]});
                        while(left<right && nums[left+1]==nums[left]) left++;  //重複
                        while(left<right && nums[right-1]==nums[right]) right--;  //重複

                        left++;
                    }
                    else if(nums[i]+nums[j]+nums[left]+nums[right]<target){
                        left++;
                    }
                    else{
                        right--;
                    }
                }
            
            }
        }
        return res;
    }
};

也可以定義一個set,可以避免重複項,記得最後要轉化回去

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //vector<vector<int>> res;
        set<vector<int>> res;
        int len=nums.size();
        if(len<4) return  vector<vector<int>>(); //或者用{{}}
       
        sort(nums.begin(),nums.end());//排序
        for(int i=0;i<len-3;i++){
            //if(i>0 && nums[i]==nums[i-1]) continue;
            for(int j=i+1; j<len-2;j++){
                //if(j>i+1 && nums[j]==nums[j-1]) continue;
                int left=j+1, right=len-1;
                while(left<right){
                    if(nums[i]+nums[j]+nums[left]+nums[right]==target){
                        vector<int> tmp{nums[i], nums[j], nums[left], nums[right]};
                        res.insert(tmp); //用set,當有重複結果時,插入會失敗
                        left++;
                    }
                    else if(nums[i]+nums[j]+nums[left]+nums[right]<target){
                        left++;
                    }
                    else{
                        right--;
                    }
                }
            
            }
        }
        return vector<vector<int>>(res.begin(), res.end()); //由set轉化爲vector輸出
    }
};

☺☺☺

(6)217.存在重複元素

給定一個整數數組,判斷是否存在重複元素。
如果任何值在數組中出現至少兩次,函數返回 true。如果數組中每個元素都不相同,則返回 false。

示例 1:
輸入: [1,2,3,1]
輸出: true
示例 2:
輸入: [1,2,3,4]
輸出: false
示例 3:
輸入: [1,1,1,3,3,4,3,2,4,2]
輸出: true

法1:哈希,判斷key值是否大於1
//unordered_map<int,int> map;//無序map更快

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        map<int,int> p;
        for(int i=0;i<nums.size();i++){
            p[nums[i]]++;   //注意後寫if條件
            if(p[nums[i]]>1){
                return true;
            }
        }
        return false;
    }
};

法2.利用集合,與原數組比較大小

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        set<int> st (nums.begin(),nums.end());  //初始化
        return (st.size()==nums.size()) ? false:true; 
        //return nums.size()>st.size(); 
    }
};

法3.排序,比較前後兩個元素是否相等,來判斷是否存在重複元素

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        int len=nums.size();
        if(len<2) return false;
        sort(nums.begin(),nums.end());
        for(int i=1; i<nums.size();i++){
            if(nums[i]==nums[i-1]){  //注意從1開始
                return true;
            }
        }
        return false;
    }
};

☺☺☺

(7)26. 刪除排序數組中的重複項

給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。
示例 1:
給定數組 nums = [1,1,2],
函數應該返回新的長度 2, 並且原數組 nums 的前兩個元素被修改爲 1, 2。
你不需要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,0,1,1,1,2,2,3,3,4],
函數應該返回新的長度 5, 並且原數組 nums 的前五個元素被修改爲 0, 1, 2, 3, 4。
你不需要考慮數組中超出新長度後面的元素。
方法:雙指針法(覆蓋法);相等時不覆蓋,不等時覆蓋
設置兩個指針,一個慢指針i,一個快指針j,當nums[i]!=nums[j],將nums[i+1]=nums[j]。

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

☺☺☺

(8)80. 刪除排序數組中的重複項 II 【中等】

給定一個排序數組,你需要在原地刪除重複出現的元素,使得每個元素最多出現兩次,返回移除後數組的新長度。
不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完成。

示例 :
給定 nums = [1,1,1,2,2,3],
函數應返回新長度 length = 5, 並且原數組的前五個元素被修改爲 1, 1, 2, 2, 3 。

思路:原地刪除,用雙指針!快指針:遍歷整個數組;慢指針:記錄可以覆寫數據的位置;
當數組的長度小於等於 2 時,不需要操作,直接返回原數組即可。

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

☺☺☺

(9)349. 兩個數組的交集

給定兩個數組,編寫一個函數來計算它們的交集。

示例 1:
輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2]
示例 2:
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [9,4]
說明:
輸出結果中的每個元素一定是唯一的。
我們可以不考慮輸出結果的順序。
法1:查找,用set避免重複
注意:迭代器 end 指向尾元素的“下一個位置”
O(n) ,O(n)

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> st1(nums1.begin(),nums1.end()); //用unordered_set對nums1的元素去重
        unordered_set<int> st2(nums2.begin(),nums2.end());//用unordered_set對nums2的元素去重
        vector<int> res;
        for(int i:st2){//set不能用下標運算符,所以寫成**a:st2**,遍歷st2中元素
            if(st1.find(i)!=st1.end()){  //必須寫**!=st1.end()**
                res.push_back(i);
            }
        }
    return res;
    }
};

法2:同法1

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
   
        unordered_set<int> m(nums1.begin(), nums1.end()); //將第一個數組的元素建立一個unordered_set
        vector<int> res;//建立關於結果的vector

        for(int a:nums2)  //set不能用下標運算符,所以寫成**a:nums2**,遍歷nums中元素
        {
            if( m.count(a))  //查找了m中是否存在a,
            {
                res.push_back(a);//在vector res的末尾加入a
                m.erase(a);//去除該數,//**刪除的是m中的元素**
                //這裏不理解, a是迭代器嗎???????和(10)法1比較
                //因爲在m中刪除,所以變成迭代器了???
            }
        }
        return res;
    }
};

自我理解上面問題:set裏面不需要,vector需要抽象出一個迭代器。

法3:排序後再找
空間換時間
O(nlogn),O(1)

**注意:獲取首元素和尾元素最直接的方法是調用 front 和 back **

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int i=0,j=0;
        while (i<nums1.size() && j<nums2.size()){
            if(nums1[i]<nums2[j]){
                i++;
            }
            else if(nums1[i]>nums2[j]){
                j++;
            }
            else{//==
                if(res.empty() || res.back()!=nums1[i]){
                    res.push_back(nums1[i]);
                }
                i++;
                j++;
            }
        }
        return res;
    }
};

細節注意:c.erase(p)是刪除迭代器p所指的元素,所以說p是一個迭代器!

set<int> iset={1,2,3,4,5,6,7,8,9,10};
iset.find(1);  //返回一個迭代器!!!,指向key==1的元素
iset.find(11);  //返回一個迭代器!!!,其值等於iset.end()
iset.count(1);   //返回1
iset.count(11);  //返回0

☺☺☺

(10)350. 兩個數組的交集 II

給定兩個數組,編寫一個函數來計算它們的交集。

示例 1:
輸入: nums1 = [1,2,2,1], nums2 = [2,2]
輸出: [2,2]
示例 2:
輸入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
輸出: [4,9]
說明:
輸出結果中每個元素出現的次數,應與元素在兩個數組中出現的次數一致。
我們可以不考慮輸出結果的順序。

法1:直接在數組上查找,查找完之後刪除

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int>::iterator it;  //必須先定義迭代器it
        vector<int> res;
        for(int i:nums1){// i是一個值
            it=find(nums2.begin(),nums2.end(),i);  //it是一個迭代器
            if(it!=nums2.end()){
                res.push_back(i); // 或者 res.push_back(*it);
                nums2.erase(it);  //it是迭代器
            }
        }
        return res;
    }
};

排序後再找

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
		if(nums1.empty() || nums2.empty()) return {};
        vector<int> res;
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int i=0,k=0;
        while( i<nums1.size() && k<nums2.size() )
        {
            if(nums1[i]==nums2[k])
            {
                res.push_back(nums1[i]);
                i++;
                k++;
            }
           else  if(nums1[i]>nums2[k])
                k++;
           else
                i++;
        }
       return res;
    }
};

法3:hash表
**思路:此題可以看成是一道傳統的映射題(map映射),爲什麼可以這樣看呢,因爲我們需找出兩個數組的交集元素,同時應與兩個數組中出現的次數一致。這樣就導致了我們需要知道每個值出現的次數,所以映射關係就成了<元素,出現次數>,所以我們可以首先統計數組1中所有元素的出現次數。然後再遍歷數組2,如果數組2中的元素在map中存在(出現次數大於0),該元素就是一個交集元素,我們就將其存入返回數組中並且將map中該元素的出現次數減一即可.
**

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

☺☺☺

(11)228. 彙總區間

給定一個無重複元素的有序整數數組,返回數組區間範圍的彙總。

示例 1:
輸入: [0,1,2,4,5,7]
輸出: [“0->2”,“4->5”,“7”]
解釋: 0,1,2 可組成一個連續的區間; 4,5 可組成一個連續的區間。
示例 2:

輸入: [0,2,3,4,6,8,9]
輸出: [“0”,“2->4”,“6”,“8->9”]
解釋: 2,3,4 可組成一個連續的區間; 8,9 可組成一個連續的區間。

思路:雙指針

因爲題目強調的是連續的區間,所以可用如下方法。第二指針結束位置:區間右端

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        vector<string> res;
        if(nums.empty()){
            return res;
        }
        for(int i=0; i<nums.size();){  //注意寫 ;
            int left=i;
            int right=i;
            while(right+1<nums.size() && nums[right+1]==nums[right]+1){
                right++;
            }
            if(right-left==0){
                res.push_back(to_string(nums[right]));
            }
            else if(right-left>0){
                res.push_back(to_string(nums[left])+"->"+ to_string(nums[right]));
            }
            i=right+1;
        }
        return res;
    }
};

注意:寫成這樣會超時:

            while(right+1<nums.size() && nums[right+1]-nums[right]==1){
                right++;
            }

方法同上:

class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        vector<string> res;
        if(!nums.empty()){
            long i = 0, j = 0;
            while(j < nums.size()){
                while(j < nums.size()-1 && nums[j+1] == nums[j]+1) j++;
                if(j == i){
                    res.push_back(to_string(nums[i]));
                }
                if(j > i) res.push_back(to_string(nums[i])+"->"+to_string(nums[j]));
                i = j + 1;
                j = i;
            }
        }
        return res;
    }
};

☺☺☺

(12)334. 遞增的三元子序列

給定一個未排序的數組,判斷這個數組中是否存在長度爲 3 的遞增子序列。

數學表達式如下:
如果存在這樣的 i, j, k, 且滿足 0 ≤ i < j < k ≤ n-1,
使得 arr[i] < arr[j] < arr[k] ,返回 true ; 否則返回 false 。
說明: 要求算法的時間複雜度爲 O(n),空間複雜度爲 O(1) 。

示例 1:
輸入: [1,2,3,4,5]
輸出: true
示例 2:
輸入: [5,4,3,2,1]
輸出: false
注意:可以是不連續的數,比如[2,1,5,0,4,6]也返回true

思路:雙指針。。。長度爲3意思是連續的3個數,[2,1,5,0,4,6]結果是[0,4,6]
m1, m2保存兩個較小數,找出一個同時大於m1和m2的數即返回。
時間複雜度:O(n)
空間複雜度:O(1)

class Solution {
public:
    bool increasingTriplet(vector<int>& nums) {
        int m1 = INT_MAX, m2 = INT_MAX;
        for (auto a : nums) {
            if (a<=m1) m1 = a; //a爲掃描過程中遇到的最小數,是第一個數的候選
            else if (a<=m2) m2 = a;//當a 大於m1,a可能是第二或第三個數
            else return true;   //  m1<m2<a,說明這樣的三元子序列存在
        }
        return false;
    }
};

☺☺☺

(13)66. 加一

給定一個由整數組成的非空數組所表示的非負整數,在該數的基礎上加一。
最高位數字存放在數組的首位, 數組中每個元素只存儲單個數字。
你可以假設除了整數 0 之外,這個整數不會以零開頭。

示例 1:
輸入: [1,2,3]
輸出: [1,2,4]
解釋: 輸入數組表示數字 123。
示例 2:
輸入: [4,3,2,1]
輸出: [4,3,2,2]
解釋: 輸入數組表示數字 4321。

/*
問題:數組加一,高位在前,低位在後
要考慮進位情況
*/
class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        //123    129     199   999
        for(int i=digits.size()-1; i>=0; i--){//從後往前掃描 
            if(digits[i]==9){//如果爲9,置0,下次進位
                digits[i]=0;
            }
            else{
                digits[i]++;
                return digits;//不爲9就加一,退出
            }
        }
        digits[0]=1;//如果最高位有進位,置1
        digits.push_back(0); //補充一位
        return digits;
    }
};

☟☟☟

(14)283. 移動零

知識點:c.erase(p) 刪除迭代器p所指定的元素,返回一個指向被刪除元素之後的迭代器(注意是 之後!);若p指向尾元素,則返回尾後迭代器。
c.push_back(t) 在c的尾部創建一個值爲t的元素,返回void;
c.insert(p,t) 在迭代器p指向的元素之前創建一個值爲t的元素。返回指向新添加的元素的迭代器; c.inert(p,n,t) 在迭代器p指向的元素之前插入n個值爲t的元素。返回指向新添加的第一個元素的迭代器。

題目:
給定一個數組 nums,編寫一個函數將所有 0 移動到數組的末尾,同時保持非零元素的相對順序。

示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]
說明:
必須在原數組上操作,不能拷貝額外的數組。
儘量減少操作次數

法1:遍歷數組,只要遇到0則刪除,count加1(計0的個數),最後在末尾補上count個0即可。因爲使用了迭代器,要注意的是避免迭代器失效。
方法不咋地,但是能學會運用不少東西啊,比如迭代器。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int count=0;
        for (auto it=nums.begin(); it!=nums.end();){
            if(*it==0){
                it=nums.erase(it);
                count++;
            }
            else it++;
        }
        while(count>0){
            nums.push_back(0);
            //nums.insert(nums.end(),0);
            count--;
        }
        //nums.insert(nums.end(),count,0);//可以用這個代替上面的while循環
    }
};

法2:雙指針

✘錯誤代碼

原因:int i 定義到了for循環裏面,是局部變量,當第一個for循環結束,i就不存在了。

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

**正確代碼,定義全局變量i **

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i=0; 
        for(i=0; i<nums.size() && nums[i]!=0; ++i);////找到第一個0;
        for(int j=i+1; j<nums.size(); j++){//將第一個零之後的第一個非零數字與該0交換
            if(nums[j]!=0){
                swap(nums[i],nums[j]);
                ++i;
            }
        }

    }
};

☟☟☟

(15)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
示例 2:
輸入:
[
  ["8","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"]
]
輸出: false
解釋: 除了第一行的第一個數字從 5 改爲 8 以外,空格內其他數字均與 示例1 相同。
     但由於位於左上角的 3x3 宮內有兩個 8 存在, 因此這個數獨是無效的。
說明:
一個有效的數獨(部分已被填充)不一定是可解的。
只需要根據以上規則,驗證已經填入的數字是否有效即可。
給定數獨序列只包含數字 1-9 和字符 '.' 。
給定數獨永遠是 9x9 形式的。

法1:暴力!先按行排查,再按列排查,最後按3*3排查。具體實現採用了輔助數組a[10]來統計0-9出現的次數,若a[x]>1,則返回false。
因爲要存放數字1-9,最大要到9的位置,所以定義數組大小爲a[10],下標爲0~9

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        
        //先按照行排查
        for(int i=0;i<9;++i){
            int a[10]={0,0,0,0,0,0,0,0,0,0};
            for(int j=0;j<9;++j){
                if(board[i][j]=='.') continue;
                a[board[i][j]-'0']+=1;
                if(a[board[i][j]-'0']>1) return false;
            }
        }
        //按列排查
        for(int j=0;j<9;++j){
            int a[10]={0,0,0,0,0,0,0,0,0,0};
            for(int i=0;i<9;++i){
                if(board[i][j]=='.') continue;
                a[board[i][j]-'0']+=1;
                if(a[board[i][j]-'0']>1)return false;
            }
        }
        //按照3*3排查
        for(int i=0;i<9;i=i+3){
            for(int j=0;j<9;j=j+3){
                int a[10]={0,0,0,0,0,0,0,0,0,0};
                for(int x=i;x<i+3;x++){
                  for(int y=j;y<j+3;y++){  
                    if(board[x][y]=='.') continue;
                    a[board[x][y]-'0']+=1;
                    if(a[board[x][y]-'0']>1)return false;
                  }  
                }
            }
        }
        return true;
    }
};

法2:這個解法有點燒腦!!!

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int row[9][10] = {0};// 哈希表存儲每一行的每個數是否出現過,默認初始情況下,每一行每一個數都沒有出現過
        // 整個board有9行,第二維的維數10是爲了讓下標有9,和數獨中的數字9對應。
        int col[9][10] = {0};// 存儲每一列的每個數是否出現過,默認初始情況下,每一列的每一個數都沒有出現過
        int box[9][10] = {0};// 存儲每一個box的每個數是否出現過,默認初始情況下,在每個box中,每個數都沒有出現過。整個board有9個box。
        for(int i=0; i<9; i++){
            for(int j = 0; j<9; j++){
                // 遍歷到第i行第j列的那個數,我們要判斷這個數在其所在的行有沒有出現過,
                // 同時判斷這個數在其所在的列有沒有出現過
                // 同時判斷這個數在其所在的box中有沒有出現過
                if(board[i][j] == '.') continue;
                int curNumber = board[i][j]-'0';
                if(row[i][curNumber]) return false; 
                if(col[j][curNumber]) return false;
                if(box[j/3 + (i/3)*3][curNumber]) return false;

                row[i][curNumber] = 1;// 之前都沒出現過,現在出現了,就給它置爲1,下次再遇見就能夠直接返回false了。
                col[j][curNumber] = 1;
                box[j/3 + (i/3)*3][curNumber] = 1;
            }
        }
        return true;
    }
};

☟☟☟

(16)48. 旋轉圖像

給定一個 n × n 的二維矩陣表示一個圖像。
將圖像順時針旋轉 90 度。

說明:
你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。

示例 1:
給定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋轉輸入矩陣,使其變爲:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
給定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋轉輸入矩陣,使其變爲:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]

對角線座標關係:
在這裏插入圖片描述

順時針旋轉矩陣:先上下翻轉,再沿對角線翻轉

//問題:旋轉圖像(要求:原地順時針旋轉90°)
/*
 * 順時針旋轉方法:先上下翻轉,再沿對角線翻轉
 * clockwise rotate
 * first reverse up to down, then swap the symmetry
 * 1 2 3     7 8 9     7 4 1
 * 4 5 6  => 4 5 6  => 8 5 2
 * 7 8 9     1 2 3     9 6 3
*/
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        reverse(matrix.begin(), matrix.end());  //stl中翻轉數組,對每列reverse,直接reverse matrix即可
        for(int i=0; i<matrix.size(); i++){//只需對一半元素執行操作
            for(int j=i+1; j<matrix[0].size(); j++){
                swap(matrix[i][j],matrix[j][i]);
            }
        }
    }
};

逆時針旋轉矩陣:先左右翻轉,再沿對角線翻轉

/*
 *  * 逆時針旋轉方法:先左右翻轉,再沿對角線翻轉
 * anticlockwise rotate
 * first reverse left to right, then swap the symmetry
 * 1 2 3     3 2 1     3 6 9
 * 4 5 6  => 6 5 4  => 2 5 8
 * 7 8 9     9 8 7     1 4 7
*/
void anti_rotate(vector<vector<int> > &matrix) {
    for (auto vi : matrix) reverse(vi.begin(), vi.end()); //對每行reverse,故要遍歷matrix,reverse內部容器
    for (int i = 0; i < matrix.size(); ++i) {
        for (int j = i + 1; j < matrix[i].size(); ++j)
            swap(matrix[i][j], matrix[j][i]);
    }
}

☟☟☟

(17)面試題57. 和爲s的兩個數字

輸入一個遞增排序的數組和一個數字s,在數組中查找兩個數,使得它們的和正好是s。如果有多對數字的和等於s,則輸出任意一對即可。


示例 1:

輸入:nums = [2,7,11,15], target = 9
輸出:[2,7] 或者 [7,2]
示例 2:

輸入:nums = [10,26,30,31,47,60], target = 40
輸出:[10,30] 或者 [30,10]

思路:雙指針

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> res;
        int left=0, right=nums.size()-1;
        while(left < right){
            if(nums[left]+nums[right]==target){
                res.push_back(nums[left]);
                res.push_back(nums[right]);
                break; //找到了, 就彈出
            }
            else if(nums[left]+nums[right] < target) left++;
            else right--;
        }
        
        return res;
    }
};

如果題目要求輸出乘積最小的,如下:
題目描述
輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。

則,思路也一樣,因爲:

不要被題目誤導了!證明如下,清晰明瞭:
//輸出兩個數的乘積最小的。這句話的理解?
假設:若b>a,且存在,
a + b = s;
(a - m ) + (b + m) = s
則:(a - m )(b + m)=ab - (b-a)m - m*m < ab;說明外層的乘積更小
class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> result;
        int size=array.size();
        int left=0;
        int right=size-1;
        while(right>left){
            if(array[left]+array[right]==sum){
                result.push_back(array[left]);
                result.push_back(array[right]);
                break;
            }
            else if(array[left]+array[right]<sum){
                left++;
            }
            else{
                right--;
            }
        }
        return result;
    }
};

也貼上這個代碼吧,萬一人家要求輸出乘積最大的呢。變一下下面的符號就行。

//雙指針
class Solution {
public:
    vector<int> FindNumbersWithSum(vector<int> array,int sum) {
        vector<int> ret;
        int i = 0, j = array.size()-1;
        while(i < j){
            if(array[i] + array[j] == sum){
                if(ret.empty()){
                    ret.push_back(array[i]);
                    ret.push_back(array[j]);
                }
                else if(array[i]*array[j] < ret[0]*ret[1]){
                    ret[0] = array[i];
                    ret[1] = array[j];
                }
                ++i;
                --j;    
            }
            else if(array[i] + array[j] < sum) ++i;
            else --j;          
        }
        return ret;
    }
};

☟☟☟

(18)面試題57 - II. 和爲s的連續正數序列

輸入一個正整數 target ,輸出所有和爲 target 的連續正整數序列(至少含有兩個數)。

序列內的數字由小到大排列,不同序列按照首個數字從小到大排列。

示例 1:

輸入:target = 9
輸出:[[2,3,4],[4,5]]
示例 2:

輸入:target = 15
輸出:[[1,2,3,4,5],[4,5,6],[7,8]]
求連續數和
等差數列公式:和 = n(a1+an)/2

思路:雙指針

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> res;
        
        int small=1; //正整數,從1開始
        int big=2;
        while(small<big){
            int sum=(big-small+1)*(big+small)/2; //等差數列
            if(sum==target){
                vector<int> tmp;
                for(int i=small; i<=big; i++){ //每個數都放入
                    tmp.push_back(i);
                }
                res.push_back(tmp);
                big++; //或small++;
            }
            else if(sum< target) big++;
            else if(sum> target) small++;
        }
        return res;
    }
};

☟☟☟

(19)面試題04. 二維數組中的查找

在一個 n * m 的二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。

示例:
現有矩陣 matrix 如下:
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
給定 target = 5,返回 true。
給定 target = 20,返回 false

思路:定位到右上角。從右上角開始比較,比它大就往下數一行,比它小就往左數一列

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0 || matrix[0].size()==0) return false;//記得寫
        int row=0;
        int col=matrix[0].size()-1; //定位到右上角
        while(row < matrix.size() && col>=0){//邊界條件
            if(matrix[row][col]==target) return true;
            else if(matrix[row][col] < target) row++;//如果當前位置元素比target小,則row++
            else if(matrix[row][col] > target) col--;//如果當前位置元素比target大,則col--
        }
        return false;
    }
};

☟☟☟

(20)面試題03. 數組中重複的數字

找出數組中重複的數字。

在一個長度爲 n 的數組 nums 裏的所有數字都在 0~n-1 的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。

示例 1:

輸入:
[2, 3, 1, 0, 2, 5, 3]
輸出:2 或 3 

法1:用一個數組存放出現的次數

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int n=nums.size();
        vector<int> index(n,0);
        int res;
        for(int i=0; i<n; i++){
            index[nums[i]]++;
            if(index[nums[i]]>1){
                res= nums[i]; //保存在全局變量,如果用局部變量,當退出for循環後,res也會消失
                break;
            }
        }
        return res;
    }
};

法2:hash

其實和法1是一樣的,把hash表理解成法1的數組。

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int n=nums.size();
        map<int,int>m;
        int res;
        for(int i=0; i<n; i++){
            m[nums[i]]++;
            if(m[nums[i]]>1){
                res= nums[i]; //保存在全局變量,如果用局部變量,當退出for循環後,res也會消失
                break;
            }
        }
        return res;
    }
};

☟☟☟

(21)面試題39. 數組中出現次數超過一半的數字

數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。
你可以假設數組是非空的,並且給定的數組總是存在多數元素。

示例 1:

輸入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
輸出: 2

解法一:排序取中位數


//時間O(nlogn),空間O(1)
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        return nums[nums.size()/2];
    }
};

解法二:建立哈希表法

//時間O(n),空間O(n)
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        map<int,int> m;
        int res;//定義全局變量
        for(int i=0; i<nums.size(); i++){
            m[nums[i]]++;
            if(m[nums[i]]>nums.size()/2){//不必等到哈希表完全建立再進行此判斷
                res=nums[i];
            }
        }
        return res;
    }
};

解法3:摩爾投票法
核心理念爲 “正負抵消” 。時間複雜度爲 O(N) ,空間複雜度爲 O(1) 。是本題的最佳解法。
在這裏插入圖片描述
如果有符合條件的數字,則它出現的次數比其他所有數字出現的次數和還要多。
在遍歷數組時保存兩個值:一是數組中一個數字,一是次數。遍歷下一個數字時,若它與之前保存的數字相同,則次數加1,否則次數減1;若次數爲0,則保存下一個數字,並將次數置爲1。遍歷結束後,所保存的數字即爲所求。

class Solution {
public:
    int majorityElement(vector<int>& numbers) {
        int n = numbers.size();
        if (n == 0) return 0;
         
        int num = numbers[0], count = 1;
        for (int i = 1; i < n; i++) {
            if (numbers[i] == num) count++;
            else count--;
            if (count == 0) {
                num = numbers[i];
                count = 1;
            }
        }
        return num;
    }
};

☟☟☟

(22)面試題53 - I. 在排序數組中查找數字 I(統計一個數字在排序數組中出現的次數。)

統計一個數字在排序數組中出現的次數。

示例 1:

輸入: nums = [5,7,7,8,8,10], target = 8
輸出: 2
示例 2:

輸入: nums = [5,7,7,8,8,10], target = 6
輸出: 0
法1:二分查找(面試官要的)

** 二分查找,通過兩次二分查找,分別找到第一個k和最後一個k,可以使時間複雜度減少爲O(logn)**

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int first= findFirst(nums, target);
        int last=findLast(nums,target);
        if(first==-1 || last==-1)
            return 0;
        return last-first+1;//計算次數
    }
    
    int findFirst(vector<int>& nums, int target){
        int left=0, right=nums.size()-1;
        while(left <= right){
            int mid=left+(right-left)/2;
            if(nums[mid] < target){
                left++;
            }
            else if(nums[mid] > target){
                right--;
            }
            else if(mid-1>=0 && nums[mid-1]==target){
                right=mid-1;
            }
            else 
                return mid;
        }
        return -1;
    }
    
    int findLast(vector<int>& nums, int target){
        int left=0, right=nums.size()-1;
        while(left <= right){
            int mid=left+(right-left)/2;
            if(nums[mid] < target){
                left++;
            }
            else if(nums[mid] > target){
                right--;
            }
            else if(mid+1<nums.size() && nums[mid+1]==target){
                left=mid+1;
            }
            else 
                return mid;
        }
        return -1;
    }
};
其他:簡單暴力,不是面試官想要的;

法2:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        return count(data.begin(),data.end(),k);
    }
};

法3:

//利用C++ stl的二分查找
class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        auto resultPair = equal_range(data.begin(), data.end(),k);
        return resultPair.second - resultPair.first;
    }
};

法4:

class Solution {
public:
    int GetNumberOfK(vector<int> data ,int k) {
        int num=0;
        for(int i=0;i<data.size();i++){
            if(data[i]==k)
                num++;
        }
        return num;
    }
};

☟☟☟

(23)面試題53 - II. 0~n-1中缺失的數字

一個長度爲n-1的遞增排序數組中的所有數字都是唯一的,並且每個數字都在範圍0~n-1之內。在範圍0~n-1內的n個數字中有且只有一個數字不在該數組中,請找出這個數字。

示例 1:
輸入: [0,1,3]
輸出: 2

示例 2:
輸入: [0,1,2,3,4,5,6,7,9]
輸出: 8

二分!!! 排序數組中的搜索問題,首先想到 二分法 解決。

根據題意,數組可以按照以下規則劃分爲兩部分。
左子數組: nums[i]=i ;
右子數組: nums[i]!=i ;
缺失的數字等於 “右子數組的首位元素” 對應的索引;因此考慮使用二分法查找 “右子數組的首位元素” 。

在這裏插入圖片描述
在這裏插入圖片描述

class Solution {
public:
    //二分
    int missingNumber(vector<int>& nums) {
        int left=0;
        int right=nums.size()-1;
        while(left<=right){
            int mid=left+(right-left)/2;
            if(nums[mid]==mid){
                left=mid+1;
            }
            else // !=
                right=mid-1;
        }
        return left;
    }
};

☟☟☟

(24)面試題66:構建乘積數組

給定一個數組 A[0,1,,n-1],請構建一個數組 B[0,1,,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:
輸入: [1,2,3,4,5]
輸出: [120,60,40,30,24]

思路:
B[i]的值可以看作下圖的矩陣中每行的乘積。
下三角用連乘可以很容求得,上三角,從下向上也是連乘。
因此我們的思路就很清晰了,先算下三角中的連乘,即我們先算出B[i]中的一部分,然後倒過來按上三角中的分佈規律,把另一部分也乘進去。

在這裏插入圖片描述

class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        int n=a.size();
        vector<int> b(n);
        if(n!=0){
            b[0]=1; //計算下三角連乘
            for(int i=1; i<n; i++){
                b[i]=b[i-1]*a[i-1];
            }
            //計算上三角
            int tmp=1;
            for(int j=n-2; j>=0; j--){
                tmp=tmp*a[j+1];
                b[j]=b[j]*tmp;
            }         
        }
        return b;//從B[0]~B[n-1]
    }
};

☟☟☟

(25)面試題21. 調整數組順序使奇數位於偶數前面

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的後半部分。

示例:
輸入:nums = [1,2,3,4]
輸出:[1,3,2,4] 
注:[3,1,2,4] 也是正確的答案之一。

法1思路:首尾雙指針

定義頭指針 left ,尾指針 right .
left 一直往右移,直到它指向的值爲偶數
right 一直往左移, 直到它指向的值爲奇數
交換 nums[left] 和 nums[right] .
重複上述操作,直到 left == rightleft==right .
class Solution {
public:
    vector<int> exchange(vector<int>& nums) {
     //   if (nums.empty()) return nums;
        int l = 0, r = nums.size() - 1;
        
        while (l < r) {
            while (l < r && nums[l] % 2 == 1) ++l;
            while (l < r && nums[r] % 2 == 0) --r;
            
            swap(nums[l], nums[r]);
        }
        
        return nums;
    }
};

法2: 用一個新數組保存 low

class Solution {
public:   //評論裏說此方法有點low!
    void reOrderArray(vector<int> &array) {
        vector<int> res;
        for(int i = 0; i < array.size(); i++)
        {
            if(array[i] % 2 == 1)
                res.push_back(array[i]);
        }
        for(int i = 0; i < array.size(); i++)
        {
            if(array[i] % 2 == 0)
                res.push_back(array[i]);
        }
        //array.swap(res);
        array = res;
    }
};

法3(沒看):插入算法思想,先把當前的數保存,整體後移。。

class Solution {
public:
    //插入排序思想
    void reOrderArray(vector<int> &array) {
         int size=array.size();
         for(int i=0;i<size;i++){
         int target = array[i];
         if(array[i] % 2 == 1){
             int j = i;
             while(j >= 1 && array[j-1] % 2 == 0){
                 array[j] = array[j-1];
                 j--;
             }
             array[j] = target;
                }
            }
    }
};

☟☟☟

(26)面試題61. 撲克牌中的順子

從撲克牌中隨機抽5張牌,判斷是不是一個順子,即這5張牌是不是連續的。2~10爲數字本身,A爲1,J爲11,Q爲12,K爲13,而大、小王爲 0 ,可以看成任意數字。A 不能視爲 14。

示例 1:
輸入: [1,2,3,4,5]
輸出: True

示例 2:
輸入: [0,0,1,2,5]
輸出: True

思路:排序後,求出0的數量,求出空的間隔數,比較它們

class Solution {
public:
    bool isStraight(vector<int>& nums) {
        int size=nums.size();
        //if(size<=0) return false;//沒有撲克
        
        sort(nums.begin(), nums.end());//注意排序
        
        int numof0=0;//0的數量
        for(int i=0; i<size; i++){
            if(nums[i]==0) numof0++;
        }
        
        int kong=0;//空格數量
        for(int i=numof0+1; i<size; i++){
            if(nums[i]==nums[i-1]) return false;
            else kong += nums[i]-nums[i-1]-1;
        }
        
        if(numof0 >= kong) return true;
        else return false;
    }
};

知識點:sort

c++中的sort函數和實例
sort中的比較函數compare要聲明爲靜態成員函數或全局函數,不能作爲普通成員函數,否則會報錯。 因爲:非靜態成員函數是依賴於具體對象的,而std::sort這類函數是全局的,因此無法再sort中調用非靜態成員函數。靜態成員函數或者全局函數是不依賴於具體對象的, 可以獨立訪問,無須創建任何對象實例就可以訪問。同時靜態成員函數不可以調用類的非靜態成員。

通俗說明用法:
c++中的sort函數一般用來對數組進行排序,有三個參數,第一個參數是是數組的起始位子,第二個參數爲你要排序的數組的終止位子。
第三個參數一般是排序的條件,可以通過這個參數達到各種各樣的排序(後面再講),也可以不寫,默認是升序。
如:int arr[5]={1,3,2,5,4}. 操作:sort(arr,arr+5). 結果{1,2,3,4,5} //默認升序
如: int arr[5]={1,3,2,5,3}. 操作:sort(arr,arr+3) 結果{1,2,3,5,4} //對數組可以部分操作

這裏我對第三個參數進行詳細解釋:第三個參數可以是一個函數,如果該函數返回爲真,就將操作對象位子不變,否則交換位子(後面有例子)。
我們可以通過調整該函數的內容來控制,當某個條件滿足時返回值的真假。
如:例如一個數組{32,3}這兩個數如何拼接組合達到的數最小,兩種情況323,332。顯然323小。這類問題可以用sort來進行操作。
代碼如下:
int arr[3]={3,32,321} // 組成最小數是321323
sort(arr,arr+3,cmp). //對數組三個位子進行操作,條件是cmp函數,一般是bool類型函數

static bool cmp(int a, int b)
{
string A = to_string(a)+to_string(b);
string B =to_string(b)+to_string(a);
return A<B;
}
//函數的意思是sort函數操作的對象數組中兩個挨着的順序的元素,分別賦值到a和b上。
通過一系列操作,滿足條件某個條件(題中條件是A<B),返回如果真,兩個數順序不變,如果返回假,兩個元素交換位子。
詳細解讀上面得到最小值321323的過程:
從sort函數開始,將數組前兩個值3,32丟入cmp中。即a=3,b=32.先將a和b轉換成字符串
再拼接故A=“332” B=“323” 故return A<B這個條件是假值,sort函數將這兩個值的位子交換。此時arr={32,3,321}.
接下來類似操作依次得到結果是:
arr={32,321,3}
arr={321,32,3}。
將上述數組遍歷輸出及得到結果。(到這裏sort內容講完)

☟☟☟

(27)面試題45. 把數組排成最小的數

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。

示例 1:
輸入: [10,2]
輸出: "102"

示例 2:
輸入: [3,30,34,5,9]
輸出: "3033459"

思路:sort(第三個參數排序)

class Solution {
public:
    string minNumber(vector<int>& nums) {
        int len=nums.size();
        if(len==0) return "";
        
        string res;
        sort(nums.begin(), nums.end(), cmp);
        for(int i=0; i<len; i++){
            res += to_string(nums[i]);  //所以這裏還得to_string
        }
        return res;
    }
    //是的,必須 static
    static bool cmp(int a, int b){//只是交換了原數組中的位置,並沒有把它們變成string
        string A=to_string(a)+to_string(b);
        string B=to_string(b)+to_string(a);
        return A<B;
    }
};

☟☟☟

(28)劍指11.旋轉數組的最小數字(189. Rotate Array)【該題歸類到了查找與排序(二分)】

把一個數組最開始的若干個元素搬到數組的末尾,我們稱之爲數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如,數組 [3,4,5,1,2] 爲 [1,2,3,4,5] 的一個旋轉,該數組的最小值爲1。

示例 1:

輸入:[3,4,5,1,2]
輸出:1
示例 2:

輸入:[2,2,2,0,1]
輸出:0

思路:二分查找

該題減治的思想是:通過排除不可能是最小值元素,進而縮小範圍。
當我們拿中間的數和最右邊的數相比時,有三種情況:
1. 中間的數比右邊的大,那麼中間數不可能是最小的數,
   最小的數只可能出現在中間數的後面,改left = mid + 1縮小區間
3. 中間的數和右邊的小,那麼右邊的數不可能是中位數,
   此時,中間的數可能是最小的數,改right = mid 縮小區間
5. 中間的數和右邊相等,例如[3,3,3,1,3]此時中間的數和最右邊的數都爲3,
   可以知道的是,此時我們可以排除最右邊的數,改區間爲right = right - 1

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int i=0, j=numbers.size()-1;
        while(i < j){ //當i=j時,跳出循環,返回i所指元素
            
            int m=i + (j - i) / 2;//int m=(i+j)/2;
            if(numbers[m]>numbers[j]) i=m+1;
            else if(numbers[m] < numbers[j]) j=m;
            else j--;
        }
        return numbers[i];
    }
};

while (i < j) 循環裏應該是<=吧。請問爲什麼是<
在這裏插入圖片描述
☟☟☟

(29)面試題51. 數組中的逆序對【困難】

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數。

示例 1:

輸入: [7,5,6,4]
輸出: 5

思路:歸併排序

貌似是劍指最難的一道,看了半天放棄了。這裏先貼一個可通過的代碼。複習了排序算法再返回來看。


class Solution {
public:
    int InversePairs(vector<int> data) {
       int length=data.size();
        if(length<=0)
            return 0;
       //vector<int> copy=new vector<int>[length];
       vector<int> copy;
       for(int i=0;i<length;i++)
           copy.push_back(data[i]);
       long long count=InversePairsCore(data,copy,0,length-1);
       //delete[]copy;
       return count%1000000007;
    }
    long long InversePairsCore(vector<int> &data,vector<int> &copy,int start,int end)
    {
       if(start==end)
          {
            copy[start]=data[start];
            return 0;
          }
       int length=(end-start)/2;
       long long left=InversePairsCore(copy,data,start,start+length);
       long long right=InversePairsCore(copy,data,start+length+1,end); 
        
       int i=start+length;
       int j=end;
       int indexcopy=end;
       long long count=0;
       while(i>=start&&j>=start+length+1)
          {
             if(data[i]>data[j])
                {
                  copy[indexcopy--]=data[i--];
                  count=count+j-start-length;          //count=count+j-(start+length+1)+1;
                }
             else
                {
                  copy[indexcopy--]=data[j--];
                }          
          }
       for(;i>=start;i--)
           copy[indexcopy--]=data[i];
       for(;j>=start+length+1;j--)
           copy[indexcopy--]=data[j];       
       return left+right+count;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章