


































































posted @ 2009-11-02 18:13 小強摩羯座 閱讀(410) | 評論 (0) | 編輯 收藏
posts - 195, comments - 34, trackbacks - 0, articles - 1 |
|||||||||||||||
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() posted @ 2009-11-02 18:13 小強摩羯座 閱讀(410) | 評論 (0) | 編輯 收藏 排序算法、時間復雜度與信息熵
![]() ![]() ![]() 在這篇文章里,我們從信息論的角度證明了,基于比較的排序算法需要的比較次數(shù)(在最壞情況下)至少為log2(n!),而log(n!)=Θ(nlogn),這給出了比較排序的一個下界。但那里我們討論的只是最理想的情況。一個事件本身所含的信息量是有大小之分的。看到這篇文章之后,我的思路突然開闊了不少:信息論是非常強大的,它并不只是一個用來分析理論最優(yōu)決策的工具。從信息論的角度來分析算法效率是一件很有趣的事,它給我們分析排序算法帶來了一種新的思路。 假如你手里有一枚硬幣。你希望通過拋擲硬幣的方法來決定今天晚上干什么,正面上網(wǎng)反面看電影。投擲硬幣所產(chǎn)生的結(jié)果將給你帶來一些“信息”,這些信息的多少就叫做“信息量”。如果這個硬幣是“公正”的,正面和反面出現(xiàn)的概率一樣,那么投擲硬幣后不管結(jié)果咋樣,你都獲得了1 bit的信息量。如果你事先就已經(jīng)知道這個硬幣并不是均勻的,比如出現(xiàn)正面的概率本來就要大得多,這時我們就說事件結(jié)果的不確定性比剛才更小。如果投擲出來你發(fā)現(xiàn)硬幣果然是正面朝上,這時你得到的信息量就相對更小(小于1 bit);反之如果投擲出來居然反面朝上了,那你就得到了一個相對較大的信息量(大于1 bit)。但平均下來,我們得到的信息量是小于1 bit的,因為前者發(fā)生的可能性畢竟要大一些。最極端的情況就是,這是一枚被搗了鬼的魔術(shù)硬幣,你怎么投都是正面。此時,你投了硬幣等于沒投,反正結(jié)果都是正面朝上,你得到的信息量永遠為0。 首先我們來看三種經(jīng)典的平方復雜度算法。它們的效率并不高,原因就在于算法過程中會出現(xiàn)越來越多概率嚴重不均的比較。隨著冒泡排序的進行,整個序列將變得越來越有序,位置顛倒的泡泡將越來越少;選擇排序的每一趟選擇中,你都會不斷得到越來越大的數(shù),同時在以后的比較中找到更大的數(shù)的概率也越來越低;在插入排序中,你總是把新的數(shù)與已經(jīng)排好的數(shù)按從大到小的順序依次進行比較,可以想到新的數(shù)一開始就比前面所有的數(shù)中最大的那個還大的概率是相當小的。受此啟發(fā),我們可以很自然地想到一個插入排序的改進:處理一個新的數(shù)時,為何不一開始就與前面處理過的數(shù)中的中位數(shù)進行比較?這種比較的熵顯然更大,能獲取的信息量要大得多,明顯更有價值一些。這就是插入排序的二分查找改進。 下面我們再來看一看幾種O(nlogn)的排序算法。在快速排序算法中,比較的信息熵不會因為排序算法的進行而漸漸減小,這就是快速排序比上面幾個排序算法更優(yōu)秀的根本原因。仔細回顧快速排序算法的過程,我們立即看出,每次比較的兩種結(jié)果出現(xiàn)的概率完全由這一趟劃分過程所選擇的基準關鍵字決定:選擇的基準關鍵字剛好是當前處理的數(shù)字集合的中位數(shù),則比較結(jié)果的不確定性達到最大;如果選擇的基準關鍵字過大或過小,都會出現(xiàn)比較產(chǎn)生的結(jié)果不均等的情況,這使得每次比較平均帶來的信息量大大減少。因此,快速排序算法是很看人品的:如果基準選的好,算法完全有可能達到理論上的最優(yōu);如果基準選的不好,復雜度很容易退化到O(n^2)。 最后,為什么線性排序的算法可以達到O(n)的復雜度?這是因為,線性排序算法并不是基于比較的。一次比較事件(假設沒有相等的情況)所能產(chǎn)生的信息量最多1 bit,而一次Hash分類可以獲得的信息量遠遠超過了1 bit,因為它可以一次確定出n種等概率的可能情況。 Matrix67原創(chuàng),轉(zhuǎn)貼請注明出處~~ posted @ 2009-11-02 16:19 小強摩羯座 閱讀(307) | 評論 (0) | 編輯 收藏 傳說中效率最高的最大流算法(Dinic)呵呵,又從DK那偷代碼了,好興奮哈,以下是這個算法的簡單介紹,不過我用它去解決HDU的1532 竟然TLE,郁悶.到時候再繼續(xù)問問DK吧...so 煩躁. 哈哈 終于經(jīng)過大牛的指點 原來本算法是從0開始標號的...... Dinic是個很神奇的網(wǎng)絡流算法。它是一個基于“層次圖”的時間效率優(yōu)先的最大流算法。 層次圖是什么東西呢?層次,其實就是從源點走到那個點的最短路徑長度。于是乎,我們得到一個定理:從源點開始,在層次圖中沿著邊不管怎么走,經(jīng)過的路徑一定是終點在剩余圖中的最短路。(摘自WC2007王欣上論文)注意,這里是要按照層次走。 那么,MPLA(最短路徑增值)的一大堆復雜的證明我就略掉了,有興趣的請自行參閱WC2007王欣上神牛的論文。 首先我們得知道,Dinic的基本算法步驟是,先算出剩余圖,然后用剩余圖算層次圖,然后在層次圖里找增廣路。不知道你想到?jīng)]有,這個層次圖找增廣路的方法,恰恰就是Ford-Fulkerson類算法的時間耗費最大的地方,就是找一個最短的增廣路。所以呢,層次圖就相當于是一個已經(jīng)預處理好的增廣路標志圖。 如何實現(xiàn)Dinic呢? 首先我們必然要判一下有沒有能到達終點的路徑(判存在增廣路與否),在這個過程中我們順便就把層次圖給算出來了(當然不用算完),然后就沿著層次圖一層一層地找增廣路;找到一條就進行增廣(注意在沿著層次圖找增廣路的時候使用棧的結(jié)構(gòu),把路徑壓進棧);增廣完了繼續(xù)找,找不到退棧,然后繼續(xù)找有沒有與這個結(jié)點相連的下一層結(jié)點,直到棧空。如果用遞歸實現(xiàn),這個東西就很好辦了,不過我下面提供的程序是用了模擬棧,當然這樣就不存在結(jié)點數(shù)過多爆棧的問題了……不過寫起來也麻煩了一些,對于“繼續(xù)找”這個過程我專門開了一個數(shù)組存當前搜索的指針。 上面拉拉雜雜說了一大堆,實際上在我的理解中,層次圖就是一個流從高往低走的過程(這玩意兒有點像預流推進的標號法……我覺得),在一條從高往低的路徑中,自然有些地方會有分叉;這就是Dinic模擬棧中退棧的精華。這就把BFS的多次搜索給省略了不說,時間效率比所謂的理論復雜度要高得多。 這里有必要說到一點,網(wǎng)絡流的時間復雜度都是很悲觀的,一般情況下絕對沒有可能到達那個復雜度的。
#include<iostream>
using namespace std; const long maxn=300; const long maxm=300000; const long inf=0x7fffffff; struct node { long v,next; long val; }s[maxm*2]; long level[maxn],p[maxn],que[maxn],out[maxn],ind; void init() { ind=0; memset(p,-1,sizeof(p)); } inline void insert(long x,long y,long z) { s[ind].v=y; s[ind].val=z; s[ind].next=p[x]; p[x]=ind++; s[ind].v=x; s[ind].val=0; s[ind].next=p[y]; p[y]=ind++; } inline void insert2(long x,long y,long z) { s[ind].v=y; s[ind].val=z; s[ind].next=p[x]; p[x]=ind++; s[ind].v=x; s[ind].val=z; s[ind].next=p[y]; p[y]=ind++; } long max_flow(long n,long source,long sink) { long ret=0; long h=0,r=0; while(1) { long i; for(i=0;i<n;++i) level[i]=0; h=0,r=0; level[source]=1; que[0]=source; while(h<=r) { long t=que[h++]; for(i=p[t];i!=-1;i=s[i].next) { if(s[i].val&&level[s[i].v]==0) { level[s[i].v]=level[t]+1; que[++r]=s[i].v; } } } if(level[sink]==0)break; for(i=0;i<n;++i)out[i]=p[i]; long q=-1; while(1) { if(q<0) { long cur=out[source]; for(;cur!=-1;cur=s[cur].next) { if(s[cur].val&&out[s[cur].v]!=-1&&level[s[cur].v]==2) { break; } } if(cur>=0) { que[++q]=cur; out[source]=s[cur].next; } else { break; } } long u=s[que[q]].v; if(u==sink) { long dd=inf; long index=-1; for(i=0;i<=q;i++) { if(dd>s[que[i]].val) { dd=s[que[i]].val; index=i; } } ret+=dd; //cout<<ret<<endl; for(i=0;i<=q;i++) { s[que[i]].val-=dd; s[que[i]^1].val+=dd; } for(i=0;i<=q;i++) { if(s[que[i]].val==0) { q=index-1; break; } } } else { long cur=out[u]; for(;cur!=-1;cur=s[cur].next) { if(s[cur].val&&out[s[cur].v]!=-1&&level[u]+1==level[s[cur].v]) { break; } } if(cur!=-1) { que[++q]=cur; out[u]=s[cur].next; } else { out[u]=-1; q--; } } } } return ret; } long m,n; int main() { while(scanf("%ld %ld",&m,&n)!=EOF) { init(); for(int i=0;i<n;i++) { long from,to,cost; scanf("%ld %ld %ld",&from,&to,&cost); insert(--from,--to,cost); } long Start,End; scanf("%ld %ld",&Start,&End); printf("%ld\n",max_flow(n,--Start,--End)); } return 0; } posted @ 2009-10-30 14:37 小強摩羯座 閱讀(758) | 評論 (0) | 編輯 收藏 摘要: 最短路徑 之 SPFA算法 (轉(zhuǎn)載)(2009-05-06 20:41:51)
var $tag='spfa,雜談';
var $tag_code='0c0816ca8a11d99e776ffbef47dd2fd0';
標簽:spfa 雜談
... 閱讀全文
posted @ 2009-10-30 14:00 小強摩羯座 閱讀(2960) | 評論 (0) | 編輯 收藏 #include <stdio.h>
int add(int x,int y) {return x+y;} int sub(int x,int y) {return x-y;} int mul(int x,int y) {return x*y;} int div(int x,int y) {return x/y;} int (*func[])()={add,sub,mul,div}; int num,curch; char chtbl[]="+-*/()="; char corch[]="+-*/()=0123456789"; int getach() { int i; while(1) { curch=getchar(); if(curch==EOF) return -1; for(i=0;corch[i]&&curch!=corch[i];i++); if(i<strlen(corch)) break; } return curch; } int getid() { int i; if(curch>='0'&&curch<='9') { for(num=0;curch>='0'&&curch<='9';getach()) num=10*num+curch-'0'; return -1; } else { for(i=0;chtbl[i];i++) if(chtbl[i]==curch) break; if(i<=5) getach(); return i; } } int cal() { int x1,x2,x3,op1,op2,i; i=getid(); if(i==4) x1=cal(); else x1=num; op1=getid(); if(op1>=5) return x1; i=getid(); if(i==4) x2=cal(); else x2=num; op2=getid(); while(op2<=4) { i=getid(); if(i==4) x3=cal(); else x3=num; if((op1/2==0)&&(op2/2==1)) x2=(*func[op2])(x2,x3); else { x1=(*func[op1])(x1,x2); x2=x3; op1=op2; } op2=getid(); } return (*func[op1])(x1,x2); } void main(void) { int value; printf("Please input an expression:\n"); getach(); while(curch!='=') { value=cal(); printf("The result is:%d\n",value); printf("Please input an expression:\n"); getach(); } } posted @ 2009-10-30 00:36 小強摩羯座 閱讀(265) | 評論 (0) | 編輯 收藏 (一)簡單的函數(shù)指針的應用。
//形式1:返回類型(*函數(shù)名)(參數(shù)表) char (*pFun)(int); char glFun(int a){ return;} void main() { pFun = glFun; (*pFun)(2); } 第一行定義了一個指針變量pFun。首先我們根據(jù)前面提到的“形式1”認識到它是一個指向某種函數(shù)的指針,這種函數(shù)參數(shù)是一個int型,返回值是char類型。只有第一句我們還無法使用這個指針,因為我們還未對它進行賦值。 第二行定義了一個函數(shù)glFun()。該函數(shù)正好是一個以int為參數(shù)返回char的函數(shù)。我們要從指針的層次上理解函數(shù)——函數(shù)的函數(shù)名實際上就是一個指針,函數(shù)名指向該函數(shù)的代碼在內(nèi)存中的首地址。 然后就是可愛的main()函數(shù)了,它的第一句您應該看得懂了——它將函數(shù)glFun的地址賦值給變量pFun。main()函數(shù)的第二句中“*pFun”顯然是取pFun所指向地址的內(nèi)容,當然也就是取出了函數(shù)glFun()的內(nèi)容,然后給定參數(shù)為2。 (二)使用typedef更直觀更方便。 //形式2:typedef 返回類型(*新類型)(參數(shù)表) typedef char (*PTRFUN)(int); PTRFUN pFun; char glFun(int a){ return;} void main() { pFun = glFun; (*pFun)(2); } typedef的功能是定義新的類型。第一句就是定義了一種PTRFUN的類型,并定義這種類型為指向某種函數(shù)的指針,這種函數(shù)以一個int為參數(shù)并返回char類型。后面就可以像使用int,char一樣使用PTRFUN了。 第二行的代碼便使用這個新類型定義了變量pFun,此時就可以像使用形式1一樣使用這個變量了。 (三)在C++類中使用函數(shù)指針。 //形式3:typedef 返回類型(類名::*新類型)(參數(shù)表) class CA { public: char lcFun(int a){ return; } }; CA ca; typedef char (CA::*PTRFUN)(int); PTRFUN pFun; void main() { pFun = CA::lcFun; ca.(*pFun)(2); } 在這里,指針的定義與使用都加上了“類限制”或“對象”,用來指明指針指向的函數(shù)是那個類的這里的類對象也可以是使用new得到的。比如: CA *pca = new CA; pca->(*pFun)(2); delete pca; 而且這個類對象指針可以是類內(nèi)部成員變量,你甚至可以使用this指針。比如: 類CA有成員變量PTRFUN m_pfun; void CA::lcFun2() { (this->*m_pFun)(2); } 一句話,使用類成員函數(shù)指針必須有“->*”或“.*”的調(diào)用。
作者:csumck 更新日期:2004-11-07 posted @ 2009-10-30 00:35 小強摩羯座 閱讀(152) | 評論 (0) | 編輯 收藏 系統(tǒng)的可靠度計算公式 收藏
|---(R)————(R)---| 類似于串兩個電阻,在并兩個電阻的圖。問怎樣計算? 本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.csdn.net/zwhfyy/archive/2009/04/02/4042513.aspx posted @ 2009-10-29 11:05 小強摩羯座 閱讀(830) | 評論 (0) | 編輯 收藏 摘要: 背包問題
1.引子
我們?nèi)祟愂且环N貪婪的動物,如果給您一個容量一定的背包和一些大小不一的物品,裝到背包里面的物品就歸您,遇到這種好事大家一定不會錯過,用力塞不一定是最好的辦法,用腦子才行,下面就教您如何解決這樣的問題,以獲得更多的獎品。
2.應用場景
在一個物品向量中找到一個子集滿足條件如下 :
1)這個子集加起來的體積大小不能... 閱讀全文
posted @ 2009-10-28 21:01 小強摩羯座 閱讀(393) | 評論 (0) | 編輯 收藏 在今年的信息學冬令營上,陳啟峰提出了一個自己創(chuàng)造的BST數(shù)據(jù)結(jié)構(gòu)—Size Balanced Tree。這個平衡二叉樹被全世界內(nèi)的許多網(wǎng)站所討論,大家討論的主題也只有一個—SBT能夠取代Treap嗎?本文詳細介紹SBT樹的性質(zhì),以及一些常用的操作,最后證明SBT是一顆高度平衡的二分查找樹。 一. 眾所周知,BST能夠快速的實現(xiàn)查找等動態(tài)操作。但是在某些情況下,比如將一個有序的序列依次插入到BST中,則BST會退化成為一條鏈,效率非常之低。由此引申出來很多平衡BST,比如AVL樹,紅黑樹,treap樹等。這些數(shù)據(jù)結(jié)構(gòu)都是通過引入其他一些性質(zhì)來保證BST的高度在最壞的情況下都保持在O(log n)。其中,AVL樹和紅黑樹的很多操作都非常麻煩,因此實際應用不是很多。而treap樹加入了一些隨機化堆的性質(zhì),實際應用效果非常好,實現(xiàn)起來很簡單,一直以來受到很多人的青睞。本文介紹一種新的平衡BST樹,實現(xiàn)起來也是非常之簡單,并且能夠支持更多的操作,實際評測效率跟treap也不差上下。 在介紹SBT之前,先介紹一下BST以及在BST上的旋轉(zhuǎn)操作。 1. BST是一種高級的數(shù)據(jù)結(jié)構(gòu),它支持很多動態(tài)操作,包括查找,求最小值,最大值,前驅(qū),后繼,插入和刪除,能夠用于字典以及優(yōu)先隊列。 2. 為了保證BST的平衡(不會退化成為一條鏈),通常通過旋轉(zhuǎn)操作來改變BST的結(jié)構(gòu)。旋轉(zhuǎn)操作不會影響binary-search-tree的性質(zhì)!
二.Size Balanced Tree Size Balanced Tree(簡稱SBT)是一種平衡二叉搜索樹,它通過子樹的大小s[t]來維持平衡性質(zhì)。它支持很多動態(tài)操作,并且都能夠在O(log n)的時間內(nèi)完成。
SBT樹中的每個結(jié)點都有left,right,key以及前面提到的size域。SBT能夠保持平衡性質(zhì)是因為其必須滿足下面兩個條件: 對于SBT中的每個結(jié)點t,有性質(zhì)(a)(b): (a). s[right[t]]≥s[left[left[t]]],s[right[left[t]]] (b). s[left[t]]≥s[right[right[t]]],s[left[right[t]]]
即在上圖中,有s[A],s[B]≤s[R]&s[C],s[D] ≤s[L] 三. 假設我們要在BST中插入一個鍵值為v的結(jié)點,一般是用下面這個過程: Simple-Insert(t,v) 執(zhí)行完操作Simple-Insert后,SBT的性質(zhì)(a)和(b)就有可能不滿足了,這是我們就需要修復(Maintain)SBT。 Maintain(t)用來修復根為t的SBT,使其滿足SBT性質(zhì)。由于性質(zhì)(a)和(b)是對稱的,下面僅討論對性質(zhì)(a)的修復。 Case 1:s[left[left[t]]]>s[right[t]]
這種情況下可以執(zhí)行下面的操作來修復SBT 執(zhí)行Right-Rotate(T) 有可能旋轉(zhuǎn)后的樹仍然不是SBT,需要再次執(zhí)行Maintain(T) 由于L的右兒子發(fā)生了變化,因此需要執(zhí)行Maintain(L) Case 2:s[right[left[t]]]>s[right[t]] 這種情況如下圖所示:
需要執(zhí)行一下步驟來修復SBT: 執(zhí)行Left-Rotate(L)。如下圖所示
執(zhí)行Right-Rotate(T)。如下圖所示
當執(zhí)行完(1)(2)后,樹的結(jié)構(gòu)變得不可預測了。但是幸運的是,在上圖中,A,E,F,R子樹仍然是SBT。因此我們可以執(zhí)行Maintain(L)和Maintain(T)來修復B的子樹。 Case 3: 這種情況和case 1是對稱的 Case 4: 這種情況和case 2是對稱的 Maintain操作的偽代碼: 在Maintain過程中,用一個變量flag來避免額外的檢查。當flag為false時,代表case 1和case 2需要被檢查,否則case 3和case 4需要被檢查。 Maintain (t,flag) If flag=false then Maintain(right[t],true) Maintain(t,false) 四.常用操作 插入操作 SBT和插入操作和BST的基本相同,只是在插入之后需要執(zhí)行下Maintain操作。 Insert (t,v) If t=0 then t←NEW-NODE(v) Else s[t] ←s[t]+1 If v<key[t] then Simple-Insert(left[t],v) Else Simple-Insert(right[t],v) Maintain(t,v≥key[t]) 刪除操作 如果沒有找到要刪除的結(jié)點,那么就刪除最后一個訪問的結(jié)點并記錄。 Delete (t,v) If s[t]≤2 then record←key[t] t←left[t]+right[t] Exit s[t] ←s[t]-1 If v=key[t] then Delete(left[t],v[t]+1) Key[t] ←record Maintain(t,true) Else If v<key[t] then Delete(left[t],v) Else Delete(right[t],v) Maintain(t,v<key[t]) 另外,由于SBT的平衡性質(zhì)是靠size域來維護的,而size域本身(子樹所含節(jié)點個數(shù))對于很多查詢算法都特別有用,這樣使得查詢集合里面的譬如第n小的元素,以及一個元素在集合中的排名等操作都異常簡單,并且時間復雜度都穩(wěn)定在O(log n)。下面僅介紹下上表提到的select(t,k)操作和rank(t,v)操作。 3.Select操作 Select(t,k) 4.Rank操作 Rank(t,v) 同樣,求前驅(qū)結(jié)點的操作Pred和后繼結(jié)點的操作都很容易通過size域來實現(xiàn)。 五.相關證明分析 顯然Maintain操作是一個遞歸過程,可能你會懷疑它是否會結(jié)束。下面我們可以證明Maintain操作的平攤時間復雜度為O(1)。 1.關于樹的高度的分析 設f[h]表示高度為h的SBT中結(jié)點數(shù)目的最小值,則有 f[h]= a.證明: (1) (2) s[left[t] ]≥f[h-1],同樣的,左子樹中有一顆高度為h-2的子樹,換句話說,左子樹中含有一顆結(jié)點數(shù)至少為f[h-2]的子樹。由SBT的性質(zhì)(b),可知s[right[t]] ≥f[h-2]。因此我們有s[t]=s[left[t]]+s[right[t]]+1≥f[h-1]+f[h-2]+1。 另外一方面,我們可以構(gòu)造一顆高度為h,并且結(jié)點數(shù)正好為f[h]的SBT,稱這樣的SBT為tree[t]。可以這樣來構(gòu)造tree[h]: tree[h]= 由f[h]的定義可知f[h] ≤f[h-1]+f[h-2]+1(h>1)。因此f[h]的上下界都為f[h-1]+f[h-2]+1,因此有f[h]=f[h-1]+f[h-2]+1。 b.最壞情況下的高度 事實上f[h]是一個指數(shù)函數(shù),通過f[h]的遞推可以計算出通項公式。
定理: 含有n個結(jié)點的SBT在最壞情況下的高度是滿足f[h] ≤n的最大的h值。 假設maxh為含有n個結(jié)點的SBT的最壞情況下的高度。由上面的定理,有 于是很明顯SBT的高度為O(logn),是一顆高度平衡的BST! 2.對Maintain操作的分析 通過前面的計算分析我們能夠很容易分析出Maintain操作是非常高效的。 首先,有一個非常重要的值來評價一顆BST的好壞:所有結(jié)點的平均深度。它是通過所有結(jié)點的深度之和SD除以結(jié)點個數(shù)n計算出來的。一般來說,這個值越小,這顆BST就越好。由于對于一顆BST來說,結(jié)點數(shù)n是一個常數(shù),因此我們期望SD值越小越好。 現(xiàn)在我們集中來看SBT的SD值,它的重要性在于能夠制約Maintain操作的執(zhí)行時間。回顧先前提到的BST中的旋轉(zhuǎn)操作,有個重要的性質(zhì)就是:每次執(zhí)行旋轉(zhuǎn)操作后,SD值總是遞減的! 由于SBT樹的高度總是O(log n),因此SD值也總是保持在O(log n)。并且SD僅在插入一個結(jié)點到SBT后才增加,因此(T是Maintain操作中執(zhí)行旋轉(zhuǎn)的次數(shù))
Maintain操作的次數(shù)等于T加上不需要旋轉(zhuǎn)操作的Maintain操作的次數(shù)。由于后者為O(nlogn)+O(T),因此Maintain的平攤分析時間復雜度為:
對各個操作時間復雜度的分析 現(xiàn)在我們知道了SBT的高度為O(log n),并且 posted @ 2009-10-28 01:07 小強摩羯座 閱讀(480) | 評論 (0) | 編輯 收藏 摘要: 目前最快的數(shù)獨求解程序 - 實現(xiàn)了Knuth的Dancing Links+Algorithm X算法
C++語言: 目前最快的數(shù)獨求解程序 - 實現(xiàn)了Knuth的Dancing Links+Algorithm X算法
//from: http://code.google.com/p/klsudoku/source/checkout
//半瓶墨水修改于 2009 Sept 18
//R... 閱讀全文
posted @ 2009-10-26 23:11 小強摩羯座 閱讀(1204) | 評論 (0) | 編輯 收藏 |
|||||||||||||||