摘要:但是一個(gè)偏序關(guān)系,如果我們默認(rèn),先出現(xiàn)的排在前面,那么我們就能比較,的關(guān)系了。對(duì)于算法的每個(gè)節(jié)點(diǎn),我們都有一個(gè)發(fā)現(xiàn)時(shí)間,一個(gè)訪問時(shí)間,當(dāng)運(yùn)行完成時(shí),對(duì)于圖中的任意一條邊,都有所以拓?fù)渑判虻拇涡蚺c頂點(diǎn)的完成時(shí)間恰好相反。
1. 偏序和全序的概念
1.1. 偏序
設(shè)R是集合A上的一個(gè)二元關(guān)系,若R滿足下列三個(gè)性質(zhì)則稱R為A上的偏序關(guān)系
自反性:對(duì)任意x∈A,有
反對(duì)稱性:對(duì)任意的x,y∈A,如果
傳遞性:對(duì)任意x,y,z∈A,若
通俗解釋:自然數(shù)之間的"大于等于"是一個(gè)偏序關(guān)系,例如自然數(shù)的一個(gè)子集A={1,2,3}
"大于等于"的一個(gè)子集R={<1,1>,<2,2>,<3,3>,<3,2>,<2,1>,<3,1>}對(duì)于自反的解釋是1=1,2=2,3=3;對(duì)于反對(duì)稱性,<3,2>,<2,1>,<3,1>∈R,但關(guān)系R中不存在元素<2,3>,<1,2><1,3>因?yàn)?b>2<3,1<2,1<3,對(duì)于傳遞<3,2>,<2,1>∈R即3>2,2>1所以3>1即<3,1>∈R
1.2. 全序
全序是偏序的一個(gè)子集,即集合中任意兩個(gè)元素之間都有明確的"序"的關(guān)系也就是下面的性質(zhì)
完全性:集合中任意兩個(gè)元素之間都有明確的"序"的關(guān)系,由此可見完全性包含了自反性,任意就包含了元素和元素自己
通俗解釋:是偏序但不是全序,設(shè)集合A={1,2,3,b},b=2i+1;由于b是一個(gè)復(fù)數(shù),所以其它的三個(gè)元素都不可以和它來比較大小
1.3. 算法的穩(wěn)定性
如果我們要對(duì)下列數(shù)組的元素按照index的大小進(jìn)行排序 [{id:a,index:1},{id:b,index:1},{id:c,index:2}],我們?cè)O(shè)第一個(gè)為A,第二個(gè)為B,第三個(gè)為C, 我們應(yīng)該如何確定A和B之間的順序呢?
由于A,B的index值相同,但A和B確實(shí)是不同的元素,因此我們無法確定他們的先后順序,即A和B不可比較,所以在A,B,C三個(gè)元素組成的關(guān)系不具備全序關(guān)系。但是一個(gè)偏序關(guān)系,如果我們默認(rèn),先出現(xiàn)的排在前面,那么我們就能比較A,B的關(guān)系了。我們排序就有C,A,B
穩(wěn)定的算法:是對(duì)于存在上述情況的元素總能按照元素出現(xiàn)的先后順序排列的算法
不穩(wěn)定的算法:是對(duì)于上述情況,不能保證先出現(xiàn)的排在前面由此我們說,直接插入排序,冒泡排序,歸并排序,基數(shù)排序是穩(wěn)定的而shell排序,堆排序,快速排序直接選擇排序是不穩(wěn)定的
2. 拓?fù)渑判?/strong>說明:本文圖的構(gòu)建方法及DFS算法可以參考 BFS,DFS 算法原理及js實(shí)現(xiàn)
我們每天早上起床后穿衣的過程可以分為很多步驟,例如,穿內(nèi)褲,穿褲子,穿內(nèi)褲必須在穿褲子之前,同樣的穿襪子必須在穿鞋子之前等等,戴手表和其它的任何一個(gè)動(dòng)作之間都沒有明顯的關(guān)系,因此放在這個(gè)線性序列中的哪里都無所謂
2.1. 拓?fù)渑判蚨x
對(duì)于一個(gè)有向無環(huán)圖G來說,拓?fù)渑判蚓褪菍?duì)圖G中的所有節(jié)點(diǎn)的一次線性排序,該排序滿足如下條件,如果圖G中包含邊(u,v),則節(jié)點(diǎn)u一定在v的前面,可以將拓?fù)渑判蚩醋魇菍D的所有節(jié)點(diǎn)在一條直線上水平排開
3. Kahn算法3.1. 算法原理
對(duì)于一個(gè)有向無環(huán)AOV(頂點(diǎn)表示活動(dòng),邊表示優(yōu)先關(guān)系)圖,我們重復(fù)執(zhí)行以下兩個(gè)步驟,直到不存在入度為0的頂點(diǎn)為止
(1)先擇一個(gè)入度為0的頂點(diǎn)并輸出 (2)從圖中刪除由該節(jié)點(diǎn)發(fā)出的所有邊
這樣得到的序列就是該圖的拓?fù)渑判颍绻局杏协h(huán),則輸出的頂點(diǎn)的數(shù)目小于圖中節(jié)點(diǎn)的數(shù)目
3.2. 算法描述
L一個(gè)用來存放已排序頂點(diǎn)的List S一個(gè)用來存放如度為0的頂點(diǎn)List 當(dāng)S不為空時(shí)執(zhí)行循環(huán)執(zhí)行以下步驟 從S中移除一個(gè)頂點(diǎn)(沒有順序要求,隨意移除)n 將n插入到L中 循環(huán)遍歷從n發(fā)出的邊,對(duì)于所有的孩子節(jié)點(diǎn)m 移除邊如果m的入度為0,則將m放入S中 如果循環(huán)結(jié)束后圖中還有邊則說明圖中有環(huán) 否則L是拓?fù)渑判虻慕Y(jié)果
3.3. 算法的js實(shí)現(xiàn)
//數(shù)據(jù)結(jié)構(gòu) 鄰接鏈表-頂點(diǎn) function Vertex() { if (!(this instanceof Vertex)) return new Vertex(); this.color = this.WHITE; //初始為 白色 this.pi = null; //初始為 無前驅(qū) this.d = this.INFINITY; //初始為 無窮大 this.edges = null; //由頂點(diǎn)發(fā)出的所有邊 this.value = null; //節(jié)點(diǎn)的標(biāo)識(shí) this.data = null; //節(jié)點(diǎn)的數(shù)據(jù) this.incoming = 0; //節(jié)點(diǎn)的入度 } Vertex.prototype = { constructor: Vertex, WHITE: "white", //白色 GRAY: "gray", //灰色 BLACK: "black", //黑色 INFINITY: null, //d 為 null 時(shí)表示無窮大 } //數(shù)據(jù)結(jié)構(gòu) 鄰接鏈表-邊 function Edge() { if (!(this instanceof Edge)) return new Edge(); this.index = null; //邊所依附的節(jié)點(diǎn)的位置 this.sibling = null; this.w = null; //保存邊的權(quán)值 } //數(shù)據(jù)結(jié)構(gòu) 圖-G function Graph() { if (!(this instanceof Graph)) return new Graph(); this.graph = []; this.dict = {}; //字典 用來映射標(biāo)節(jié)點(diǎn)的識(shí)符和數(shù)組中的位置 } Graph.prototype = { constructor: Graph, //這里加進(jìn)來的已經(jīng)具備了邊的關(guān)系 addNode: function(node) { this.graph.push(node); }, getNode: function(index) { return this.graph[index]; }, //創(chuàng)建圖的 節(jié)點(diǎn) initVertex: function(vertexs) { //創(chuàng)建節(jié)點(diǎn)并初始化節(jié)點(diǎn)屬性 value for (let value of vertexs) { let vertex = Vertex(); vertex.value = value.value; vertex.data = value.data; this.graph.push(vertex); } //初始化 字典 for (let i in this.graph) { this.dict[this.graph[i].value] = i; } }, //建立圖中 邊 的關(guān)系 initEdge: function(edges) { for (let field in edges) { let index = this.dict[field]; //從字典表中找出節(jié)點(diǎn)在 graph 中的位置 let vertex = this.graph[index]; //獲取節(jié)點(diǎn) vertex.edges = createLink(0, edges[field].length, edges[field], this.dict, this.graph); } } } //創(chuàng)建鏈表,返回鏈表的第一個(gè)節(jié)點(diǎn) function createLink(index, len, edges, dict, vertexs) { if (index >= len) return null; let edgeNode = Edge(); edgeNode.index = dict[edges[index].id]; //邊連接的節(jié)點(diǎn) 用在數(shù)組中的位置表示 參照字典 vertexs[edgeNode.index].incoming = vertexs[edgeNode.index].incoming + 1; //設(shè)置節(jié)點(diǎn)的入度 edgeNode.w = edges[index].w; //邊的權(quán)值 edgeNode.sibling = createLink(++index, len, edges, dict, vertexs); //通過遞歸實(shí)現(xiàn) 回溯 return edgeNode; } // a內(nèi)褲 b襪子 c手表 d褲子 e鞋 f腰帶 g襯衣 h領(lǐng)帶 i 夾克 vertexs = [{value: "a", data: "內(nèi)褲"}, {value: "b", data: "襪子"}, {value: "c",data: "手表"}, {value: "d", data: "褲子"}, {value: "e",data: "鞋"}, {value: "f", data: "腰帶"}, {value: "g",data: "襯衣"}, {value: "h", data: "領(lǐng)帶"}, {value: "i",data: "夾克"}]; var edges = { a: [{id: "d", w: 1 }, {id: "e", w: 1 }], b: [{id: "e", w: 1}], c: [], d: [{id: "e", w: 1 }, {id: "f", w: 1 }], e: [], f: [{id: "i", w: 1}], g: [{id: "f", w: 1 }, {id: "h", w: 1 }], h: [{id: "i", w: 1}], i: [] } //kahn算法 function kahn(g) { let s = []; //用于存放入度為0的頂點(diǎn) let l = []; //用來存放已經(jīng)排好序的頂點(diǎn) //初始化set 將圖中所有入度為0的節(jié)點(diǎn)加入到set中 for(let v of g.graph) { if(v.incoming==0) s.push(v); } while(s.length > 0) { let u = s.shift(); l.push(u); if (u.edges == null) continue; let sibling = u.edges; while (sibling != null) { let index = sibling.index; let n = g.getNode(index); n.incoming = n.incoming - 1; //刪除邊 if(n.incoming == 0) s.push(n); //入度為0的放入s sibling = sibling.sibling; } } return l; } var g = Graph(); g.initVertex(vertexs); g.initEdge(edges); var r = kahn(g); console.log(r);
運(yùn)行結(jié)果
4. 基于DFS的拓?fù)渑判蛩惴?/strong>4.1. 算法原理
原理:拓?fù)渑判虻拇涡蚺c頂點(diǎn)的完成時(shí)間恰好相反
對(duì)于拓?fù)渑判颍覀円龅氖潜WC對(duì)于任意一條邊(u,v),節(jié)點(diǎn)u一定出現(xiàn)在節(jié)點(diǎn)v前面。
對(duì)于DFS算法的每個(gè)節(jié)點(diǎn),我們都有一個(gè)發(fā)現(xiàn)時(shí)間d,一個(gè)訪問時(shí)間f,當(dāng)DFS運(yùn)行完成時(shí),對(duì)于圖中的任意一條邊(u,v),都有 u.f>v.f,所以拓?fù)渑判虻拇涡蚺c頂點(diǎn)的完成時(shí)間恰好相反。
當(dāng)DFS在圖上運(yùn)行時(shí),探索到任意一條邊(u,v)時(shí),u為灰色,所以v要么是白色,要么是黑色,如果v是白色,則v一定早于u被訪問,即 u.f>v.f,當(dāng)v為黑色時(shí),此時(shí)v已經(jīng)被訪問過,而u還為灰色,即u沒有被訪問,所以v依然早于u被訪問,仍有 u.f>v.f,由此可見上述結(jié)論成立
4.2. js實(shí)現(xiàn)
基于以上結(jié)論,我們用DFS實(shí)現(xiàn)拓?fù)渑判颍恍枰诠?jié)點(diǎn) 的f被設(shè)置值即節(jié)點(diǎn)被訪問后,將其加入一個(gè)后進(jìn)先出隊(duì)列中(調(diào)用unshift方法始終向數(shù)組的頭部添加新元素)L中,當(dāng)DFS運(yùn)行結(jié)束后,L中的元素就是經(jīng)過拓?fù)渑判虻慕Y(jié)果
//數(shù)據(jù)結(jié)構(gòu) 鄰接鏈表-頂點(diǎn) function Vertex() { if (!(this instanceof Vertex)) return new Vertex(); this.color = this.WHITE; //初始為 白色 this.pi = null; //初始為 無前驅(qū) this.d = this.INFINITY; //初始為 無窮大 this.edges = null; //由頂點(diǎn)發(fā)出的所有邊 this.value = null; //節(jié)點(diǎn)的標(biāo)識(shí) this.data = null; //節(jié)點(diǎn)的數(shù)據(jù) this.incoming = 0; } Vertex.prototype = { constructor: Vertex, WHITE: "white", //白色 GRAY: "gray", //灰色 BLACK: "black", //黑色 INFINITY: null, //d 為 null 時(shí)表示無窮大 } //數(shù)據(jù)結(jié)構(gòu) 鄰接鏈表-邊 function Edge() { if (!(this instanceof Edge)) return new Edge(); this.index = null; //邊所依附的節(jié)點(diǎn)的位置 this.sibling = null; this.w = null; //保存邊的權(quán)值 } //數(shù)據(jù)結(jié)構(gòu) 圖-G function Graph() { if (!(this instanceof Graph)) return new Graph(); this.graph = []; this.dict = {}; //字典 用來映射標(biāo)節(jié)點(diǎn)的識(shí)符和數(shù)組中的位置 } Graph.prototype = { constructor: Graph, //這里加進(jìn)來的已經(jīng)具備了邊的關(guān)系 addNode: function(node) { this.graph.push(node); }, getNode: function(index) { return this.graph[index]; }, //創(chuàng)建圖的 節(jié)點(diǎn) initVertex: function(vertexs) { //創(chuàng)建節(jié)點(diǎn)并初始化節(jié)點(diǎn)屬性 value for (let value of vertexs) { let vertex = Vertex(); vertex.value = value.value; vertex.data = value.data; this.graph.push(vertex); } //初始化 字典 for (let i in this.graph) { this.dict[this.graph[i].value] = i; } }, //建立圖中 邊 的關(guān)系 initEdge: function(edges) { for (let field in edges) { let index = this.dict[field]; //從字典表中找出節(jié)點(diǎn)在 graph 中的位置 let vertex = this.graph[index]; //獲取節(jié)點(diǎn) vertex.edges = createLink(0, edges[field].length, edges[field], this.dict, this.graph); } } } //創(chuàng)建鏈表,返回鏈表的第一個(gè)節(jié)點(diǎn) function createLink(index, len, edges, dict, vertexs) { if (index >= len) return null; let edgeNode = Edge(); edgeNode.index = dict[edges[index].id]; //邊連接的節(jié)點(diǎn) 用在數(shù)組中的位置表示 參照字典 vertexs[edgeNode.index].incoming = vertexs[edgeNode.index].incoming + 1; //設(shè)置節(jié)點(diǎn)的入度 edgeNode.w = edges[index].w; //邊的權(quán)值 edgeNode.sibling = createLink(++index, len, edges, dict, vertexs); //通過遞歸實(shí)現(xiàn) 回溯 return edgeNode; } // a內(nèi)褲 b襪子 c手表 d褲子 e鞋 f腰帶 g襯衣 h領(lǐng)帶 i 夾克 vertexs = [{value: "a", data: "內(nèi)褲"}, {value: "b", data: "襪子"}, {value: "c",data: "手表"}, {value: "d", data: "褲子"}, {value: "e",data: "鞋"}, {value: "f", data: "腰帶"}, {value: "g",data: "襯衣"}, {value: "h", data: "領(lǐng)帶"}, {value: "i",data: "夾克"}]; var edges = { a: [{id: "d", w: 1 }, {id: "e", w: 1 }], b: [{id: "e", w: 1}], c: [], d: [{id: "e", w: 1 }, {id: "f", w: 1 }], e: [], f: [{id: "i", w: 1}], g: [{id: "f", w: 1 }, {id: "h", w: 1 }], h: [{id: "i", w: 1}], i: [] } function DFS(g) { let t = 0; let l =[]; for (let v of g.graph) { if (v.color == v.WHITE) DFSVisit(g, v); } function DFSVisit(g, v) { t = t + 1; v.d = t; v.color = v.GRAY; let sibling = v.edges; while (sibling != null) { let index = sibling.index; let n = g.getNode(index); if (n.color == n.WHITE) { n.pi = v; DFSVisit(g, n); //先縱向找 } sibling = sibling.sibling; //利用遞歸的特性來回溯 } v.color = v.BLACK; t = t + 1; v.f = t; //設(shè)置完成時(shí)間 l.unshift(v); //拓?fù)渑判虻拇涡蚺c頂點(diǎn)的完成時(shí)間恰好相反 } console.log(l) } var g = Graph(); g.initVertex(vertexs); g.initEdge(edges); DFS(g);
運(yùn)行結(jié)果
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/92303.html
摘要:歸并排序歸并排序,或,是創(chuàng)建在歸并操作上的一種有效的排序算法,效率為大符號(hào)。以此類推,直到所有元素均排序完畢。與快速排序一樣都由托尼霍爾提出的,因而也被稱為霍爾選擇算法。 showImg(https://segmentfault.com/img/remote/1460000019096360);編譯:周素云、蔣寶尚 學(xué)會(huì)了Python基礎(chǔ)知識(shí),想進(jìn)階一下,那就來點(diǎn)算法吧!畢竟編程語言只...
摘要:結(jié)構(gòu)型模式適配器模式橋接模式裝飾模式組合模式外觀模式享元模式代理模式。行為型模式模版方法模式命令模式迭代器模式觀察者模式中介者模式備忘錄模式解釋器模式模式狀態(tài)模式策略模式職責(zé)鏈模式責(zé)任鏈模式訪問者模式。 主要版本 更新時(shí)間 備注 v1.0 2015-08-01 首次發(fā)布 v1.1 2018-03-12 增加新技術(shù)知識(shí)、完善知識(shí)體系 v2.0 2019-02-19 結(jié)構(gòu)...
摘要:安全性不可更改性排序結(jié)果不能被壞人的攻擊更改。這也是很嚴(yán)重的公鏈安全事故。總而言之,通過設(shè)計(jì)安全的拓?fù)渑判蛩惴ǎ鉀Q交易順序問題。區(qū)塊排序的一致可以保證無效交易標(biāo)記的一致。樞軸鏈和分叉鏈的區(qū)塊獎(jiǎng)勵(lì)計(jì)算規(guī)則是一致的。 showImg(https://segmentfault.com/img/remote/1460000017710155?w=893&h=380); 12月27日,Conf...
摘要:安全性不可更改性排序結(jié)果不能被壞人的攻擊更改。這也是很嚴(yán)重的公鏈安全事故。總而言之,通過設(shè)計(jì)安全的拓?fù)渑判蛩惴ǎ鉀Q交易順序問題。區(qū)塊排序的一致可以保證無效交易標(biāo)記的一致。樞軸鏈和分叉鏈的區(qū)塊獎(jiǎng)勵(lì)計(jì)算規(guī)則是一致的。 showImg(https://segmentfault.com/img/remote/1460000017710155?w=893&h=380); 12月27日,Conf...
閱讀 1399·2021-11-08 13:14
閱讀 754·2021-09-23 11:31
閱讀 1046·2021-07-29 13:48
閱讀 2786·2019-08-29 12:29
閱讀 3380·2019-08-29 11:24
閱讀 1905·2019-08-26 12:02
閱讀 3695·2019-08-26 10:34
閱讀 3441·2019-08-23 17:07