1、給定一個N位數,例如12345,從里面去掉k個數字,得到一個N-k位的數,例如去掉2,4,得到135,去掉1,5,得到234。設計算法,求出所有得到的N-k位數里面最小的那一個?
解決方案一:
(1)第一步要確定剩余N-K位的數的最高位:
從個位開始算起,從第N-K位開始向高位比較,求出最小數字,作為高位。
例如,3 1 1 2 3 3 1,K=3時,從7-3=4開始(為2),向上比較發現,1更小,所以高位設置為1,記錄此時位置P1(等於也可以替換,從而取最高位的1).
(2)再確定次高位:
從N-K-1開始向上到P1-1,此時最小數字為1.
依此類推,最后就可以得到最小數1121.
解決方案二:
例如,1 2 3 4 5 6 7, K=3
剩余N-K位,則定義一個N-K的數組R,用來存放所選數字。
(1)初始化R,存最低N-K位 4567
(2)向上遇到1,則3<R最高位4:
此時需要對R進行調整,調整規則如下,如果高位數字小於相鄰低位數字,則用高位數字替換之。
最高位再用當前數字替換。
於是,得到3456.
不斷執行以上過程,就得到了最終結果1234.
對方案一中舉例進行驗證,正確。
解決方案三:
求每位數字元素,依據元素建立最小堆進行處理,若頭為0,設為次高位,以此類推。。。時間復雜度O(nlog(n))。。
2、“找明星”,N個人中,只有一個明星:明星不認識其他所有的人,而其他人都認識明星,這些人中也可能相互認識。你每次只可以問一個人是否認識另一個人這樣的問題,問最少問多少次可以找出明星。
分析:該問題在創新工場(Innovation Works)和葫蘆(Hulu)的面試中都被問到:
N個人中只有一個明星:明星不認識其他所有的人,而其他人都認識明星,不是明星的人可能認識也可能不認識。你每次只可以問一個人是否認識另一個人這樣的問題,問最少問多少次可以找出明星。
方法:從N個人中找兩個人a b,問a是否認識b,若a認識b則a肯定不是明星排除a,若a不認識b則分兩種情況討論,為明星為1,不為明星為0。
1: 0 0
2: 0 1(不可能,a肯定認識b)
3: 1 0
4: 1 1(不可能,只有一個明星)
只有1,3兩種情況,這兩種情況中b肯定不是明星。因此問一個問題我們便可以排除掉一個人,最多經過n-1次便可以找到明星。
擴展:
如果有兩個明星呢?所有的人都認識明星,明星之間互不認識,其它的人可能認識也可能不認識。最少問多少次可以找出明星?
一種顯然的方法是:先通過第一種方法找出一個明星,然后在剩下的n-1個人種找出第2個明星。這樣總共需要問(n-1 + n - 2)=2n-3次問題。
這個問題等價於找未知序列數中的最小數
我們將reg這個函數等價為以下過程:
如果i認識j,記作i大於等於j,同樣j不一定大於等於i,滿足要求
i不認識j記作i<j
對明星k,他不認識所有人,則k是其中最小的數,且滿足其余的人都認識他,也就是其余的人都大於等於k.
這樣問題就被轉換了。
就拿N=5來說
首先有數組S[5]={A,B,C,D,E}這5個變量,里邊存放着隨機數,求是否存在唯一最小數,如果存在位置在S中的哪里。(樓主這里是這個意思,按我的理解題中這個最小數一定是存在且唯一的)
int finds(S,N) { int flag=0;//用於判定是否有明星,即當前最小數另外出現幾次 int temp=0;//存放最小數在S中的位置 for(i=1;i<N;i++) { if(!reg(S[i],S[temp])//如果temp標號的數小於i標號的數 { temp=i; flag=0;//更換懷疑對象(最小數)時,標記清零 } elseif(reg(S[temp],S[i])//如果temp里存放的確實是唯一最小數是不會跑進這里來的 { flag++; ` } } if(flag>0) return -1;//表示沒有明星,例如所有的數都相等 return temp;//返回明星在S中的位置 }
遍歷 1~n 這n個人;
首先取出 1號 和 2號, 如果 1 認識 2, 那么把 1 去掉;--如果1不認識2,就可以把2去掉了。
如果 2 認識 1, 那么把 2 去掉;
如果 1 和 2 都互相不認識,把他們都去掉;
如果有剩下,在拿剩下的和3號進行比較;
如果沒有剩下的,則拿出3號和4號進行比較;
如此循環;
最后若剩下最后1個人,再遍歷一遍看是否剩下的n-1個人都認識它
時間復雜度分析:
每對之間的比較最多比較2次, 每對之間的比較至少淘汰1個人,要淘汰n-1個人最多比較 2*(n-1)次;
所以時間復雜度是 O(n)的。。
3、兩個有序鏈表的合並。看過這個題,考慮下邊界問題,可以用O(n)時間,O(1)空間解決。寫完后,說我代碼有個小bug,然后討論后改之。問這個算法在哪種條件下不work,想了許久,突然靈光一現,想出可能鏈表有環或者兩個鏈表有可能有公共節點。他很開心,說很久沒有人能同時想出兩個case了。
node merge_sorted_list(const node head1,const node head2) { if((NULL == head1) && (NULL == head1)) { return NULL; } else if(NULL == head1) { return head2; } else if(NULL == head2) { return head1; } else { node head = NULL,p1 = NULL,p2 = NULL; if(head1->value >= head2->value) { head = head1; p1 = head1->next; p2 = head2; } else { head = head2; p2 = head2->next; p1 = head1; } node p = head; while((NULL != p1) && (NULL != p2)) { if(p1->value >= p2->value) { p->next = p1; p = p1; p1 = p1->next; } else { p->next = p2; p = p2; p2 = p2->next; } } p->next = p1 ? p1 : p2; //if(NULL != p1->next) // p->next = p1; //else // p->next = p2; return head; } }
遞歸實現:
Node * MergeRecursive(Node *head1 , Node *head2) { if ( head1 == NULL ) return head2 ; if ( head2 == NULL) return head1 ; Node *head = NULL ; if ( head1->value > head2->value ) { head = head1 ; head->next = MergeRecursive(head1->next,head2); } else { head = head2 ; head->next = MergeRecursive(head1,head2->next); } return head ; }
//不帶頭節點的鏈表 #include <stdio.h> #include <malloc.h> struct Node { int value; struct Node *next; }; typedef struct Node * node; node CreateList(int iStart,int num) { node head = NULL,p = NULL,q = NULL; for(int i = num;i > 0 ;i --) { p = (node)malloc(sizeof(struct Node)); p->value = iStart + i; if(head == NULL) { head = p; } else { q->next = p; } q = p; } p->next = NULL; return head; } node merge_sorted_list(node head1,node head2) { if((NULL == head1) && (NULL == head2)) { return NULL; } else if(NULL == head1) { return head2; } else if(NULL == head2) { return head1; } else { node p = NULL,p1 = NULL,p2 = NULL; if(head1->value >= head2->value) { p = head1; p1 = head1->next; p2 = head2; } else { p = head2; p2 = head2->next; p1 = head1; } while((NULL != p1) && (NULL != p2)) { if(p1->value >= p2->value) { p->next = p1; p = p1; p1 = p1->next; } else { p->next = p2; p = p2; p2 = p2->next; } } p->next = p1 ? p1 : p2; return head1->value >= head2->value ? head1 : head2; } } void display(node head) { node p = head; while(NULL != p) { printf("%3d",p->value); p = p->next; } printf("\n"); } int main() { node head1 = NULL ,head2 = NULL,head = NULL; head1 = CreateList(5,6); head2 = CreateList(5,6); display(head1); display(head2); head = merge_sorted_list(head1,head2); display(head); return 0; }
2、字符串A和字符串B。是否B包含了A所有的字符串,要考慮字符的個數問題,比如A:aabb , B: abccc,就不滿足條件了。這個題目跟google當年的筆試題很像,開一個256的int[]數組做hashtable,很容易解決了。由於之前沒有考慮上述的情況,他指出來了,稍微改下,就過了
3、一個n*n迷宮,方塊里可能是牆,可能是路,問怎么走出出口,求最短路徑。先說思路,然后寫偽代碼。很簡單的寬度優先,每個方格里記錄走的步數和來自於哪個方塊。很快就解決了。
分析:寬度有限搜索問題,采用隊列結構。。
1)N個數,選出任意兩個數求和,問所有這些可能性的和是多少。我說最簡單的方法是模擬,O(N^2),然后問有沒有更簡單的,想了想,計算了下所有數出現的個數是 (N-1)/2,所以很簡單,就是 sum*(N-1)/2,時間復雜度是O(N)
2)問試卷最后一個題。之前聽同學說過,我自己想過。A B兩個有序數組,A中選一個,B中選一個,要求和為某個指定值m,問怎么選。感覺是《編程之美》上一維數組中求兩個數和的變形,所以只要變換一下:A中的數從頭往尾走,B中數從尾往前走就好;但是這么會遺漏,如果沒找到,用相同的方式,A中的數從尾往頭走,B中的數從頭往尾走,看能否找到
3)問知道怎么確定有環鏈表。說知道。然后問,怎么確定環的起點節點。然后說沒見過。他說,浙大的很奇怪,第一個問題都會,而第二個問題都不會。然后我開始想,最簡單的用hash表保存已遍歷的節點。然后他說需要常數空間。想了很久大概15分鍾不會,讓他提示下。說如果兩個鏈表有公共節點,問怎么去找這個公共節點,想了幾分鍾,想出來了。只要都遍歷一下得到長度的信息,利用這個信息再遍歷一次,就可以找到公共節點。然后想到第有環的只是一個變種,只要把環斷開。就成了第一個問題。然后叫我寫代碼,很順利的寫完。
4)已知兩個矩形的四個節點信息,然后給一個API——可以得到某個點在是否在某矩形內,問怎么判斷矩形相交。答曰,矩形相交不需要這么復雜,只要判斷線段相交就行。可能他之前沒想到我會這么回答,仔細解釋了下,他說可行。然后問有沒有特殊情況,我說有,一個矩形在另一個矩形內,可能線段不相交,矩形也相交了。然后答曰,這個只要判斷小矩陣的幾點是否在大矩陣內就可以了
5)問一個n*n的方塊內,有一條環形路徑。路徑上的點都是1,其他點都是0.。給路徑中的任意一個點,問這個路徑所包含的面積。想了一分鍾,覺得粉兩步走:1)深度優先找路徑 2)寬度優先算面積 然后解釋了下,說可行
更多詳見:http://tianwei.sinaapp.com/2012/09/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%94%B6%E9%9B%86/