程序員進(jìn)階之算法練習(xí)(三十五)LeetCode專場

前言

LeetCode上的題目是大公司面試常見的算法題揩慕,今天的目標(biāo)是拿下5道算法題:
題目1是基于鏈表的大數(shù)加法妓局,既考察基本數(shù)據(jù)結(jié)構(gòu)的了解冬阳,又考察在處理加法過程中的邊界處理丑掺;
題目2是求數(shù)組出現(xiàn)頻率前k大的數(shù)字猪钮,考察思維能力品山,代碼很短;
題目3是給出從兩個數(shù)組中選擇數(shù)字躬贡,組成一個最大的數(shù)字谆奥,考察的是貪心的思想;
前三個都偏向于考察想法拂玻,實(shí)現(xiàn)的代碼都比較簡單酸些;
題目4、5是數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)題檐蚜,也是大部分人比較頭疼的題目魄懂,因?yàn)樾枰^多的數(shù)據(jù)結(jié)構(gòu)和STL實(shí)現(xiàn),并且還有時間和空間的限制闯第。

正文

1市栗、Add Two Numbers II

題目鏈接
題目大意

給倆個鏈表,節(jié)點(diǎn)由0~9的數(shù)字組成咳短,分別表示兩個數(shù)字填帽;
求出兩個數(shù)字的和,以鏈表的形式返回咙好。

例如
Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)

7243 + 564 =7807

Output: 7 -> 8 -> 0 -> 7

題目解析:
題目的意思很明顯篡腌,就是把兩個數(shù)字加起來,需要考慮進(jìn)位的情況勾效。
因?yàn)槭菃蜗虻逆湵磬诘浚闅v后很難回溯叛甫,所以先把數(shù)字存到vec中。
并且為了處理方便杨伙,vec的最低位存在vec的起始部分其监。
于是從0開始遍歷兩個vec即可,注意考慮最后進(jìn)位的情況限匣。

復(fù)雜度解析:
時間復(fù)雜度是O(N)
空間復(fù)雜度是O(N)


struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode *ret = NULL;
        vector<int> vec1, vec2;
        sum(l1, vec1);
        sum(l2, vec2);
        int n = vec1.size(), m = vec2.size(), flag = 0;
        for (int i = 0; i < n || i < m; ++i) {
            int x = 0, y = 0;
            if (i < n) {
                x = vec1[i];
            }
            if (i < m) {
                y = vec2[i];
            }
            int s = x + y + flag;
            if (s > 9) {
                s -= 10;
                flag = 1;
            }
            else {
                flag = 0;
            }
            ListNode *tmp = new ListNode(s);
            tmp->next = ret;
            ret = tmp;
        }
        if (flag) {
            ListNode *tmp = new ListNode(1);
            tmp->next = ret;
            ret = tmp;
        }
        return ret;
    }
    
    void sum(ListNode* list, vector<int> &vec) {
        if (list->next) {
            sum(list->next, vec);
        }
        vec.push_back(list->val);
    }
};

2.Top K Frequent Elements

題目鏈接
題目大意

給出一個數(shù)組和一個數(shù)字k抖苦,返回按數(shù)字出現(xiàn)頻率的前k個的數(shù)字;
1 <= k <= n, n是數(shù)組大刑鸥睛约;

 example,
 Given [1,1,1,2,2,3] and k = 2, return [1,2].

題目解析:

題目分為兩個步驟:
1、統(tǒng)計每個數(shù)字的出現(xiàn)次數(shù)哲身;
2辩涝、從中選擇k個次數(shù)最多的數(shù)字;

一個簡單的做法:
用哈希表統(tǒng)計每個數(shù)字的出現(xiàn)次數(shù)勘天;
把每個數(shù)字的出現(xiàn)次數(shù)和數(shù)字組成一個pair怔揩,放入優(yōu)先隊(duì)列;

這樣從優(yōu)先隊(duì)列中取出k個即可脯丝。

復(fù)雜度解析:
時間復(fù)雜度是O(NlogN)商膊,主要在最后的優(yōu)先隊(duì)列。

其他解法:
有一個O(NlogK)的優(yōu)化宠进;
首先把隊(duì)列變成最小有限隊(duì)列晕拆,
每次pair放入優(yōu)先對時,如果當(dāng)前的size大于k材蹬,那么彈出top实幕;
這樣每次的操作從O(logN)變成O(logK)。


class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> numsHash;
        for (int i = 0; i < nums.size(); ++i) {
            ++numsHash[nums[i]];
        }
        priority_queue<pair<int, int>> q;
        for (int i = 0; i < nums.size(); ++i) {
            if(numsHash[nums[i]]) {
                q.push(make_pair(numsHash[nums[i]], nums[i]));
                numsHash[nums[i]] = 0;
            }
        }
        vector<int> ret;
        for (int i = 0; i < k; ++i) {
            ret.push_back(q.top().second);
            q.pop();
        }
        return ret;
    }
}leetcode;

3堤器、create-maximum-number

題目鏈接
題目大意
給出兩個數(shù)組昆庇,數(shù)組只包括0~9十個數(shù)字,長度分別為n闸溃、m整吆;
從兩個數(shù)組中選出k個數(shù),組成一個長度為k的數(shù)字辉川,要求:
1表蝙、從數(shù)組n、m選擇出來的數(shù)字相對位置不變乓旗;
2府蛇、最后的數(shù)字最大;
輸出最后的數(shù)字寸齐。

 Example 1:
 nums1 = [3, 4, 6, 5]
 nums2 = [9, 1, 2, 5, 8, 3]
 k = 5
 return [9, 8, 6, 5, 3]
 
 Example 2:
 nums1 = [6, 7]
 nums2 = [6, 0, 4]
 k = 5
 return [6, 7, 6, 0, 4]

題目解析:

要求最后數(shù)字最大欲诺,那么盡可能把數(shù)字大的排在前面;
在都合法的前提下渺鹦,99* 肯定比 98*要大扰法;
那么可以按照這樣的貪心策略:
先枚舉t,t表示從數(shù)組nums1中選出t個數(shù)字毅厚,那么數(shù)組nums2中應(yīng)該選出k-t個數(shù)字塞颁;
兩個數(shù)組的所有數(shù)字組成最大的數(shù)字,因?yàn)閮蓚€數(shù)組間的數(shù)字是可以任意順序吸耿,那么只需每次選擇較大的放在前面即可祠锣。

問題簡化成,O(N)每次從數(shù)組中選出t個最大的數(shù)字咽安;
這個可以用貪心解決:
假設(shè)數(shù)組當(dāng)前枚舉到第i個伴网,且nums[i]=x;
從左到右遍歷已經(jīng)選擇的數(shù),當(dāng)遇到一個數(shù)字t妆棒,t<x時澡腾,判斷插入x后,后續(xù)是否存在合法解糕珊;如果存在則替換动分,否則直到最后,插入尾部红选;


class Solution {
public:
    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int n = (int)nums1.size(), m = (int)nums2.size();
        vector<int> ret(k, 0);
        for (int i = max(0, k - m); i <= k && i <= n; ++i) {
            vector<int> tmp1 = maxArray(nums1, i);
            vector<int> tmp2 = maxArray(nums2, k - i);
            vector<int> tmp = merge(tmp1, tmp2, k);
            if (greater(tmp, 0, ret, 0)) {
                ret = tmp;
            }
        }
        return ret;
    }
    
    vector<int> maxArray(vector<int> &nums, int k) {
        int n = (int)nums.size();
        vector<int> ret(k, 0);
        for (int i = 0, j = 0; i < n; ++i) {
            while (n - i + j > k && j > 0 && ret[j - 1] < nums[i]) {
                --j;
            }
            if (j < k) {
                ret[j++] = nums[i];
            }
        }
        return ret;
    }
    
    vector<int> merge(vector<int>& nums1, vector<int>& nums2, int k) {
        vector<int> ret(k, 0);
        for (int i = 0, j = 0, r = 0; r < k; ++r) {
            ret[r] = greater(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
        }
        return ret;
    }
    
    bool greater(vector<int> &nums1, int i, vector<int> &nums2, int j) {
        while (i < nums1.size() && j < nums2.size() && nums1[i] == nums2[j]) {
            ++i;
            ++j;
        }
        return j == nums2.size() || (i < nums1.size() && nums1[i] > nums2[j]);
    }
};

4澜公、 Insert Delete GetRandom O(1) - Duplicates allowed

題目鏈接
題目大意
實(shí)現(xiàn)一個數(shù)據(jù)結(jié)構(gòu),包括以下三個方法:
1喇肋、insert(val): 插入一個數(shù)字坟乾;
2、remove(val): 移除一個數(shù)字苟蹈;
3糊渊、getRandom: O(1)隨機(jī)返回一個數(shù)字;


 Example
 插入數(shù)字1慧脱;
 collection.insert(1);
 插入數(shù)字1:
 collection.insert(1);
 插入數(shù)字2
 collection.insert(2);
 隨機(jī)返回數(shù)字渺绒,要求 2/3可能返回1, 1/3可能返回2菱鸥;
 collection.getRandom();

題目解析:

插入和移除數(shù)字不麻煩宗兼,考慮如何在O(1)時間返回一個數(shù)字。
容易知道氮采,放在數(shù)組里面可以殷绍,然后隨機(jī)返回一個位置可以實(shí)現(xiàn)。
增加可以在數(shù)組最末端增加鹊漠;
刪除數(shù)組中間某個數(shù)字時主到,可以把最末端的數(shù)字放到刪除的位置上茶行;

現(xiàn)在的問題是,如何快速找到數(shù)組中該刪除的某個位置登钥;
考慮用hash來實(shí)現(xiàn)畔师。
數(shù)組就是vector<pair<int, int> >; first存val,second存出現(xiàn)次數(shù)牧牢;
再用一個哈希map看锉,unordered_map<int, vector<int>> 里面存對應(yīng)數(shù)字出現(xiàn)的位置;


class RandomizedCollection {
public:
    /** Initialize your data structure here. */
    RandomizedCollection() {
        
    }
    
    /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
    bool insert(int val) {
        bool ret = hashMap.find(val) == hashMap.end();
        hashMap[val].push_back(randVec.size());
        randVec.push_back(make_pair(val, hashMap[val].size() - 1));
        return ret;
    }
    
    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    bool remove(int val) {
        bool ret = hashMap.find(val) != hashMap.end();
        if (ret) {
            auto last = randVec.back();
            hashMap[last.first][last.second] = hashMap[val].back();
            randVec[hashMap[val].back()] = last;
            hashMap[val].pop_back();
            if (hashMap[val].empty()) {
                hashMap.erase(val);
            }
            randVec.pop_back();
        }
        return ret;
    }
    
    /** Get a random element from the collection. */
    int getRandom() {
        return randVec[rand() % randVec.size()].first;
    }
    
private:
    unordered_map<int, vector<int>> hashMap;
    vector<pair<int, int>> randVec;
}leetcode;

5塔鳍、 All O`one Data Structure

題目鏈接
題目大意

實(shí)現(xiàn)一個數(shù)據(jù)結(jié)構(gòu)伯铣,要求:
1、Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string.
2轮纫、Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string.
3腔寡、GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "".
4、GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "".

要求所有的數(shù)據(jù)結(jié)構(gòu)的時間復(fù)雜度是O(1);

題目解析:

在不考慮復(fù)雜度的前提下掌唾,樸素做法是遍歷蹬蚁,O(N);
簡單的優(yōu)化,用map來維護(hù)優(yōu)先隊(duì)列郑兴,操作1犀斋、2先獲取key值,更新完重新插入情连;操作3叽粹、4直接拿隊(duì)列top;每個操作的復(fù)雜度是O(LogN);

題目要求是O(1)却舀,那么必然不能使用樹類型的結(jié)構(gòu)虫几,應(yīng)該利用題目特性,配合hash以及貪心來實(shí)現(xiàn)挽拔。

假設(shè)有一個key-hash表辆脸,來存key的對應(yīng)值。
操作1螃诅、先看keyHash里面是否有key啡氢,有則+1,無則插入术裸;
操作2倘是、先看keyHash里面是否有key,有則-1袭艺,無則Nothing搀崭;

為了維護(hù)最值,引入鏈表list猾编,里面所有的元素是從小到大瘤睹;每個元素是一個桶升敲,桶里放著值相同的key;
操作3轰传、直接獲取list頭元素的值冻晤;
操作4、直接獲取list尾元素的值绸吸;

同時,操作1设江、2在操作的過程中锦茁,需要把當(dāng)前key值從list對應(yīng)的桶里移除蕴茴,放到上一個或者下一個桶里银受,或者丟棄习寸。
為了實(shí)現(xiàn)O(1)獲取key所在位置莲镣,可以用iter-hash來維護(hù)key所對應(yīng)元素的迭代器蚁鳖。


struct Bucket {
    int value;
    unordered_set<string> keys;
};

class AllOne {
public:
    list<Bucket> buckets;
    unordered_map<string, list<Bucket>::iterator> bucketOfKey;
    /** Initialize your data structure here. */
    AllOne() {
        
    }
    /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */
    void inc(string key) {
        if (bucketOfKey.find(key) == bucketOfKey.end()) {
            bucketOfKey[key] = buckets.insert(buckets.begin(), {0, {key}});
        }
        auto next = bucketOfKey[key], bucket = next++;
        if (next == buckets.end() || next->value > bucket->value + 1) {
            next = buckets.insert(next, {bucket->value+1, {}});
        }
        next->keys.insert(key);
        bucketOfKey[key] = next;
        
        bucket->keys.erase(key);
        if (bucket->keys.empty()) {
            buckets.erase(bucket);
        }
    }
    
    
    /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
    void dec(string key) {
        if (bucketOfKey.find(key) == bucketOfKey.end()) {
            return ;
        }
        auto pre = bucketOfKey[key], bucket = pre;
        if (pre != buckets.begin()) {
            --pre;
        }
        
        bucketOfKey.erase(key);
        if (bucket->value > 1) {
            if (bucket == buckets.begin() || pre->value < bucket->value - 1) {
                pre = buckets.insert(bucket, {bucket->value - 1, {}});
            }
            pre->keys.insert(key);
            bucketOfKey[key] = pre;
        }
        
        bucket->keys.erase(key);
        if (bucket->keys.empty()) {
            buckets.erase(bucket);
        }
    }
    
    /** Returns one of the keys with maximal value. */
    string getMaxKey() {
        return buckets.empty() ? "" : *(buckets.rbegin()->keys.begin());
    }
    
    /** Returns one of the keys with Minimal value. */
    string getMinKey() {
        return buckets.empty() ? "" : *(buckets.begin()->keys.begin());
    }
}leetcode;

總結(jié)

算法重在勤思多練藻雌,也基本都是面試前才會花很多時間去練習(xí)栅贴。
最近在忙新項(xiàng)目著瓶,積累了很多新的感觸瞳秽,但是還沒時間去整理出來瓣履,只能先更新算法練習(xí)。
每天中午飯后一道m(xù)edium练俐,能堅持一年也會有上百道題袖迎。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腺晾,隨后出現(xiàn)的幾起案子燕锥,更是在濱河造成了極大的恐慌,老刑警劉巖悯蝉,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件归形,死亡現(xiàn)場離奇詭異,居然都是意外死亡鼻由,警方通過查閱死者的電腦和手機(jī)暇榴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蕉世,“玉大人跺撼,你說我怎么就攤上這事√直耍” “怎么了歉井?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哈误。 經(jīng)常有香客問我哩至,道長躏嚎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任菩貌,我火速辦了婚禮卢佣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘箭阶。我一直安慰自己虚茶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布仇参。 她就那樣靜靜地躺著嘹叫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诈乒。 梳的紋絲不亂的頭發(fā)上罩扇,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音怕磨,去河邊找鬼喂饥。 笑死,一個胖子當(dāng)著我的面吹牛肠鲫,可吹牛的內(nèi)容都是我干的员帮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼导饲,長吁一口氣:“原來是場噩夢啊……” “哼集侯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起帜消,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤棠枉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泡挺,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辈讶,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年娄猫,在試婚紗的時候發(fā)現(xiàn)自己被綠了贱除。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡媳溺,死狀恐怖月幌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情悬蔽,我是刑警寧澤扯躺,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響录语,放射性物質(zhì)發(fā)生泄漏倍啥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一澎埠、第九天 我趴在偏房一處隱蔽的房頂上張望虽缕。 院中可真熱鬧,春花似錦蒲稳、人聲如沸氮趋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剩胁。三九已至,卻和暖如春决记,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背倍踪。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工系宫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人建车。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓扩借,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缤至。 傳聞我的和親對象是個殘疾皇子潮罪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前言 BAT常見的算法面試題解析:程序員算法基礎(chǔ)——動態(tài)規(guī)劃程序員算法基礎(chǔ)——貪心算法工作閑暇也會有在線分享,算法...
    落影l(fā)oyinglin閱讀 1,097評論 0 7
  • 高先生雖然穿衣打扮比較直男 但聲音還是蠻好聽的 不然也不會被從小夸到大 還被選進(jìn)了辯論隊(duì)领斥,打了說話最多的辯位 所以...
    某高閱讀 231評論 2 1
  • 1. Rest 1.1 序列化json自動命名: json中如果有下劃線命名的key,例如 user_name...
    Juude閱讀 458評論 1 0
  • 描述 人民幣和美元是世界上通用的兩種貨幣之一嫉到,寫一個程序進(jìn)行貨幣間幣值轉(zhuǎn)換,其中: 人民幣和美元間匯率固定為:1美...
    一抹靈閱讀 1,690評論 0 0
  • 聯(lián)網(wǎng)的通信安全月洛,建立在SSL/TLS協(xié)議之上何恶。 本文簡要介紹SSL/TLS協(xié)議的運(yùn)行機(jī)制。文章的重點(diǎn)是設(shè)計思想和運(yùn)...
    殺破魂閱讀 639評論 0 3