程式員進階之演算法練習:LeetCode專場

来源:https://www.cnblogs.com/qcloud1001/archive/2018/11/26/10021759.html
-Advertisement-
Play Games

歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~ 本文由落影發表 前言 LeetCode上的題目是大公司面試常見的演算法題,今天的目標是拿下5道演算法題: 題目1是基於鏈表的大數加法,既考察基本數據結構的瞭解,又考察在處理加法過程中的邊界處理; 題目2是求數組出現頻率前k大的數字,考察思維能力 ...


歡迎大家前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~

本文由落影發表

前言

LeetCode上的題目是大公司面試常見的演算法題,今天的目標是拿下5道演算法題: 題目1是基於鏈表的大數加法,既考察基本數據結構的瞭解,又考察在處理加法過程中的邊界處理; 題目2是求數組出現頻率前k大的數字,考察思維能力,代碼很短; 題目3是給出從兩個數組中選擇數字,組成一個最大的數字,考察的是貪心的思想; 前三個都偏向於考察想法,實現的代碼都比較簡單; 題目4、5是數據結構實現題,也是大部分人比較頭疼的題目,因為需要較多的數據結構和STL實現,並且還有時間和空間的限制。

正文

1、Add Two Numbers II

題目鏈接 題目大意

給倆個鏈表,節點由0~9的數字組成,分別表示兩個數字; 求出兩個數字的和,以鏈表的形式返回。

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

7243 + 564 =7807

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

題目解析: 題目的意思很明顯,就是把兩個數字加起來,需要考慮進位的情況。 因為是單向的鏈表,遍歷後很難回溯,所以先把數字存到vec中。 並且為了處理方便,vec的最低位存在vec的起始部分。 於是從0開始遍歷兩個vec即可,註意考慮最後進位的情況。

複雜度解析: 時間複雜度是O(N) 空間複雜度是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

題目鏈接 題目大意

給出一個數組和一個數字k,返回按數字出現頻率的前k個的數字; 1 <= k <= n, n是數組大小;

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

題目解析:

題目分為兩個步驟: 1、統計每個數字的出現次數; 2、從中選擇k個次數最多的數字;

一個簡單的做法: 用哈希表統計每個數字的出現次數; 把每個數字的出現次數和數字組成一個pair,放入優先隊列;

這樣從優先隊列中取出k個即可。

複雜度解析: 時間複雜度是O(NlogN),主要在最後的優先隊列。

其他解法: 有一個O(NlogK)的優化; 首先把隊列變成最小有限隊列, 每次pair放入優先對時,如果當前的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

題目鏈接 題目大意: 給出兩個數組,數組只包括0~9十個數字,長度分別為n、m; 從兩個數組中選出k個數,組成一個長度為k的數字,要求: 1、從數組n、m選擇出來的數字相對位置不變; 2、最後的數字最大; 輸出最後的數字。

 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]

題目解析:

要求最後數字最大,那麼儘可能把數字大的排在前面; 在都合法的前提下,99* 肯定比 98*要大; 那麼可以按照這樣的貪心策略: 先枚舉t,t表示從數組nums1中選出t個數字,那麼數組nums2中應該選出k-t個數字; 兩個數組的所有數字組成最大的數字,因為兩個數組間的數字是可以任意順序,那麼只需每次選擇較大的放在前面即可。

問題簡化成,O(N)每次從數組中選出t個最大的數字; 這個可以用貪心解決: 假設數組當前枚舉到第i個,且nums[i]=x; 從左到右遍歷已經選擇的數,當遇到一個數字t,t<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

題目鏈接 題目大意: 實現一個數據結構,包括以下三個方法: 1、insert(val): 插入一個數字; 2、remove(val): 移除一個數字; 3、getRandom: O(1)隨機返回一個數字;

 Example
 插入數字1;
 collection.insert(1);
 插入數字1:
 collection.insert(1);
 插入數字2
 collection.insert(2);
 隨機返回數字,要求 2/3可能返回1, 1/3可能返回2;
 collection.getRandom();

題目解析:

插入和移除數字不麻煩,考慮如何在O(1)時間返回一個數字。 容易知道,放在數組裡面可以,然後隨機返回一個位置可以實現。 增加可以在數組最末端增加; 刪除數組中間某個數字時,可以把最末端的數字放到刪除的位置上;

現在的問題是,如何快速找到數組中該刪除的某個位置; 考慮用hash來實現。 數組就是vector<pair<int, int> >; first存val,second存出現次數; 再用一個哈希map,unordered_map<int, vector

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

題目鏈接 題目大意

實現一個數據結構,要求: 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 "".

要求所有的數據結構的時間複雜度是O(1);

題目解析:

在不考慮複雜度的前提下,朴素做法是遍歷,O(N); 簡單的優化,用map來維護優先隊列,操作1、2先獲取key值,更新完重新插入;操作3、4直接拿隊列top;每個操作的複雜度是O(LogN);

題目要求是O(1),那麼必然不能使用樹類型的結構,應該利用題目特性,配合hash以及貪心來實現。

假設有一個key-hash表,來存key的對應值。 操作1、先看keyHash裡面是否有key,有則+1,無則插入; 操作2、先看keyHash裡面是否有key,有則-1,無則Nothing;

為了維護最值,引入鏈表list,裡面所有的元素是從小到大;每個元素是一個桶,桶里放著值相同的key; 操作3、直接獲取list頭元素的值; 操作4、直接獲取list尾元素的值;

同時,操作1、2在操作的過程中,需要把當前key值從list對應的桶里移除,放到上一個或者下一個桶里,或者丟棄。 為了實現O(1)獲取key所在位置,可以用iter-hash來維護key所對應元素的迭代器。

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;

總結

這5個題目如果都能獨立完成,那麼水平已經可以足以應付國內各大企業的演算法面。 演算法重在勤思多練,埋怨公司出演算法題是沒用的,多花時間準備才是正道。

此文已由作者授權騰訊雲+社區發佈,更多原文請點擊

搜索關註公眾號「雲加社區」,第一時間獲取技術乾貨,關註後回覆1024 送你一份技術課程大禮包!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Flutter for iOS 開發者 本文檔適用那些希望將現有 iOS 經驗應用於 Flutter 的開發者。如果你擁有 iOS 開發基礎,那麼你可以使用這篇文檔開始學習 Flutter 的開發。 開發 Flutter 時,你的 iOS 經驗和技能將會大有裨益,因為 Flutter 依賴於移動操作 ...
  • 往後餘生,唯獨有你 簡書作者:達叔小生 90後帥氣小伙,良好的開發習慣;獨立思考的能力;主動並且善於溝通 簡書博客: https://www.jianshu.com/u/c785ece603d1 結語 下麵我將繼續對 其他知識 深入講解 ,有興趣可以繼續關註 小禮物走一走 or 點贊 ...
  • 1.使用自定義字體 (1)將字體文件導入項目 (2)在info.plist文件中添加 Fonts provided by application (3)獲取字體在項目中的名稱 (4)雙擊打開導入的字體,獲取名稱 (5)獲取自定義字體名稱 (6)使用字體 ...
  • JavaBean是使用Java語言開發的一個可重用的組件,在JSP的開發中可以使用JavaBean減少重覆代碼,使整個JSP代碼的開發更簡潔。JSP搭配JavaBean來使用,有以下的優點: 1.可將HTML和Java代碼分離,這主要是為了日後維護的方便。如果把所有的程式代碼(HTML和Java)寫 ...
  • 工具簡介 代碼格式化工具:CSS ...
  • 如果一個網站打開速度超級慢,你會忍受幾秒呢?根據對周圍同事的調查,北京新起點網路小編髮現普通人對網頁打開速度的期望是“秒開”,如果我們的網站打開速度很慢,基本上就會被用戶拋棄了。同類網站那麼多,誰都會想去體驗好的網站。對於一個網站來說,網站的打開速度很重要,而想解決這個問題也比較簡單。造成網站打開速 ...
  • 將類似如下數據轉換成樹形的數據 先附上代碼 數據轉換後是 思路 :將有父子關係的數組數據先分為兩類,一類是沒有父節點的數據(取個別名parents),另一類是有父節點的數據(取個別名children),然後通過遍歷parents,對每一個父節點在children查找對應的子節點,並將其放入父節點的c ...
  • JSP聲明 JSP 聲明用來定義程式中使用的實體,如變數、方法和類。 語法格式:<%! 變數/方法/類的聲明 %> 例如: 註意1:JSP 聲明中定義的變數、方法和類是全局 性的,在 JSP 頁面中的任何地方都能夠使用。 註意2:JSP 聲明中不能使用out.print()系列方法做 輸出操作。 ( ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...