顯示具有 Interview 標籤的文章。 顯示所有文章
顯示具有 Interview 標籤的文章。 顯示所有文章

2022年1月3日 星期一

Topological Ordering 介紹

Directed Acyclic Graph (DAG) 介紹

不同於 Tree 的無方向、無環,DAG 則是有方向無環。DAG 特性是不斷地前進,有時分流、有時合流,日常常見的 DAG 為族譜、水流以及課程擋修規則

實作

  • Topological Ordering
    拿課程擋修規則為例,有必須先修的課程及後修的課程,將其完整排列出來視為 Topological Ordering。
        // 課程 1 須先修 : None
        // 課程 2 須先修 : 課程 1
        // 課程 3 須先修 : 課程 1, 課程 2
        // 課程 4 須先修 : None
        // 
        // Gragh:
        // 課程 1 -> 課程 2
        //        ↘   ↓
        // 課程 4    課程 3
        //
        // Topological Ordering:
        //  1) 1 2 3 4
        //  2) 4 1 2 3
        //  3) 1 4 2 3
        //  4) 1 2 4 3
        // 兩個都行,只要順序符合規則就好 (按這個例子,課程 4 的排序位置無關緊要)
    
  • Topological Sort ( Kahn's Algorithm  )
    排序時,首先要找到第一個點 (課程 1 或課程 4)。第一個點有一個特別之處,就是沒有人指向他。找出來該第一點後,把其指向的連接也跟著去掉。使下一次課程 2 變為第一個點。以此排序。
        // Gragh:
        //         課程 2
        //           ↓
        // 課程 4   課程 3
    
  • Code
    題目之範例解答
        bool canFinish (int numCourses, vector<vector<int>>& prerequisites) {
    
            // Kahn's Algorithm
            // http://web.ntnu.edu.tw/~algo/DirectedAcyclicGraph.html
    
            // adj 紀錄 node 連出去的所有 nodes
            // ref 紀錄 node 被連的次數
            vector<vector<int>> adj(numCourses, vector<int>());
            vector<int> ref(numCourses, 0);
    
            // init
            for (auto&v : prerequisites) {
                ++ref[v[0]];
                adj[v[1]].push_back(v[0]);
            }
    
            // Every loop 都可以找到一個 first node, 需要找出 numCourses 個
            for (int i = 0; i < numCourses; ++i) {
    
                // 一定有 node 的被連的次數為 0, 是為 first node
                // 若所有 node 被連次數都 > 0, 代表有 cycle
                int head = 0;
                while (head < numCourses && ref[head] != 0) ++head;
                if (head == numCourses) return false;
    
                // 找過的 first node 要刪除, 設為 -1
                // 把這次的 first node 所連出去 nodes 的 被連次數(ref) 減一
                ref[head] = -1;
                for (auto & n : adj[head]) --ref[n];
            }
    
            return true;
        }
    
參考資料 :
1.leetcode-problem-course-schedule
2.http://web.ntnu.edu.tw/~algo/DirectedAcyclicGraph.html

2021年6月18日 星期五

Segment Tree 筆記

Segment-Tree 介紹

主要用來找區間最大值區間總和。由於我是為了這題所以以區間總和做介紹。下面陣列是應對區間總和,每個 node 紀錄的有起始點, 結束點及區間總和,EXAMPLE: [0,4] 代表 index 0 ~ 4 的總和,其總和為 10,[0,0] 代表 index 0~0 也就是 nums[0] 的本身值。至於樹為什麼長這樣跟 Build tree 有關。
    // nums:[-1, 4, 2, 0, 5]
    //
    // Segment Tree for the Above Array:
    //
    //         10                      [0,4]
    //        /  \
    //      5      5           [0,2]          [3,4]
    //     / \    / \
    //    3   2  0   5      [0,1]  [2,2]   [3,3]  [4,4]
    //   / \   
    // -1  4            [0,0]  [1,1]

Segment-Tree 實作

  • Build Tree
    這邊我用 c++ 概略作介紹。基本上是 top-down 的 build 法,時間複雜度為 O(n)。
        // Node 資料結構
        class segTreeNode {
        public:
            int begin, end, sum;
            segTreeNode * left = NULL;
            segTreeNode * right = NULL;
            segTreeNode(int s, int e, int m):begin(s), end(e), sum(m) {}
        };
    
        // Build
        segTreeNode* buildSegTree(int begin, int end, vector& nums) {
            // 若 begin = end,代表是 leaf。
            if (begin == end) {
                auto node = new segTreeNode(begin, begin, nums[begin]);
                return node;
            }  
            
            // 分割
            int mid = (begin + end) / 2;
            auto left  = buildSegTree(begin, mid, nums);
            auto right = buildSegTree(mid+1, end, nums);
            auto node = new segTreeNode(begin, end, left->sum + right->sum);
            node->left = left;
            node->right = right;
            return node;
        }
    
  • Update tree
    時間複雜度為 O(log n)。
        // Update
        void updateSegTree(segTreeNode* node, int index, int val) {
            
            // 若是該 index 的 leaf。則更新其 sum
            if (node->begin == index && node->end == index) {
                node->sum = val;
                return;
            }
            
            // 若是中間 node 則分割往下繼續找
            int mid = (node->begin + node->end) / 2;
            if (index > mid)
                updateSegTree(node->right, index, val);
            else
                updateSegTree(node->left, index, val);
                
            // 最後更新 node's sum
            node->sum = node->left->sum + node->right->sum;
        }
    
  • Sum
    時間複雜度為 O(log n + k)。
        // Sum
        int sumSegTree(segTreeNode* node, int left, int right) {
            // 若完美覆蓋區間,直接回傳。
            if (node->begin == left && node->end == right)
                return node->sum;
            
            int mid = (node->begin + node->end) / 2;
            
            // 要求區間完全在左邊
            if (right <= mid)
                return sumSegTree(node->left, left, right);
            // 要求區間完全在右邊
            else if (left > mid)
                return sumSegTree(node->right, left, right);
            // 兩邊都有
            else
                return sumSegTree(node->left, left, mid) + 
                       sumSegTree(node->right, mid+1, right);
        }
    
參考資料 :
1.leetcode/hg3994 answer
2.youtube 中文講解

2021年2月18日 星期四

Programmer Interview - stack v.s. heap

 stack v.s. heap

  • 與 threads 的互動
    在一個 multi-threaded 的程式中,每個 thread 都各自擁有一個 stack,但共享一個 heap。
  • object 可以儲存在 heap,而非 stack
    在 c++ 可以使用 new,來將 object 實體儲存在 heap。
        void foo () {
            // myClass, myPointer 儲存在 stack
            // myPointer 所指向的 tempClass object 則儲存在 heap
            // function 結束 myClass, myPointer 則會與 stack 一起 remove
            // 而 myPointer 所指向的 tempClass 不會,所以下 delete
            tempClass myClass;
            tempClass *myPointer = new tempClass();
            delete myPointer;
        }
  • Java 或 .NET 可以透過 garbage collection 來作到 delete myPointer; 的效果。
  • stack 跟 heap 的大小
    stack 大小是固定的,有某些語言可以增加其大小。若 stack 不夠則會造成 stackoverflow (ex 無限遞迴)。heap 大小則是靠 OS 給的。
參考資料 :

2021年1月5日 星期二

面試心得 - Houzz

Job

Company :  Houzz
Job :  Back-End Software Engineer
Source :  Recruiter on LinkedIn
Result :  止步二面

Summary

1. 英文程度不佳 :  純英文溝通 Coding 時的想法及實作方法等相關經驗幾乎為零。
2. 資歷不夠 :  有接受履歷但分數肯定不高。
3. Coding :  沒拿出應有的水準。
4. Q&A 發揮趨近於零 :  除了一面,二面QA都跟啞巴一樣

面試流程及其內容

    共三次,三個面試官 ( 一面 1 個,二面 2 個 )。每次的面試官流程都一樣,大略分三部份,簡短自我介紹->Coding->Q&A。時間大概都 1 小時 10 分左右,可能我 Coding 解太久,因為表定都是 1 小時。
  • 一面 ( Coding Q1 )
    自我介紹: 
    簡短的自我介紹
    
    Coding:
    Coding Question: 3 sum (LeetCode -> Problem -> 0015)
    因為當下解不出 O(n^2),所以沒有第二題。
  • 二面 - 1 ( Coding Q2 )
    自我介紹:
    問了為什麼想應徵這份工作。
    
    Coding 第一題: 
    Design a data schema for google questionnaire
    一份 questionnaire 有很多 question
    每一 question 會秀相關 option 供填問卷者回答
    
    Coding 第二題:
    給一個只包含數字的 string,分割成質數回傳。
    example:
    Input: 11373
    Output:
    ["11", "37", "3"]
    ["113" "7", "3"]
    ["113" "73"]
    ...
  • 二面 - 2
    自我介紹:
    問了近期 Coding challenge or breakthrough。
    因為回答 Regular expression,所以後面問了如何實作。
    
    Coding 第一題: 
    Given an array of letters and an array of dictionary.
    Return WORD if there is a pumutation of all letters can be found in given dictionary 
    The time of any operation on dictionary can be ignored.
    Example: 
    Input: ["E", "H", "L", "O", "L"]
    Dictionary: ["Apple", "Banana", "Hello", "Tree", "Zebra"]
    Output:
    Hello
    (整題完全展現出我英文有多爛,一直跟 Interviewer 雞同鴨講)


Popular Posts