• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0


            求兩定點之間的全部路徑,其根本是一個涉及到搜索和回溯的問題。我們設計算法時所關心的首要問題是:按照何種順序搜索和回溯才能保證路徑可以不重不漏地被全部找到。
             

            圖的存儲結構:鄰接矩陣。Arcs

            工作結構:結點棧 mystack;

            狀態保存結構: 

            (1) VertexStatus[]={0,0,0,1,1,}。當結點未進棧或者已經出棧,則其對應的狀態為0,否則狀態為1

            (2) ArcStatus[][]={0,0,1,0,1..}當且僅當邊的兩個結點都在棧外時,邊的狀態才為0,否則為1

            注意我們只所以設計如上結點、邊兩個狀態存儲結構,就是依據于path的定義,結點不重復,邊不重復。具有邊狀態存儲結構,也是我的算法與其他算法根本上的不同。

            不失一般性,我們假設原點的編號最小為0,目標點的編號最大N。我們的問題轉換成了,求最小編號的節點與最大編號的節點之間的所有路徑。

            Paths={}//路徑集合

            VertexStatus[]={0};//全部置0

            ArcStatus[][]={0};////全部置0

            mystack.push(0);

            VertexStatus[0]=1;

            While(!mystack.empty()){

              Int elem= mystack.top();//獲得棧頂元素

              if(elem==N){//找到了一條路徑

            path=Traverse(mystack);

            Paths.add(path);

            VertexStatus[elem]=0;

            UpdateArcStatus();//更新ArcStatus[][],使得所有兩個端點都不在棧內的邊的狀態為0

            mystack.pop();//移除棧頂元素
              }else{

            i=0;
                      For(;i<N;i++)
                  if(VertexStatus[i]=0&&ArcStatus[elem][i]=0&&Arcs.contain(elem,i))
            {

            VertexStatus[i]=1;

            ArcStatus[elem][i]=1;

            Mystack.push(i);//入棧
                break;
              }
            }
            if(i=N){//該節點沒有符合要求的后續節點

            VertexStatus[elem]=0;

                UpdateArcStaus();////更新ArcStatus[][],使得所有兩個端點都不在棧內的邊的狀為0

                      Mystack.pop();//出棧
                }
               }
            }


             

             

            posted @ 2013-06-07 16:47 鑫龍 閱讀(2208) | 評論 (0)編輯 收藏

            考慮n位二進制數,有多少個數中不存在兩個相鄰的1。例如,3位數中有5個數符合這一要求:000、001、010、100、101。 
            1、試找出其中的規律 
            2、請給出完整代碼實現(參數輸入代碼可略) 
            3、試證明你找到的規律是正確的 
            1、 答:規律為2 n/2+2n-n/2-1。 
            2、 代碼如下 
            #include<cmath> 
            using namespace std; 
            int Func(int n) { 
              if(n<0) {  return -1; } 
              if(n==0) {  return 0; } 
              return pow(2,(n/2))+pow(2,((n-(n/2)))-1;
             
            3、 證明(構造法) 
            答: 
            1列出n位由1組成的2進制數a,111。。。。111;
            2奇數位都用0取代得到數b,010。。。。010; 
            3偶數位也用0取代得到數c,101。。。。101; 
            4數b和數c中的任何一個1都可以被0取代,或不被取代,而構造出滿足要求的數,且每個1的取代具有獨立性。 
            5數b可以構造出2n/2個滿足要求的數,同時數c可以構造出2n-n/2個滿足要求的數。 
            6數b和數a都可以構造出全由0組成二進制數,因此統計存在一個重復。 
            7規律為2 n/2+2n-n/2-1;

            -------------------------------------------------------------------------------------------------
            n位的二進制,比如3位:000 001 010 100 101
            什么規律呢,假設n位二進制有f(n)種不相鄰的組合,
            從最左邊的位置開始放,可以放0,也可以放1。
            如果最左邊放0,那么將不影響第二位的放置,從第二位放起,即有f(n-1)種。
            如果最左邊放1,那么第二位則只能放0,從第三位放起,即有f(n-2)種。
            所以f(n)=f(n-1)+f(n-2)種。
            另外f(1)=2(因為可以放0或者1),f(2)=3(因為可以是00,01,10)。




            posted @ 2013-06-06 21:57 鑫龍 閱讀(238) | 評論 (0)編輯 收藏

            轉自:http://blog.csdn.net/superhackerzhang/article/details/6410670 

            以SGI的STL為例

             

            sort有兩種重載形式

             

            template <class RandomAccessIterator>

            void sort(RandomAccessIterator first, RandomAccessIterator last);  
            template <class RandomAccessIterator, class StrictWeakOrdering>
            void sort(RandomAccessIterator first, RandomAccessIterator last,StrictWeakOrdering comp);
            這兩種形式都要求形隨機訪問迭代器,因此只能用于容器vector或deque,這里我們只以第一種為例進行講解
            在數據量大時,采用Quick Sort,分段遞歸排序,一旦分段后的數據量小于門限值時,為避免遞歸的額外開銷,便采用Insertion sort。 
            如果遞歸的層次過深,還會使用Heap Sort.
            對于以下的以__開頭的命名函數,表示其為被內部的其它函數調用,而不能被用戶直接調用。
            首先來看插入排序
            template <class _RandomAccessIter> 
            void __insertion_sort(_RandomAccessIter __first, _RandomAccessIter __last) {   
            if (__first == __last) return;    
            for (_RandomAccessIter __i = __first + 1; __i != __last; ++__i)     
            __linear_insert(__first, __i, __VALUE_TYPE(__first)); }
            其所調用的 __linear_insert如下示:
            template <class _RandomAccessIter, class _Tp>
            inline void __linear_insert(_RandomAccessIter __first, 
                                        _RandomAccessIter __last, _Tp*) {
              _Tp __val = *__last;
              if (__val < *__first) {
                copy_backward(__first, __last, __last + 1);
                *__first = __val;
              }else
                __unguarded_linear_insert(__last, __val);
            }
            這里需要對其進行一下說明,通常情況下在進行插入排序時,既要進行大小的比較,又要對邊界進行控制,經過上面的改進后,但只需要進行大小的比較便可,這就是代碼的高明這處。
            首先對要插入有序部分的元素__val與隊首的最小元素 *__first進行比較,如果__val < *__first,則只__first與 __last之間的元素向后移一個位置,然后將__val插入隊首。
            如果__val >= *__first,則說明__val在將要新生成的有序隊列中不是最小,因此,在下面的while中不用進行界限控制,只比較元素的大小即可。
            template <class _RandomAccessIter, class _Tp> 
            void __unguarded_linear_insert(_RandomAccessIter __last, _Tp __val) {
            _RandomAccessIter __next = __last;
            --__next;
            while (__val < *__next) {
            *__last = *__next;
            __last = __next;
            --__next;
            }
            *__last = __val;
            }
            在STL中對避免快排時每次都選擇最小或最大的元素做軸,使用以下函數選擇一個三點中值。
            template <class _Tp> 
            inline const _Tp& __median(const _Tp& __a, const _Tp& __b, const _Tp& __c) {
            __STL_REQUIRES(_Tp, _LessThanComparable);
            if (__a < __b)
            if (__b < __c) return __b;
            else if (__a < __c) return __c;
            else return __a;
            else if (__a < __c) return __a;
            else if (__b < __c) return __c;
            else return __b;
            }
            下面是快排中所要使用的分割函數。對[first,last)區間的元素進行分割,使用得中軸右邊的元素大于等于中軸,左邊的元素小于等于中軸,并返回中軸所在位置。
            template <class _RandomAccessIter, class _Tp> 
            _RandomAccessIter __unguarded_partition(_RandomAccessIter __first,_RandomAccessIter __last,_Tp __pivot) {
            while (true) {
            while (*__first < __pivot) ++__first;
            --__last;
            while (__pivot < *__last) --__last;
            if (!(__first < __last))
            return __first;
            iter_swap(__first, __last);
            ++__first;
            }
            }
            sort 代碼如下
            template <class _RandomAccessIter> 
            inline void sort(_RandomAccessIter __first, _RandomAccessIter __last) {
            if (__first != __last) {
            __introsort_loop(__first, __last,
            __VALUE_TYPE(__first),
            __lg(__last - __first) * 2);
            __final_insertion_sort(__first, __last);
            }
            }
            基中__lg(n)用來找出2^k<=n的最大k值,用以控控制遞歸的深度。
            template <class _Size> 
            inline _Size __lg(_Size __n) {
            _Size __k;
            for (__k = 0; __n != 1; __n >>= 1) ++__k;
            return __k;
            }
            下面的是用來對區間使用快排,以達到部分有序狀態。
            template <class _RandomAccessIter, class _Tp, class _Size> 
            void __introsort_loop(_RandomAccessIter __first,
            _RandomAccessIter __last, _Tp*,
            _Size __depth_limit) {
            while (__last - __first > __stl_threshold) {//__stl_threshold在這里用前面定義的常量16,當區間多于16個元素時,才有必要使用快排。
            if (__depth_limit == 0) {//當遞歸的層次過深時,使用堆排序。
            partial_sort(__first, __last, __last);
            return;
            }
            --__depth_limit;
            _RandomAccessIter __cut = __unguarded_partition(__first, __last,
            _Tp(__median(*__first,
            *(__first + (__last - __first)/2),
            *(__last - 1))));//使用分割函數,反回中軸
            __introsort_loop(__cut, __last, (_Tp*) 0, __depth_limit);//對右半部分進行遞歸排序
            __last = __cut;//使尾指針指向中軸,即要對左半部分排序
            }
            }
            最后使用插入排序對各部分進行排序。
            template <class _RandomAccessIter> 
            void __final_insertion_sort(_RandomAccessIter __first,
            _RandomAccessIter __last) {
            if (__last - __first > __stl_threshold) {
            __insertion_sort(__first, __first + __stl_threshold);
            __unguarded_insertion_sort(__first + __stl_threshold, __last);
            } else __insertion_sort(__first, __last);
            }
            些函數先判斷元素個數是否大于16,如是大于,則先用__insertion_sort()對16個元素的子序列排序,再用__unguarded_insertion_sort()對
            其余的排序。否則直接用__insertion_sort()排序。
            template <class _RandomAccessIter> 
            inline void __unguarded_insertion_sort(_RandomAccessIter __first,
            _RandomAccessIter __last) {
            __unguarded_insertion_sort_aux(__first, __last, __VALUE_TYPE(__first));
            }

            template <class _RandomAccessIter, class _Tp, class _Compare>
            void __unguarded_insertion_sort_aux(_RandomAccessIter __first,
            _RandomAccessIter __last,
            _Tp*, _Compare __comp) {
            for (_RandomAccessIter __i = __first; __i != __last; ++__i)
            __unguarded_linear_insert(__i, _Tp(*__i), __comp);
            }
            template <class _RandomAccessIter, class _Tp> 
            void __unguarded_linear_insert(_RandomAccessIter __last, _Tp __val) {
            _RandomAccessIter __next = __last;
            --__next;
            while (__val < *__next) {
            *__last = *__next;
            __last = __next;
            --__next;
            }
            *__last = __val;
            }
            ps:個人感覺__final_insertion_sort()中區分區間大小是多此一舉,因為最終其調用的都是__unguarded_linear_insert(),其并沒用針對不
            同的大小區間采用明顯不用的算法。

            posted @ 2013-06-01 18:53 鑫龍 閱讀(425) | 評論 (0)編輯 收藏

                 摘要:                第二十八~二十九章:最大連續乘積子串、字符串編輯距離前言    時間轉瞬即逝,一轉眼,又有4個多月沒來更新blog了,過去4個月都在干啥呢?對的,今2013年元旦和朋友利用業余時間一起搭了個方便朋友們找工作的編程面試算法論壇:為學論壇http://www.51weixu...  閱讀全文

            posted @ 2013-05-28 22:18 鑫龍 閱讀(344) | 評論 (0)編輯 收藏

            第二十七章:不改變正負數之間相對順序重新排列數組.時間O(N),空間O(1)


            前言

                在這篇文章:九月騰訊,創新工場,淘寶等公司最新面試十三題第5題(一個未排序整數數組,有正負數,重新排列使負數排在正數前面,并且要求不改變原來的正負數之間相對順序),自從去年九月收錄了此題至今,一直未曾看到令人滿意的答案,為何呢?

                因為一般達不到題目所要求的:時間復雜度O(N),空間O(1),且保證原來正負數之間的相對位置不變。本編程藝術系列第27章就來闡述這個問題,若有任何漏洞,歡迎隨時不吝指正。謝謝。


            重新排列使負數排在正數前面

            原題是這樣的:

            一個未排序整數數組,有正負數,重新排列使負數排在正數前面,并且要求不改變原來的正負數之間相對順序。
            比如: input: 1,7,-5,9,-12,15 ,ans: -5,-12,1,7,9,15 。且要求時間復雜度O(N),空間O(1) 。

                OK,下面咱們就來試著一步一步解這道題,如下5種思路(從復雜度O(N^2)到O(N*logN),從不符合題目條件到一步步趨近于條件)):

            1. 最簡單的,如果不考慮時間復雜度,最簡單的思路是從頭掃描這個數組,每碰到一個正數時,拿出這個數字,并把位于這個數字后面的所有數字往前挪動一位。挪完之后在數組的末尾有一個空位,這時把該正數放入這個空位。由于碰到一個正,需要移動O(n)個數字,因此總的時間復雜度是O(n2)。
            2. 既然題目要求的是把負數放在數組的前半部分,正數放在數組的后半部分,因此所有的負數應該位于正數的前面。也就是說我們在掃描這個數組的時候,如果發現有正數出現在負數的前面,我們可以交換他們的順序,交換之后就符合要求了。
              因此我們可以維護兩個指針,第一個指針初始化為數組的第一個數字,它只向后移動;第二個指針初始化為數組的最后一個數字,它只向前移動。在兩個指針相遇之前,第一個指針總是位于第二個指針的前面。如果第一個指針指向的數字是正而第二個指針指向的數字是負數,我們就交換這兩個數字。
              但遺憾的是上述方法改變了原來正負數之間的相對順序。所以,咱們得另尋良策
            3. 首先,定義這樣一個過程為“翻轉”:(a1,a2,...,am,b1,b2,...,bn) --> (b1,b2,...,bn,a1,a2,...,am)。其次,對于待處理的未排序整數數組,從頭到尾進行掃描,尋找(正正...正負...負負)串;每找到這樣一個串,則計數器加1;若計數為奇數,則對當前串做一個“翻轉”;反復掃描,直到再也找不到(正正...正負...負負)串。

              此思路來自朋友胡果果,空間復雜度雖為O(1),但其時間復雜度O(N*logN)。更多具體細節參看原文:http://qing.weibo.com/1570303725/5d98eeed33000hcb.html。故,不符合題目要求,繼續尋找。

            4. 我們可以這樣,設置一個起始點j, 一個翻轉點k,一個終止點L,從右側起,起始點在第一個出現的負數, 翻轉點在起始點后第一個出現的正數,終止點在翻轉點后出現的第一個負數(或結束)。

              如果無翻轉點, 則不操作,如果有翻轉點, 則待終止點出現后, 做翻轉, 即ab => ba 這樣的操作。翻轉后, 負數串一定在左側, 然后從負數串的右側開始記錄起始點, 繼續往下找下一個翻轉點。

              例子中的就是(下劃線代表要交換順序的兩個數字):

              1, 7, -5, 9-12, 15  
              第一次翻轉: 1, 7, -5, -12,9, 15   =>  1, -12, -5, 7, 9, 15
              第二次翻轉: -5, -12, 1, 7, 9, 15

              此思路2果真解決了么?NO,用下面這個例子試一下,我們就能立馬看出了漏洞:
              1, 7, -5, -6, 9-12, 15(此種情況未能處理)
              7 -5 -6 -12 9 15
              -12 -5 -6 7 9 15
              -6 -12 -5 1 7 9 15   (此時,正負數之間的相對順序已經改變,本應該是-5,-6,-12,而現在是-6 -12 -5)
            5. 看來這個問題的確有點麻煩,不過我們最終貌似還是找到了另外一種解決辦法,正如朋友超越神所說的:從后往前掃描,遇到負數,開始記錄負數區間,然后遇到正數,記錄前面的正數區間,然后把整個負數區間與前面的正數區間進行交換,交換區間但保序的算法類似(a,bc->bc,a)的字符串原地翻轉算法。交換完之后要繼續向前一直掃描下去,每次碰到負數區間在正數區間后面,就翻轉區間。下面,將詳細闡述此思路4。

            思路5之區間翻轉

                其實上述思路5非常簡單,既然單個翻轉無法解決問題,那么咱們可以區間翻轉阿。什么叫區間翻轉?不知讀者朋友們是否還記得本blog之前曾經整理過這樣一道題,微軟面試100題系列第10題,如下:

            10、翻轉句子中單詞的順序。
            題目:輸入一個英文句子,翻轉句子中單詞的順序,但單詞內字符的順序不變。句子中單詞以空格符隔開。為簡單起見,標點符號和普通字母一樣處理。例如輸入“I am a student.”,則輸出“student. a am I”。而此題可以在O(N)的時間復雜度內解決

                由于本題需要翻轉句子,我們先顛倒句子中的所有字符。這時,不但翻轉了句子中單詞的順序,而且單詞內字符也被翻轉了。我們再顛倒每個單詞內的字符。由于單詞內的字符被翻轉兩次,因此順序仍然和輸入時的順序保持一致。
                以上面的輸入為例:翻轉“I am a student.”中所有字符得到“.tneduts a ma I”,再翻轉每個單詞中字符的順序得到“students. a am I”,正是符合要求的輸出(編碼實現,可以參看此文:http://zhedahht.blog.163.com/blog/static/254111742007289205219/)。

                對的,上述思路3就是這個意思,單詞翻轉便相當于于區間翻轉,既如此,咱們來驗證下上述思路2中那個測試用例,如下:

            1, 7, -5, -6, 9-12, 15
            1 7 -5 -6 -12 9 15
            -12 -6 -5 7 1 9 15   (借用單詞翻轉的方法,先逐個數字翻轉,后正負數整體原地翻轉)
            -5 -6 -12 1 7 9 15


            思路5再次被質疑

                但是,我還想再問,問題至此被解決了么?真的被KO了么?NO,咱們來看這樣一種情況,正如威士忌所說:

            看看這個數據,+-+-+-+-+------+,假如Nminus 等于 n/2,由于前面都是+-+-+-,區間交換需要 n/2/2 = n/4次,每次交換是 T(2*(Nminus + Nplus)) >= T(n),n/4 * T(n) = T(n*n/4)=O(n^2)。

                還有一種更壞的情況,就是+-+-+-+-+------+這種數據可能,后面一大堆的負數,前面正負交替。所以,咱們的美夢再次破滅,路漫漫其修遠兮,此題仍然未找到一個完全解決了的方案。

            posted @ 2013-05-28 17:50 鑫龍 閱讀(566) | 評論 (0)編輯 收藏

            第二十五章:二分查找實現(Jon Bentley:90%程序員無法正確實現)

            作者:July
            出處:結構之法算法之道

            引言

                Jon Bentley:90%以上的程序員無法正確無誤的寫出二分查找代碼。也許很多人都早已聽說過這句話,但我還是想引用《編程珠璣》上的如下幾段文字: 

            二分查找可以解決(預排序數組的查找)問題:只要數組中包含T(即要查找的值),那么通過不斷縮小包含T的范圍,最終就可以找到它。一開始,范圍覆蓋整個數組。將數組的中間項與T進行比較,可以排除一半元素,范圍縮小一半。就這樣反復比較,反復縮小范圍,最終就會在數組中找到T,或者確定原以為T所在的范圍實際為空。對于包含N個元素的表,整個查找過程大約要經過log(2)N次比較。 
            多數程序員都覺得只要理解了上面的描述,寫出代碼就不難了;但事實并非如此。如果你不認同這一點,最好的辦法就是放下書本,自己動手寫一寫。試試吧。 
            我在貝爾實驗室和IBM的時候都出過這道考題。那些專業的程序員有幾個小時的時間,可以用他們選擇的語言把上面的描述寫出來;寫出高級偽代碼也可以。考試結束后,差不多所有程序員都認為自己寫出了正確的程序。于是,我們花了半個鐘頭來看他們編寫的代碼經過測試用例驗證的結果。幾次課,一百多人的結果相差無幾:90%的程序員寫的程序中有bug(我并不認為沒有bug的代碼就正確)。 
            我很驚訝:在足夠的時間內,只有大約10%的專業程序員可以把這個小程序寫對。但寫不對這個小程序的還不止這些人:高德納在《計算機程序設計的藝術 第3卷 排序和查找》第6.2.1節的“歷史與參考文獻”部分指出,雖然早在1946年就有人將二分查找的方法公諸于世,但直到1962年才有人寫出沒有bug的二分查找程序。——喬恩·本特利,《編程珠璣(第1版)》第35-36頁。

                你能正確無誤的寫出二分查找代碼么?不妨一試。

            二分查找代碼

                 二分查找的原理想必不用多解釋了,不過有一點必須提醒讀者的是,二分查找是針對的排好序的數組。OK,紙上讀來終覺淺,覺知此事要躬行。我先來寫一份,下面是我寫的一份二分查找的實現(之前去某一家公司面試也曾被叫當場實現二分查找,不過結果可能跟你一樣,當時就未能完整無誤寫出),有任何問題或錯誤,懇請不吝指正:

            //二分查找V0.1實現版  
            //copyright@2011 July  
            //隨時歡迎讀者找bug,email:zhoulei0907@yahoo.cn。  
              
            //首先要把握下面幾個要點:  
            //right=n-1 => while(left <= right) => right=middle-1;  
            //right=n   => while(left <  right) => right=middle;  
            //middle的計算不能寫在while循環外,否則無法得到更新。  
              
            int binary_search(int array[],int n,int value)  
            {  
                
            int left=0;  
                
            int right=n-1;  
                
            //如果這里是int right = n 的話,那么下面有兩處地方需要修改,以保證一一對應:  
                
            //1、下面循環的條件則是while(left < right)  
                
            //2、循環內當array[middle]>value 的時候,right = mid  
              
                
            while (left<=right)          //循環條件,適時而變  
                {  
                    
            int middle=left + ((right-left)>>1);  //防止溢出,移位也更高效。同時,每次循環都需要更新。  
              
                    
            if (array[middle]>value)  
                    {  
                        right 
            =middle-1;   //right賦值,適時而變  
                    }   
                    
            else if(array[middle]<value)  
                    {  
                        left
            =middle+1;  
                    }  
                    
            else  
                        
            return middle;    
                    
            //可能會有讀者認為剛開始時就要判斷相等,但畢竟數組中不相等的情況更多  
                    
            //如果每次循環都判斷一下是否相等,將耗費時間  
                }  
                
            return -1;  
            }  


            posted @ 2013-05-28 16:41 鑫龍 閱讀(220) | 評論 (0)編輯 收藏

                 摘要:   第二十三、四章:楊氏矩陣查找,倒排索引關鍵詞Hash不重復編碼實踐作者:July、yansha。編程藝術室出品。出處:結構之法算法之道。前言    本文闡述兩個問題,第二十三章是楊氏矩陣查找問題,第二十四章是有關倒排索引中關鍵詞Hash編碼的問題,主要要解決不重復以及追加的功能,同時也是經典算法研究系列十一、從頭到尾徹底解析Hash表算法之續。  &nb...  閱讀全文

            posted @ 2013-05-28 16:39 鑫龍 閱讀(331) | 評論 (0)編輯 收藏

            題目:一個單入口單出口的有向無環圖中,要求在某些地方插入一些節點使得任何一條由起點到終點所經歷的節點數相同,類似于下面的圖,要求給出算法描述并分析時間復雜度。

            如上圖所示,節點A到C有兩條路徑,ABC這條路徑經過了一個節點,而AC路徑經過了0個節點,我們的算法所要做的事就是要在AC路徑中間加入一個節點,然后ABC路徑和ADC路徑都經過了一個節點。

            算法是這樣的:對于每個節點維護一組信息,包括節點的層數(起始節點到該節點的路徑長度,起始節點設為0)以及生成該長度的父節點,相對于右圖,節點6維護的不經處理的信息就是:層數2來自節點3和節點4;節點7維護的不經處理的信息就是:層數3來自節點5和節點6以及層數2來自節點4;節點8的不經處理的信息是:層數4來自節點7,層數3來自節點7,層數2來自節點4。我們算法所要做的事就是最終使每個節點需要維護的層信息變為一個,即無論從那條路徑到該節點,該節點所處的層數都是固定的。算法如下:
            1、初始化起始節點的層數信息
            2、從起始節點開始遍歷每條路徑,遇到每個節點生成一個維護信息
            (1)如果此節點不存在維護信息,創建之;
            (2)如果該節點存在維護信息,有兩種情況:
            (a)如果生成的維護信息的層數和原來已有的維護信息的層數是相同的,則合并這兩個維護信息,比如對于例子中的圖,節點5原來的維護信息為“層數3來自節點2”,然后從節點3到節點5生成的維護信息為“層數3來自節點3”,由于層數相同,我們可以將其合并為“層數3來自節點2和節點3”;
            (b)如果生成的維護信息的層數和原來節點的維護信息的層數不一致,我們需要比較那一個的層數較大:
            a.如果原來維護信息的層數較大,此時,我們只需要在生成此維護信息的節點與此節點之間插入一個新的節點,然后生成新節點的維護信息,然后從新節點開始(2)過程
            b.如果新生成的維護信息的層數較大,將新生成的節點信息存入此節點,然后我們需要在生成原來維護信息的所有節點和此節點之間插入新節點,并且需要從所有的新插入節點開始(2)過程



            posted @ 2013-05-26 10:40 鑫龍 閱讀(261) | 評論 (0)編輯 收藏

            題目:

            已知一個函數rand7()能夠生成1-7的隨機數,請給出一個函數,該函數能夠生成1-10的隨機數。


            思路:

            假如已知一個函數能夠生成1-49的隨機數,那么如何以此生成1-10的隨機數呢?


            解法:

            該解法基于一種叫做拒絕采樣的方法。主要思想是只要產生一個目標范圍內的隨機數,則直接返回。如果產生的隨機數不在目標范圍內,則丟棄該值,重新取樣。由于目標范圍內的數字被選中的概率相等,這樣一個均勻的分布生成了。

            顯然rand7至少需要執行2次,否則產生不了1-10的數字。通過運行rand7兩次,可以生成1-49的整數,

               1  2  3  4  5  6  7 1  1  2  3  4  5  6  7 2  8  9 10  1  2  3  4 3  5  6  7  8  9 10  1 4  2  3  4  5  6  7  8 5  9 10  1  2  3  4  5 6  6  7  8  9 10  *  * 7  *  *  *  *  *  *  *
            由于49不是10的倍數,所以我們需要丟棄一些值,我們想要的數字范圍為1-40,不在此范圍則丟棄并重新取樣。

            代碼:

            1. int rand10() {  
            2.   int row, col, idx;  
            3.   do {  
            4.     row = rand7();  
            5.     col = rand7();  
            6.     idx = col + (row-1)*7;  
            7.   } while (idx > 40);  
            8.   return 1 + (idx-1)%10;  
            9. }  

            由于row范圍為1-7,col范圍為1-7,這樣idx值范圍為1-49。大于40的值被丟棄,這樣剩下1-40范圍內的數字,通過取模返回。下面計算一下得到一個滿足1-40范圍的數需要進行取樣的次數的期望值:

            E(# calls to rand7) = 2 * (40/49) +                       4 * (9/49) * (40/49) +                       6 * (9/49)2 * (40/49) +                       ...                                             =  2k * (9/49)k-1 * (40/49)                       k=1                      = (80/49) / (1 - 9/49)2                     = 2.45
            優化:

            上面的方法大概需要2.45次調用rand7函數才能得到1個1-10范圍的數,下面可以進行再度優化。

            對于大于40的數,我們不必馬上丟棄,可以對41-49的數減去40可得到1-9的隨機數,而rand7可生成1-7的隨機數,這樣可以生成1-63的隨機數。對于1-60我們可以直接返回,而61-63則丟棄,這樣需要丟棄的數只有3個,相比前面的9個,效率有所提高。而對于61-63的數,減去60后為1-3,rand7產生1-7,這樣可以再度利用產生1-21的數,對于1-20我們則直接返回,對于21則丟棄。這時,丟棄的數就只有1個了,優化又進一步。當然這里面對rand7的調用次數也是增加了的。代碼如下:

            1. int rand10Imp() {  
            2.   int a, b, idx;  
            3.   while (true) {  
            4.     a = rand7();  
            5.     b = rand7();  
            6.     idx = b + (a-1)*7;  
            7.     if (idx <= 40)  
            8.       return 1 + (idx-1)%10;  
            9.     a = idx-40;  
            10.     b = rand7();  
            11.     // get uniform dist from 1 - 63  
            12.     idx = b + (a-1)*7;  
            13.     if (idx <= 60)  
            14.       return 1 + (idx-1)%10;  
            15.     a = idx-60;  
            16.     b = rand7();  
            17.     // get uniform dist from 1-21  
            18.     idx = b + (a-1)*7;  
            19.     if (idx <= 20)  
            20.       return 1 + (idx-1)%10;  
            21.   }  
            22. }  
            下面計算下優化后方法的調用rand7函數的期望次數:

            E(# calls to rand7) = 2 * (40/49) +                       3 * (9/49) * (60/63) +                       4 * (9/49) * (3/63) * (20/21) +                         (9/49) * (3/63) * (1/21) *                       [ 6 * (40/49) +                         7 * (9/49) * (60/63) +                         8 * (9/49) * (3/63) * (20/21) ] +                        ((9/49) * (3/63) * (1/21))2 *                       [ 10 * (40/49) +                         11 * (9/49) * (60/63) +                         12 * (9/49) * (3/63) * (20/21) ] +                       ...                      = 2.2123
            這里期望次數為2.21,比起未優化的2.45次減少了大概10%。

            posted @ 2013-05-20 22:55 鑫龍 閱讀(4027) | 評論 (0)編輯 收藏

                 摘要: 第十六~第二十章:全排列,跳臺階,奇偶排序,第一個只出現一次等問題作者:July、2011.10.16。出處:http://blog.csdn.net/v_JULY_v。引言    最近這幾天閑職在家,一忙著投簡歷,二為準備面試而搜集整理各種面試題。故常常關注個人所建的Algorithms1-14群內朋友關于筆試,面試,宣講會,offer,薪資的討論以及在群內發布的各種筆/面試...  閱讀全文

            posted @ 2013-05-16 16:42 鑫龍 閱讀(451) | 評論 (0)編輯 收藏

            僅列出標題
            共20頁: 1 2 3 4 5 6 7 8 9 Last 
            久久这里都是精品| 久久精品久久久久观看99水蜜桃| 日韩人妻无码一区二区三区久久| 亚洲AV日韩精品久久久久久久| 久久综合给久久狠狠97色| 亚洲伊人久久大香线蕉苏妲己| 国产成人精品久久亚洲高清不卡 | 久久久久久国产a免费观看不卡| 伊人久久大香线蕉av不卡| 久久精品国产亚洲一区二区| 午夜精品久久影院蜜桃 | 99久久无色码中文字幕人妻| 国产综合久久久久| 热久久国产欧美一区二区精品| 成人久久免费网站| 久久夜色精品国产亚洲av| 精品久久无码中文字幕| 久久影院亚洲一区| 伊人久久综在合线亚洲2019| 久久人与动人物a级毛片| 亚洲国产精品一区二区久久| 无码人妻久久一区二区三区 | 久久久久国产精品人妻| 久久久久免费精品国产| 久久久久亚洲av无码专区喷水 | 精品一区二区久久| 色欲久久久天天天综合网| 久久精品18| 久久精品国产精品亚洲下载| 久久国产精品久久国产精品| 久久香综合精品久久伊人| 久久久久亚洲AV成人网人人网站| 久久免费99精品国产自在现线| 国产精品久久成人影院| www性久久久com| 国产精品视频久久久| 久久精品国产久精国产| 国产精品久久久久久久| 无码AV波多野结衣久久| 久久综合久久美利坚合众国| 人妻无码精品久久亚瑟影视|