1、假設淘寶一天有5億條成交數據,求出銷量最高的100個商品並給出算法的時間複雜度。
2、有10億個雜亂無章的數,怎樣最快地求出其中前1000大的數。
2、給一列無序數組,求出中位數並給出算法的時間複雜度。
若數組有奇數個元素,中位數是a[(n-1)/2];若數組有偶數個元素,中位數爲a[n/2-1]和a[n/2]兩個數的平均值。這裏爲方便起見,假設數組爲奇數個元素。
思路一:把無序數組排好序,取出中間的元素。時間複雜度取決於排序算法,最快是快速排序,O(nlogn),或者是非比較的基數排序,時間爲O(n),空間爲O(n)。這明顯不是我們想要的。
思路二:採用快速排序的分治partition過程。任意挑一個元素,以該元素爲支點,將數組分成兩部分,左邊是小於等於支點的,右邊是大於支點的。如果左側長度正好是(n-1)/2,那麼支點恰爲中位數。如果左側長度<(n-1)/2, 那麼中位數在右側,反之,中位數在左側。 進入相應的一側繼續尋找中位數。
- //快速排序的分治過程找無序數組的中位數
- int partition(int a[], int low, int high) //快排的一次排序過程
- {
- int q = a[low];
- while (low < high)
- {
- while (low < high && a[high] >= q)
- high--;
- a[low] = a[high];
- while (low < high && a[low] <= q)
- low++;
- a[high] = a[low];
- }
- a[low] = q;
- return low;
- }
- int findMidium(int a[], int n)
- {
- int index = n / 2;
- int left = 0;
- int right = n - 1;
- int q = -1;
- while (index != q)
- {
- q = partition(a, left, right);
- if (q < index)
- left = q + 1;
- else if (q>index)
- right = q - 1;
- }
- return a[index];
- }
思路三:將數組的前(n+1)/2個元素建立一個最小堆。然後,對於下一個元素,和堆頂的元素比較,如果小於等於,丟棄之,如果大於,則用該元素取代堆頂,再調整堆,接着看下一個元素。重複這個步驟,直到數組爲空。當數組都遍歷完了,(堆中元素爲最大的(n+1)/2個元素,)堆頂的元素即是中位數。
- //構建最小堆找無序數組的中位數
- void nswap(int& i, int& j)
- {
- i = i^j;
- j = i^j;
- i = i^j;
- }
- void minHeapify(int a[], int i, int len)
- {
- int temp;
- int least = i;
- int l = i * 2 + 1;
- int r = i * 2 + 2;
- if (l < len && a[l] < a[least])
- least = l;
- if (r < len && a[r] < a[least])
- least = r;
- if (least != i)
- {
- nswap(a[i], a[least]);
- minHeapify(a, least, len);
- }
- }
- void buildMinHeap(int a[], int len)
- {
- for (int i = (len-2) / 2; i >= 0; i--)
- {
- minHeapify(a, i, len);
- }
- }
- int findMidium2(int a[], int n)
- {
- buildMinHeap(a, (n + 1) / 2);
- for (int i = (n + 1) / 2; i < n; i++)
- {
- if (a[i] > a[0])
- {
- nswap(a[i], a[0]);
- minHeapify(a, 0,(n + 1) / 2);
- }
- }
- return a[0];
- }
引申一:
查找N個元素中的第K個小的元素
編程珠璣給出了一個時間複雜度O(N)的解決方案。該方案改編自快速排序。
經過快排的一次劃分,
1)如果左半部份的長度>K-1,那麼這個元素就肯定在左半部份了
2)如果左半部份的長度==K-1,那麼當前劃分元素就是結果了。
3)如果。。。。。。。<K-1,那麼這個元素就肯定在右半部分了。
並且,該方法可以用尾遞歸實現。效率更高。
也可以用來查找N個元素中的前K個小的元素,前K個大的元素。。。。等等。
引申二:
查找N個元素中的第K個小的元素,假設內存受限,僅能容下K/4個元素。
分趟查找,
第一趟,用堆方法查找最小的K/4個小的元素,同時記錄剩下的N-K/4個元素到外部文件。
第二趟,用堆方法從第一趟篩選出的N-K/4個元素中查找K/4個小的元素,同時記錄剩下的N-K/2個元素到外部文件。
。。。
第四趟,用堆方法從第一趟篩選出的N-K/3個元素中查找K/4個小的元素,這是的第K/4小的元素即使所求。
3、輸入一個整型數組,求出子數組和的最大值,並給出算法的時間複雜度。
設b[i]表示a[0...i]的子數組和的最大值,且b[i]一定包含a[i],即:
sum爲子問題的最優解,
1. 包含a[i],即求b[i]的最大值,在計算b[i]時,可以考慮以下兩種情況,因爲a[i]要求一定包含在內,所以
1) 當b[i-1]>0, b[i] = b[i-1]+a[i]
2) 當b[i-1]<=0, b[i] = a[i], 當b[i-1]<=0,這時候以a[i]重新作爲b[i]的起點。
2. 不包含a[i],即a[0]~a[i-1]的最大值(即0~i-1局部問題的最優解),設爲sum
最後比較b[i]和 sum,即,如果b[i] >sum ,即b[i]爲最優解,然後更新sum的值.
在實現時,bMax代表 b[k], sum更新前代表前一步子問題的最優解,更新後代表當前問題的最優解。實現如下:
- //求數組的子數組和的最大值,時間複雜度爲O(n)
- int maxSumArr(int a[], int n,int* start, int* end)
- {
- int s, e;
- int sum = a[0];
- int bMax=a[0];
- *start = *end = 0;
- for (int i = 1; i < n; i++)
- {
- if (bMax > 0) //情況一,子數組包含a[i],且b[i-1]>0(上一次的最優解大於0),b[i] = b[i-1]+a[i]
- {
- bMax += a[i];
- e = i;
- }
- else //情況二,子數組包含a[i],且b[i-1]<=0(上一次的最優解小於0),這時候以a[i]重新作爲b[i]的起點。
- {
- bMax = a[i];
- s = i;
- e = i;
- } //情況三,子數組不包含a[i],即b[i]=sum
- if (bMax > sum) //三種情況相比較,最大值作爲更新後的最優解,存在sum
- {
- sum = bMax;
- *start = s;
- *end = e;
- }
- }
- return sum;
- }
引申:求子數組和的最小值
同理。
- //求數組的子數組和的最小值,時間複雜度爲O(n)
- int minSumArr(int a[], int n, int* start, int* end)
- {
- int s, e;
- int bMin = a[0];
- int sum = a[0];
- *start = *end = 0;
- for (int i = 0; i < n; i++)
- {
- if (bMin < 0) //情況一,子數組包含a[i], 且b[i-1]<0,b[i] = b[i-1]+a[i]
- {
- bMin += a[i];
- e = i;
- }
- else //情況二,子數組包含a[i],且b[i-1] > 0,這時候以a[i]重新作爲b[i]的起點
- {
- bMin = a[i];
- s = e = i;
- } //情況三,子數組不包含a[i],即b[i]=sum
- if (bMin < sum) //三種情況相比較,最小值作爲更新後的最優解,存在sum
- {
- sum = bMin;
- *start = s;
- *end = e;
- }
- }
- return sum;
- }
4、給出10W條人和人之間的朋友關係,求出這些朋友關係中有多少個朋友圈(如A-B、B-C、D-E、E-F,這4對關係中存在兩個朋友圈),並給出算法的時間複雜度。
- //朋友圈-並查集
- int set[10001];
- int find(int x)
- {
- int i, j, r;
- r = x;
- while (set[r] != r) //尋找此集合的代表
- r = set[r];
- i = x;
- while (i != r) //使得r代表的集合中,所有結點直接指向r,即路徑壓縮
- {
- j = set[i];
- set[i] = r;
- i = j;
- }
- return r;
- }
- void merge(int x, int y)
- {
- int t = find(x);
- int h = find(y);
- if (t < h)
- set[h] = t;
- else
- set[t] = h;
- }
- int friends(int n, int m, int (*r)[2]) //n個人,m對好友關係,存放在二維數組r[m][2]中
- {
- int i, count;
- for (i = 1; i <= n; i++)
- set[i] = i;
- for (i = 0; i < m; i++)
- merge(r[i][0], r[i][1]);
- count = 0;
- for (i = 1; i <= n; i++)
- {
- if (set[i] == i)
- count++;
- }
- return count;
- }
5、如圖所示的數字三角形,從頂部出發,在每一結點可以選擇向左走或得向右走,一直走到底層,要求找出一條路徑,使路徑上的值的和最大。給出算法的時間複雜度。
定義狀態爲:dp[i][j]表示,從第i行第j個數字到最後一行的某個數字的權值最大的和。那麼我們最後只需要輸出dp[1][1]就是答案了.
狀態轉移方程爲:dp[i][j] += max( dp[i+1][j+1],dp[i+1][j] );好了, 從第n-1行往上面倒退就好了。
6、有一個很長二進制串,求出除以3的餘數是多少,給出算法的時間複雜度。
======================================================================