Codility and other programming lessons: Lesson 8: Peaks (Peaks)


Codility and other programming lessons: Lesson 8: Peaks (Peaks)
A non-empty zero-indexed array A consisting of N integers is given.
peak is an array element which is larger than its neighbors. More precisely, it is an index P such that 0 < P < N − 1,  A[P − 1] < A[P] and A[P] > A[P + 1].
For example, the following array A:
    A[0] = 1
    A[1] = 2
    A[2] = 3
    A[3] = 4
    A[4] = 3
    A[5] = 4
    A[6] = 1
    A[7] = 2
    A[8] = 3
    A[9] = 4
    A[10] = 6
    A[11] = 2
has exactly three peaks: 3, 5, 10.
We want to divide this array into blocks containing the same number of elements. More precisely, we want to choose a number K that will yield the following blocks:
  • A[0], A[1], ..., A[K − 1],
  • A[K], A[K + 1], ..., A[2K − 1],
    ...
  • A[N − K], A[N − K + 1], ..., A[N − 1].
What's more, every block should contain at least one peak. Notice that extreme elements of the blocks (for example A[K − 1] or A[K]) can also be peaks, but only if they have both neighbors (including one in an adjacent blocks).
The goal is to find the maximum number of blocks into which the array A can be divided.
Array A can be divided into blocks as follows:
  • one block (1, 2, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2). This block contains three peaks.
  • two blocks (1, 2, 3, 4, 3, 4) and (1, 2, 3, 4, 6, 2). Every block has a peak.
  • three blocks (1, 2, 3, 4), (3, 4, 1, 2), (3, 4, 6, 2). Every block has a peak. Notice in particular that the first block (1, 2, 3, 4) has a peak at A[3], because A[2] < A[3] > A[4], even though A[4] is in the adjacent block.
However, array A cannot be divided into four blocks, (1, 2, 3), (4, 3, 4), (1, 2, 3) and (4, 6, 2), because the (1, 2, 3) blocks do not contain a peak. Notice in particular that the (4, 3, 4) block contains two peaks: A[3] and A[5].
The maximum number of blocks that array A can be divided into is three.
Write a function:
int solution(int A[], int N);
that, given a non-empty zero-indexed array A consisting of N integers, returns the maximum number of blocks into which A can be divided.
If A cannot be divided into some number of blocks, the function should return 0.
For example, given:
    A[0] = 1
    A[1] = 2
    A[2] = 3
    A[3] = 4
    A[4] = 3
    A[5] = 4
    A[6] = 1
    A[7] = 2
    A[8] = 3
    A[9] = 4
    A[10] = 6
    A[11] = 2
the function should return 3, as explained above.
First, we find the peeks in the array A and store their index to another array.

If no peeks are found, we return 0. If there is any peek, we start checking from the possible maximum number of blocks=the number of the peeks (each block must contains at least one peek).

int solution(int A[], int N)
{
    //we need at least 3 elements to have a peek.
    if (N < 3){
        return 0;
    }

    //we will never have the number of peeks larger than N / 2.
    int *peeks = (int*)alloca(sizeof(int) * (N / 2));
    int peek_cnt = 0;
   
    //find peeks
    int i;
    for (i = 1; i < N - 1; i++){
        if (A[i - 1] < A[i] && A[i] > A[i + 1]){
            peeks[peek_cnt++] = i;
        }
    }
   
    //if there is no peek, return 0
    if (peek_cnt == 0){
        return 0;
    }
   
    //let's check from the block of the possible maxim number of blocks.

    //as we have at least one peek, we can say the minimum number of blocks
    //that meets the given condition is 1.
    int maxdiv = 1;
 
    for (i = peek_cnt; i > 0; i--){
        if (N % i != 0){
            continue;
        }
       
        //let's check if this number satisfies the conditon.
        int elems_for_each_block    = N / i;
        int next_block_end          = elems_for_each_block;

        int pidx = 0; //the index of the peek to check
        int flg  = 1; //assume the number satisfies the condition first.

        while(1){
            //check if this block contains any peek.
            if (pidx >= peek_cnt || next_block_end <= peeks[pidx]){
                //no peeks detected, this is not a right choice.
                flg = 0;
                break;
            }
           
            //skip the peeks within the current block.
            while(pidx < peek_cnt && peeks[pidx] < next_block_end){
                pidx++;
            }

            next_block_end += elems_for_each_block;
            if (next_block_end > N){
                break;
            }
        }
       
        //at least one peek is contained in every block.
        if (flg){
            maxdiv = i;
            break;
        }
    }
    return maxdiv;
}
irst, we need to find all the peaks. Simply iterate through all elements and aggregate all the peaks into P.
Then, we can iterate through the various possible group sizes g[1,N]. We need the least gsuch that g|N (then Ng is maximized) and so that every group of size g has at least one peak. This can be done by simply iterating through all the peaks pP and checking if every has peak. 
  public int solution(int[] A) {
    int N = A.length;

    // Find all the peaks
    ArrayList<Integer> peaks = new ArrayList<Integer>();
    for(int i = 1; i < N-1; i++){
      if(A[i] > A[i-1] && A[i] > A[i+1]) peaks.add(i);
    }

    for(int size = 1; size <= N; size++){
      if(N % size != 0) continue; // skip if non-divisible
      int find = 0;
      int groups = N/size;
      boolean ok = true;
      // Find whether every group has a peak
      for(int peakIdx : peaks){
        if(peakIdx/size > find){
          ok = false;
          break;
        }
        if(peakIdx/size == find) find++;
      }
      if(find != groups) ok = false;
      if(ok) return groups;
    }
    return 0;
  }
I googled and found some people implemented a more efficient algorithm using the prefix sum. Instead of keeping the index of the peeks, they prepare another array that contains the prefix sum of the number of peeks found so far in the array A at the same index.

This strategy facilitate to check if the block size that is currently being examined satisfies the given condition or not.
int solution(int A[], int N)
{
    //for N < 3, there is no peek.
    if (N < 3){
        return 0;
    }

    //make the prefix sum of the number of the peeks at the index.
    int* peek_cnt_prefix_sum = (int*)alloca(sizeof(int) * N);
    
    peek_cnt_prefix_sum[0] = 0;
    int i;
    for (i = 1; i < N - 1; i++){
        peek_cnt_prefix_sum[i] = peek_cnt_prefix_sum[i - 1];
        if (A[i - 1] < A[i] && A[i] > A[i + 1]){
            peek_cnt_prefix_sum[i]++;
        }
    }
    
    peek_cnt_prefix_sum[N - 1] = peek_cnt_prefix_sum[N - 2];

    //no peek found.
    if (peek_cnt_prefix_sum[N - 1]  == 0){
        return 0;
    }
    
    int maxdiv = 1;
    for (i = peek_cnt; i > 0; i--){
        if (N % i != 0){
            continue;
        }
        
        int elems_for_each_block = N / i;
        
        //keep on checking
        int flg = 1; //assume this divisor of N satisfies the condition.

        int next_block_end = elems_for_each_block - 1;
        int current = peek_cnt_prefix_sum[0];
        while(next_block_end < N){  
            
            int next = peek_cnt_prefix_sum[next_block_end];
            //no peeks found between current and next.
            if (current >= next){
                flg = 0;
                break;
            }
            current = next;
            next_block_end += elems_for_each_block;
        }
        
        //if this divisor of N satisfied the condition,
        //it is the answer.
        if (flg){
            maxdiv = i;
            break;
        }
    }
    
    return maxdiv;
}
分析: 个人感觉时间复杂度很诡异……,首先如果我们用O(n)得空间统计从开始到目前为止得山峰数sum[],那么我们如何检查某个k是不是一个可行解?
          (a) 首先 n % k == 0
          (b) sum[k - 1], sum[2 * k - 1], sum[3 * k - 1],....sum[n - 1] 严格递增,注意这个数列有n / k项。
所以我们循环k有n次真正判断额外的循环次数是n / k。 于是总的时间复杂度是n + (n的所有约数和)。 
从wikipedia看到

约束和的复杂度是O(nloglog(n)))的,所以时间复杂度就是这样了。

其实只要稍加改进时间复杂度可以达到O(n)。
考虑距离最远的连个山峰,设最大距离为D,(第一个山峰到开头的距离,以及最后一个山峰到末尾的距离都算进去),则如果桶的size >= D,则一定可以。size < D / 2一定不可以。所以我们只要看D/2..D之间的n的约数即可。如果这里没有解,再沿着D向上找一个约数(但是不用再验证)。之所以把第一个山峰和最后一个山峰那块都算进去是因为可以保证第一个桶和最后一个桶不空。然后可以轻松得出上述结论。
复杂度分析,令m = D / 2。则 复杂度只有n / m + n / (m + 1) + ... + n / D,每一项不大于n / m,一共m项,所以时间复杂度居然是O(n)。
int solution(vector<int> &A) {
    // write your code in C++98
    // 2 * size - 1 >= D
    // size < D
    int n = A.size();
    if (n <= 2) {
        return 0;
    }
    vector<int> sum;
    sum.resize(n);
    int last = -1, D = 0;
    sum[0] = 0;
    for (int i = 1; i + 1 < n; ++i) {
        sum[i] = sum[i - 1];
        if ((A[i] > A[i - 1]) && (A[i] > A[i + 1])) {
            D = max(D, i - last);
            last = i;
            ++sum[i];
        }
        
    }
    if ((sum[n - 1] = sum[n - 2]) == 0) {
        return 0;
    }
    D = max(D, n - last);
    for (int i = (D >> 1) + 1; i < D; ++i) {
        if (n % i == 0) {
            last = 0;
            int j;
            for (j = i; j <= n; j += i) {
                if (sum[j - 1] <= last) {
                    break;
                }
                last = sum[j - 1];
            }
            if (j > n) {
                return n / i;
            }
        }
    }
    for (last = D; n % last; ++last)
    ;
    return n / last;
            
}
http://www.cnblogs.com/lautsie/p/4110313.html
https://github.com/yiqin/Codility-Practice/blob/master/Peaks.java
Read full article from Codility and other programming lessons: Lesson 8: Peaks (Peaks)

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts