This commit is contained in:
yinkanglong_lab
2021-03-09 21:28:25 +08:00
parent 26759fc0bd
commit 03c201fbff
63 changed files with 2782 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
\>简介
\>\>document object
model文档对象模型将HTML文档呈现为带有元素、属性和文本的树结构成为节点树
\>\>三种DOM结点;
> 元素节点:\<html\>\<body\>\<p\>(tag)
> 文本节点:\<li\>\</li\>\<script\>\<css\>
> 属性节点:元素属性\<a href = "http://"\>其中href即为元素的属性
![713150815405.png](media/41b23d86a383cc654da42e8957b33725.png)
\>\>节点属性:
> nodeName
> 1\. 元素节点的 nodeName 与标签名相同
> 2\. 属性节点的 nodeName 是属性的名称
> 3\. 文本节点的 nodeName 永远是\#text
> 4\. 文档节点的 nodeName 永远是\>\#document
> nodeType节点类型1-\>元素节点2-\>属性节点3-\>文本节点
> nodeValue节点值元素节点返回null属性节点返回属性值文本节点返回文本内容
> 1\. 元素节点的 nodeValue 是 undefined 或 null
> 2\. 文本节点的 nodeValue 是文本自身
> 3\. 属性节点的 nodeValue 是属性的值
> childNodes返回子节点数组只有元素节点有子节点
> firstChild返回第一个子节点
> lastChild返回最后一个子节点
> nextSibling返回下一个兄弟节点
> previousSibling返回节点的上一个兄弟节点
> parentNode返回节点的父节点。
\>\>节点方法是document对象的节点方法
> write():写入内容到文档
> getElementBYId()返回指定ID的元素
> getElementsByTagName():返回带有制定标签名的所有元素(是一个数组)
> get/setAttribute('key', 'value'):返回设置属性节点
\>\>其他元素的结点方法:
| 节点方法 | 说明 |
|----------------------------------|--------------------------------|
| createElenment('tafName') | 创建元素节点 |
| createTextNode(text) | 创建文本节点 |
| appendChild(o) | 在父节点末尾附加子节点 |
| reateDocumentFragment() | 创建文档片段 |
| removeChild(oP) | 删除节点 |
| replaceChild(newOp,targetOp) | 替换节点 |
| insertBefore(newOp,targerOp) | 已有的子节点前插入一个新的节点 |
| insertAfter(newOp,targetOp) | 已有的子节点后插入一个新的节点 |
| get/setAttribute('key', 'value') | 设置或得到属性节点 |
| clonNode(true/false) | 复制节点 |
\>元素内容
> innerHTML替换时包括其中的html标签
> innerText替换时只有其中的文本内容
> 修改p标签的这两个值会得到不同的结果
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51898168)
[copy](http://blog.csdn.net/estom_yin/article/details/51898168)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<p\>**JavaScript**\</p\>**
3. **\<p\>**JavaScript**\</p\>**
4. **\<input** type="button" id="inp" value="click"**\>**
5. **\<script** type="text/javascript"**\>**
6. var inp = document.getElementById('inp');
7. inp.onclick = function(){
8. var ip = document.getElementsByTagName('p');
9. alert(ip[0].innerHTML);
10. ip[0].innerHTML = "\<i\>hello\</i\>";
11. ip[1].innerText = "\<i\>hello\</i\>";
12. }
13. **\</script\>**
14. **\</body\>\</span\>**
![713160404053.png](media/a287b11ce3f0470882a238120c33a6bb.png)
![713160410069.png](media/04d4031de63e53c5addd72792a6b60bd.png)
\>元素样式
> 方法:
> style属性能够创建新的属性并赋值
> className属性只能改变标签的类属性使用已经有的类来改变标签的属性
> 语法:
> object.style.property = new style;
> object.style.className = "class"
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51898168)
[copy](http://blog.csdn.net/estom_yin/article/details/51898168)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<h2** id="ih"**\>**JavaScript**\</h2\>**
3. **\<input** type="button" id="inp" value="click"**\>**
4. **\<script** type="text/javascript"**\>**
5. var inp = document.getElementById("inp");
6. inp.onclick = function () {
7. var oh = document.getElementById("ih");
8. oh.style.color = "red";
9. oh.style.width = "300px";
10. oh.style.backgroundColor = "\#CCC";
11. }
12. **\</script\>**
13. **\</body\>\</span\>**
\>显示和隐藏
> display = none 或者block
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51898168)
[copy](http://blog.csdn.net/estom_yin/article/details/51898168)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<p** id="ip"**\>**你可以把我隐藏,也可以让我显示哦\~**\</p\>**
3. **\<input** type="button" id="ihide" value="hide"**\>**
4. **\<input** type="button" id="ishow" value="show"**\>**
5. **\<script** type="text/javascript"**\>**
6. var op = document.getElementById("ip");
7. var inp = document.getElementsByTagName("input");
8. inp[0].onclick = function () {
9. op.style.display = "none";
10. }
11. inp[1].onclick = function () {
12. op.style.display = 'block';
13. }
14. **\</script\>**
15. **\</body\>\</span\>**

View File

@@ -0,0 +1,99 @@
\>页面尺寸
\>\>宽高尺寸
> clientWidth / clientHeight窗口的宽度高度
> scrollWidth / scrollHeight文档内容的高度宽度
> offsetWidth / offsetHeight文档内容的高度宽度
\>\>坐标位置
> scrollleft / scrollTop滚轴的水平便宜距离垂直偏移距离
> offsetLeft / offsetTop对象与页面的边距
> event.clientX /
> event.clientY事件触发时鼠标指针对窗口的水平垂直坐标(event为时间)
//注意事项documentElement是整个节点树的根节点root即html标签document.body也是document能直接调用的属性标签
语法:
> object.offsetLeft/oobject.offsetTop
\>拖拽功能的实现
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51900581)
[copy](http://blog.csdn.net/estom_yin/article/details/51900581)
1. **\<html\>**
2. **\<head\>**
3. **\<meta** charset="UTF-8"**\>**
4. **\<title\>**event**\</title\>**
5. **\<style\>**
6. \#box {
7. width: 100px;
8. height: 100px;
9. background-color: aquamarine;
10. position: absolute;
11. }
12. **\</style\>**
13. **\</head\>**
14. **\<body\>**
15. **\<div** id="box"**\>\</div\>**
16. **\<script** type="text/javascript"**\>**
17. var oDiv = document.getElementById("box");
18. oDiv.onmousedown = function(ev){
19. var oEvent = ev;
20. var disX = oEvent.clientX-oDiv.offsetLeft;
21. var disY = oEvent.clientY-oDiv.offsetTop;
22. document.onmousemove = function(ev){
23. oEvent = ev;
24. oDiv.style.left = oEvent.clientX - disX +"px";
25. oDiv.style.top = oEvent.clientY - disY +"px";
26. }
27. document.onmouseup = function(){
28. document.onmousemove = null;
29. document.onmouseup = null;
30. }
31. }
32.
33. **\</script\>**
34. **\</body\>**
35. **\</html\>**

View File

@@ -0,0 +1,306 @@
\>父子节点
\>\>childNode
> 使用语法elementNode.childNodes
> 注意事项:空白节点会被浏览器但顾总文本节点
\>\>firstChild lastChild
> 使用语法node.firstChild node.lastChild
\>\>parentNode
> 使用语法:elementNode.parentNode
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<ul** id="father"**\>**
3. **\<li\>**大娃**\</li\>**
4. **\</ul\>**
5. **\<script** type="text/javascript"**\>**
6. var li_num = 0;
7. var childNodes = document.getElementById("father").childNodes;
8. for(var i = 0; i **\<** **childNodes.length**; i++){
9. document.write("节点名:" + childNodes[i].nodeName + " ");
10. document.write("节点类型:" + childNodes[i].nodeType + " ");
11. if(childNodes[i].nodeType == 1){
12. document.write("我是" + childNodes[i].innerHTML + "**\<br\>**");
13. li_num++;
14. }
15. else{
16. document.write("**\<br\>**");
17. console.log("这是一个空节点,不用理他");
18. }
19. }
20. document.write("子节点数目:" + childNodes.length + "**\<br\>**");
21. document.write("li 子节点数目:" + li_num + "**\<br\>**");
22. document.write("文本子节点数目:" + (childNodes.length - li_num));
23. **\</script\>**
24. **\</body\>\</span\>**
//补充节点的属性还有title
\>兄弟节点
> previousSibling nextSibling
> 使用语法:
> nodeobject.nextSibling / previousSibling
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<ul** id="father"**\>**
3. **\<li** title="force_max"**\>**大娃**\</li\>**
4. **\<li** id="second_children"**\>**二娃**\</li\>**
5. **\<li** title="fire"**\>**三娃**\</li\>**
6. **\</ul\>**
7. **\<script** type="text/javascript"**\>**
8. function getprenode(node){
9. var prenode = node.previousSibling;
10. while(prenode && prenode.nodeType != 1){
11. prenode = prenode.previousSibling;
12. }
13. return prenode;
14. }
15. function getnextnode(node){
16. var nextnode = node.nextSibling;
17. while(nextnode && nextnode.nodeType != 1){
18. nextnode =nextnode.nextSibling;
19. }
20. return nextnode;
21. }
22. var second_children = document.getElementById("second_children");
23. var first_children = getprenode(second_children);
24. var third_children = getnextnode(second_children);
25. alert(first\_children.innerHTML+first_children.title);
26. alert(third\_children.innerHTML+third_children.title);
27. **\</script\>**
28. **\</body\>\</span\>**
//虽然觉得这是史上最无聊的程序,但还是含泪贴上了
\>创建节点方法
> createElement('tagName'):创建节点
> crreateTextNode("text"):穿件文本节点
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>** var newinp =
document.createElement("input");
2. alert(newinp);
3. var newtext = document.createTextNode("text");
4. alert(newtext);**\</span\>**
\>添加删除节点
> nodeobject.appendChild(newnode):父节点末尾添加
> nodeobject.removeChild(node):删除节点
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<ul** id="father"**\>**
3. **\<li\>**大娃**\</li\>**
4. **\</ul\>**
5. **\<input** type="button" id="createbtn" value="祭出紫金葫芦"**\>**
6. **\<script** type="text/javascript"**\>**
7. function createnode(){
8. var btn = document.createElement("input");
9. btn.setAttribute("type", "button");
10. btn.setAttribute("name", "紫金葫芦");
11. btn.setAttribute("value", "吸进去");
12. btn.setAttribute("onclick", "removenode()");
13. document.body.appendChild(btn);
14. }
15. function removenode(){
16. var fnode = document.getElementById("father");
17. var nodes = fnode.childNodes;
18. for(var i = 0; i **\<** **nodes.length**; i++){
19. if(nodes[i] && nodes[i].nodeType == 1){
20. var rm = fnode.removeChild(nodes[i]);
21. rm = null;
22. break;
23. }
24. }
25. }
26. var createbtn = document.getElementById("createbtn");
27. createbtn.onclick = createnode;
28. **\</script\>**
29. **\</body\>\</span\>**
//有很多需要注意的地方,等吃饭回来补充
> appendChild()方法的主体必须使父节点,而且只能添加到节点对类的末尾
\>插入节点
> fnode.insertBefore(newnodenode)可以指定插如节点的位置在node之前返回值是插入的节点
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<ul** id="father"**\>**
3. **\<li\>**二娃**\</li\>**
4. **\</ul\>**
5. **\<input** type="button" id="add-btn" value="add"**\>**
6. **\<script** type="text/javascript"**\>**
7. function addnode(){
8. var fnode = document.getElementById("father");
9. var newnode = document.createElement("li");
10. newnode.innerHTML = "大娃";
11. fnode.insertBefore(newnode, fnode.childNodes[0]);
12. }
13. var add = document.getElementById("add-btn");
14. add.onclick = addnode;
15. **\</script\>**
16. **\</body\>\</span\>**
\>替换子节点(克隆替换)
> fonde.replaceChild(newnode, oldnode) //返回值是被替换的节点
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51899305)
[copy](http://blog.csdn.net/estom_yin/article/details/51899305)
1. **\<span** style="font-size:14px;"**\>\<body\>**
2. **\<ul** id="father"**\>**
3. **\<li** id="first"**\>**大娃**\</li\>**
4. **\<li\>**二娃**\</li\>**
5. **\</ul\>**
6. **\<input** type="button" id="replace-btn" value="replace"**\>**
7. **\<script** type="text/javascript"**\>**
8. function replacenode(){
9. var oldnode = document.getElementById("first");
10. var newnode = document.createElement("li");
11. newnode.innerHTML = "金刚葫芦娃";
12. oldnode.parentNode.replaceChild(newnode, oldnode);
13. }
14. var replace = document.getElementById("replace-btn");
15. replace.onclick = replacenode;
16. **\</script\>**
17. **\</body\>\</span\>**

View File

@@ -0,0 +1,155 @@
\>DOM树document_object_moudule
![724101446557.png](media/75813153285d5ceb04c812e98c3b33a1.png)
\>\>DOM定义
是w3c文档对象模型是中立于平台和语言的接口它允许程序和脚本动态的访问和更新文档内容、结构和样式。
\>\>DOM的组成
- 核心 DOM - 针对任何结构化文档的标准模型
- XML DOM - 针对 XML 文档的标准模型
- HTML DOM - 针对 HTML 文档的标准模型
\>DOM节点通过结点树的方法指明了各个元素之间的关系
\>\>文档节点、元素节点、文本节点、属性节点、注释节点
\>\>所有的结点都可以通过JS进行访问、所有的HTML元素节点都可以别修改也可以创建删除节点
\>\>节点关系
- 再节点树中,顶端点被称为根
- 每个节点都有父节点
- 一个节点可拥有任意数量的子节点
- 同胞是拥有相同父节点的结点
\>DOM方法通过定义类的对象的方法实现了访问结点
\>\>编程接口
可以通过JS等脚本语言对HTML_DOM进行访问
所有的HTML元素都被定义为对象而编程接口则是对象的方法和对象的属性
方法是能够执行的动作(添加或修改元素节点)
属性是能够获取或设置的值(节点的名称或内容)
\>\>常用的HTML_DOM方法和属性
方法:
访方法id,tagName,ClassName
修改方法修改内容、修改样式、修改属性、创建节点、删除节点。
修改内容
document.getElementById("p1").innerHTML="New text!";
修改样式
document.getElementById("p2").style.color="blue";
创建节点
var node=document.createTextNode("This is new.");
使用事件节点
\<input type="button" onclick="document.body.style.backgroundColor='lavender';"
value="Change background color" /\>
添加元素
para.appendChild(node);
element.insertBefore(para,child);
删除元素
parent.removeChild(child);
替换元素
parent.replaceChild(para,child);
| 方法 | 描述 |
|--------------------------|-----------------------------------------------------------------|
| getElementById() | 返回带有指定 ID 的元素。 |
| getElementsByTagName() | 返回包含带有指定标签名称的所有元素的节点列表(集合/节点数组)。 |
| getElementsByClassName() | 返回包含带有指定类名的所有元素的节点列表。 |
| appendChild() | 把新的子节点添加到指定节点。 |
| removeChild() | 删除子节点。 |
| replaceChild() | 替换子节点。 |
| insertBefore() | 在指定的子节点前面插入新的子节点。 |
| createAttribute() | 创建属性节点。 |
| createElement() | 创建元素节点。 |
| createTextNode() | 创建文本节点。 |
| getAttribute() | 返回指定的属性值。 |
| setAttribute() | 把指定属性设置或修改为指定的值。 |
属性:
innerHTML //元素节点的文本值
parentNode//元素节点的父节点
childNodes//元素节点的子节点
atrributes//元素节点的属性节点
nodeName属性规定节点的名称只读
nodeValue规定节点的值
nodeType属性返回节点的类型
| 元素类型 | NodeType |
|----------|----------|
| 元素 | 1 |
| 属性 | 2 |
| 文本 | 3 |
| 注释 | 8 |
| 文档 | 9 |
\>\>说一下自己的理解
在这里将html中的各个部分解析成节点的概念有助于通过结点树建立节点关系模型非常好而且简单。
然后有将html中各个部分定义成对象并且定义了对象的属性和方法能够很轻松的完成对元素的访问和操作。
提到对象联系C++中的知识,可以知道,对象,主要包括数据成员和成员函数,前者就是对象的属性,后者是对象的方法。
然后再说一下这里的DOM的理解文档对象模型。简单的说就是讲整个html文档中的所有元素定义成节点和对象的过程
并且通过这种定义简化并且系统的表达了对html的访问和操作。
\>DOM中相关的事件
.onclick=function(){displayDate()};
onclick="displayDate()"
onload="checkCookies()"
onchange="upperCase()"
onmouseover onmouseout
onmousedown onmouseup
\>DOM
var x = document.getElementsByTagName("p")
返回是节点数组
x.length能返回数组的长度

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,162 @@
**\>变量与数据类型**
\>\>定义JavaScript是一种脚本语言编程语言用来控制软件应用程序以文本ASCIIS形式保存在需要时被调用进行解释或编译广泛应用于客户端网页开发现在服务端也有应用NODEJS。动态、弱类型、基于原型的语言。像大多数编程语言一样有变量、类型、流程控制
\>\>变量名称规则字母下划线或美元符开头大小写敏感不允许使用js的关键字或者保留字作为文件名作为解释性语言的一大优势就是不用考虑变量类型提前分配孔家编译过程中分配空间就行
\>\>变量类型:整型,浮点型,字符型,字符串。
\>\>变量定义var 变量名;
感觉像是C的私生子有些地方不是那么严格更加灵活变通
**\>变量运算**
\>\>自增自减运算
\>\>简化运算。
\>\>字符串之间可以直接进行加法运算,表示连接。
\>\>字符串可以和其它类型的变量加法,表示转换成字符串类型,并连接。
\>数组
\>\>用于存放多个各种类型的数据,便于访问
\>\>数组的定义:
var arr = new array()//参数是数组长度
var arr = [a''b','c'];//可以直接使用数组内的元素定义
\>\>数组支持嵌套,多重数组(有点类似于存有多种数据的广义表)
\>\>数组一定有length属性arr.length等于键名中最大值加一。
\>\>数组的长度可以直接在定义数组的时候给出
\>\>数组的定义函数:参数可以是数组,也可以是变量初始化数组中的数据。
\>\>数组的长度可以在任意时候添加,不会出现越界,这就是解释性语言的好处。
**\>对象**
\>\>定义:带有自己的属性和方法的数据类型。含有多个键值对。
var o = {
p:"hello"
}
var 变量声明o 变量名称p 键名(属性名), hello 键值(属性值),
冒号分隔。数据对象的大括号包含,最后加分号。当键名不符合标识符的条件时,必须加引号
\>\>创建语句:
var ogj1 = {};
//大括号,只是声明了这是个对象,但不能说明这个对象属于哪一个类(类和类的对象的理念)
var obj2 = new Object(); //相当于调用对象的构造函数,然后形成一个新的对象
var obj3 = Object.create(null);//想当于调用一个已知对象的构造函数
\>\>对象的引用,如果不同的变量指向同一个对象,他们都称作这个对象的引用,也就是说这些对象指向同一个内存地址,修改其中一个变量的属性,会影响到其他的变量。
\>\>对象属性访的问方式: 对象名.对象的属性。objectName.propertyName //数据成员
\>\>对象方法的访问方式objectName.methodName(); //成员函数
\>时间类的对象定义:
var now = new Data(); //定义了一个时间对象now
now.setTime); // 设定时间
now.getTime(); //得到完整的时间
now.getFullYear(); //的到年份
now.getMonth(); //得到月份
now.getData(); //得到日期几号
now.getHours(); //得到小时
now.getMinutes(); //得到分钟
now.getSeconds(); //得到秒
now.getDay(); //星期
\>string类的 对象的使用
对象的定义:
var mystr = “i like javascript”;
var mystr = new String("some string");
对象的访问:
string.toUpperCase();
string.toLowerCase();
string.charAt(number); //返回指定的单个字符
string.indexOf(substring, startpos); //在字符串中寻找子串
string.split(separator, limit);
//将字符串分割为字符串数组,separator是分割符limit是分割次数
string.substring(startpos,stoppos);
//截取子串参数分别是起止下标终止与stop-1
string.substr(startpos, length);
//截取指定长度的子串,参数分别是起始值、子串长度
\>Math对象(本身就是一个对象而不是类)
\>\>对象成员的使用
Math.PI //圆周率
Math.abs() //绝对值
Math.ceil()/floor()/round() //分别是向上取整,向下取整,四舍五入。
random(); //返回0到1之间的随机数包含0不包含1
Math.min() / Math.max(); //返回指定数值中最低值
\>数组类的对象使用
\>\>数组对象的定义方法:
var 数组名 = new Array();
var 数组名 = new Array();
var 数组名 = [元素1元素2元素3,,,,,]
\>\>数组对象的使用
数组名[下标] = 值
\>\>数组对象的属性
arr.length //数组的长度
arr.concat(arr1, arr2,arr3.....)
//链接多个数组,不改变数组对象arr返回值是多个数组的连续
arr.join(separator); //separator是指分割符。
arr.reverse(); //倒序arr被改变
arr.slice(start, end);
//返回子数组不包含end负数表示从末尾开始想前数不修改原来的数组
arr.sort(方法函数); //如果不指定函数按Unicode编码的顺序排列

View File

@@ -0,0 +1,21 @@
\>css的三种样式的定义方法
\>\>标签选择使用html标签对html中所有标签起作用
.\>\>器类别选择器:.类名调用时class = “类名”。
\>\>ID选择器 \#id调用时id = “ID符号”
\>选择器的分类及关系
子代选择器:只能选择下一层的自带,使用\>来执行
后代选择器:也称包含选择器,选择特定元素或元素组的后代,中间用空格&nbsp隔开
伪类选择器ahoveralinkavisitedinputfocus
通用选择器:\*匹配所有的代码
群组选择器:,使用逗号隔开,相同的样式属性的几个元素
相邻同胞选择器:+表示与前者相邻的后者的格式

View File

@@ -0,0 +1,197 @@
\>浏览器对象
\>\>以浏览器为对象调用浏览器对象的属性和方法。如window.setinterval(clock,
100) and widow.navigator可以省略window。
\>\>函数的参数是另外一个函数时,只写另外一个函数的名字(待定)。
\>计时器
\>\>分类:
> 一次性计时器:尽在指定的延迟时间后触发一次行为
> 间隔性触发计时器:每隔一定时间触发一次
\>\>常用函数:
> setTimeout(代码行为, 延时时间):制定的毫秒数后调用函数或计算表达式
> clearTinmeout(时间设定函数的ID)取消有setTimeout()设置的timeout
> setIntervel(代码, 交互时间):按照指定的周期来调用函数或计算表达式
> 代码要调用的函数或执行的代码串行为交互时间周期性执行行为的时间返回值为函始时钟的id值。
> clearInterval(时间设定函数的ID)取消由setIntervel()设置的timeout
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51890683)
[copy](http://blog.csdn.net/estom_yin/article/details/51890683)
1. **\<span** style="font-size:18px;"**\>\<body\>**
2. **\<input** type="text" id="clock" size="50"**\>**
3. **\<script** type="text/javascript"**\>**
4. function clock(){
5. var time = new Date();
6. var attime = time.getHours() + ':' + time.getMinutes() + ':' +
time.getSeconds();
7. document.getElementById("clock").value = attime;
8.
9. }
10. setInterval(clock, 100);
11. **\</script\>**
12. **\</body\>\</span\>**
//实现了简单的时间及时输出
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51890683)
[copy](http://blog.csdn.net/estom_yin/article/details/51890683)
1. **\<span** style="font-size:18px;"**\>\<body\>**
2. **\<input** type="text" id="clock"**\>**
3. **\<input** type="button" id="istart" value="Start"**\>**
4. **\<input** type="button" id="istop" value="Stop"**\>**
5. **\<script** type="text/javascript"**\>**
6. var setid;
7. var time;
8. var attime;
9. function clock(){
10. time = new Date();
11. attime = time.getHours() + ":" + time.getMinutes() + ":" +
time.getSeconds();
12. document.getElementById("clock").value = attime;
13. }
14. var istart = document.getElementById("istart");
15. istart.onclick = function(){
16. setid = setInterval(clock, 100);
17. }
18. var istop = document.getElementById("istop");
19. istop.onclick = function(){
20. clearInterval(setid);
21. }
22. **\</script\>**
23. **\</body\>\</span\>**
根据setIntervel返回的id值取消时钟的设置。
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51890683)
[copy](http://blog.csdn.net/estom_yin/article/details/51890683)
1. **\<span** style="font-size:18px;"**\>\<body\>**
2. **\<input** type="text" id="inum"**\>**
3. **\<input** type="button" id="ialt" value="Start"**\>**
4. **\<script** type="text/javascript"**\>**
5. var inum = document.getElementById("inum");
6. var ialt = document.getElementById("ialt");
7. ialt.onclick = function () {
8. setTimeout("inum.value = 1", 1000);
9. setTimeout("inum.value = 2", 2000);
10. setTimeout("inum.value = 3", 3000);
11. setTimeout("inum.value = 4", 4000);
12. setTimeout("alert('666')", 5000);
13. }
14. **\</script\>**
15. **\</body\>\</span\>**
//实现了settimeout的函数的调用注意器低一个参数值可以是函数也可以是普通行为上述代码即为普通行为需要加引号而函数只需要写函数名没有参数的话可以不加括号。
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51890683)
[copy](http://blog.csdn.net/estom_yin/article/details/51890683)
1. **\<span** style="font-size:18px;"**\>\<body\>**
2. **\<input** type="text" id="count"**\>**
3. **\<input** type="button" id="istart" value="Start"**\>**
4. **\<input** type="button" id="istop" value="Stop"**\>**
5. **\<script** type="text/javascript"**\>**
6. var seti;
7. var num = 0;
8. function startCount(){
9. document.getElementById('count').value = num;
10. num = num + 1;
11. seti = setTimeout("startCount()", 1000);
12.
13. }
14. var istart = document.getElementById("istart");
15. istart.onclick = function (){
16. startCount();
17. }
18. var istop = document.getElementById("istop");
19. istop.onclick = function(){
20. clearTimeout(seti);
21. }
22. **\</script\>**
23. **\</body\>\</span\>**
//实现了按秒计数器,可以开始和结束。
PS有一个不明白的地方在代码执行的时候发现setIntervelclock1000setTimeout“startCount()”,
1000两个时间设定同样是调用两个函数前者直接使用了函数名而后者却有引号函数名和括号。为什么

View File

@@ -0,0 +1,39 @@
\>location对象
\>\>作用用于获取或设置窗体的URL并且可用于解析URL
\>\>语法location.[属性\|方法]
\>\>对象属性:
![713143941392.png](media/32ed381aeecf1527d86e15829f232641.png)
\>\>对象方法:
> assign():加载新的文档
> reload():重新加载当前文档
> replace():用心的文档替换当前的文档
\>navigator对象
\>\>作用:包含浏览器信息和操作系统版本
\>\>对象属性
![713145201241.png](media/0b65a5c473aad3b4addc35c7f1e79e89.png)
\>screen对象
\>\>作用:半酣了关于屏幕的信息,高度宽度、颜色深度
\>\>属性:
> screen.height返回屏幕分辨率的高
> screen.whidth返回屏幕分辨率的宽
> screen.availWidth屏幕的宽度
> screen.availHeight屏幕宽度

View File

@@ -0,0 +1,61 @@
\>程序控制结构简写
\>\>if-else条件判断选择
if判断条件{分支1}
else {分支2}
\>\>switch多项选择结构注意关键字default、break
switch(选择变量){
case 1:分支1;break;
case 2:分支2;break;
case 3:分支3break;
default:分支4
}
\>\>for(初始化;循环条件;循环控制){
循环体;
}
\>\>while(判断语句){循环体}
\>\>关键字continue(退出本次循环开始新的循环)
\>\>关键字break退出整个循环
\>JS中的函数
\>\>函数的定义:
function fun(){函数体}
\>\>函数调用
函数名(实际参数);
\>\>参数传递
可以传递任意数量的参数, 不用声明参数类型
\>\>函数返回值
可以返回任意类型的返回值。
\>\>函数声明,是函数在任何地方都可以定义
\>\>函数作用域,全局函数,对象的函数
\>\>this函数体中this说明当前函数方法的作用域。可以是全局作用域可以是整个windows对象可能用于指向函数外部的变量。
\>\>call(this的指向 变量)可以改变当前函数中this的指向
\>\>apply(this的指向数组)改变当前函数中this的指向

View File

@@ -0,0 +1,482 @@
\>JS与HTML的关联
**\>\>**内部关联:
> \<script type = "text/javascript"\>
> javascript 语言
> \</script\>
**\>\>**位置对页面初始化的JS必须放在开头。
> 通过事件调用执行的JS对位置没有特殊要求
**\>\>**外部关联:
> 定义一个.js文件
> \<script src = "script.js"\>
> \</script\>
\>JS与事件绑定
**\>\>**间接绑定(函数名绑定):元素.事件 = 函数名;
> 将想要出发的行为写在一个函数里面,然后再将这个函数名赋值给元素的事件属性。
> 在这里共提到三个名词:
> 元素——指网页上的一个时间单位
> 事件——指发生在元素身上的事(作为元素的一个属性)
> 行为——指发生时间后带来的动作后果(写在了函数里)
> function func(){}
> btn.onclick = func;
**\>\>**直接绑定(匿名函数绑定):元素.事件 = function{函数体}
> 将想要出发的行为,直接作为事件的函数。
> btn.onclick = function(){}
**\>\>**前者适合单一行为的绑定,后者适合多行为绑定和带参数函数行为的绑定。
\>JS对网页的操作
**\>\>**document.write()//直接向HTML中写内容
**\>\>**alert字符串变量//弹窗,在点击“确定”按钮前不能进行其他任何操作。通常用于调试程序。
**\>\>**doucument.getElementById("id_value")//通过id获取元素具有唯一性,id_value为id属性的值
> 简单说就是通过函数获取某个元素的id赋值给JS自身的变量然后对这个id的变量进行操作就相当于对HTML标签进行操作。
**\>\>**doucument.getElementsByTagName(TagName)//返回带有制定标签名的元素对象的集合一个数组。返回元素的顺序使他们在文档中出现的顺序。TagName,是HTML标签。
**\>\>**id.innerHTML 或 TagName.innerName//可以返回显示标签内部的内容。
**\>\>**id.value 或 TagName.value//访问input的标签的值
\>鼠标事件
**\>\>**元素.onclick = 函数名鼠标单击事件某个元素的onclick属性。
**[html]** [view plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. **\<span** style="font-size:18px;"**\>\<body\>**
2. **\<p** id="pid"**\>**JavaScript**\</p\>**
3. **\<button** id="btn"**\>**click**\</button\>**
4. **\<script** type="text/javascript"**\>**
5. var btn = document.getElementById("btn");
6. btn.onclick = hello;
7. function hello(){
8. var pid = document.getElementById("pid");
9. pid.innerHTML = "Hello!";
10. }
11. **\</script\>**
12. **\</body\>\</span\>**
//有一个botton对象id为btn这是元素
//修改btn.onclick属性为函数hello这是触发事件与行为的联系。
//函数hello执行的时候将id为pid的标签内容修改这是行为
**\>\>**鼠标动作
> onclick 鼠标点击
> onmousedown 鼠标按下
> onmouseup 鼠标松开
> onmouseover 鼠标悬停
> onmouseout 鼠标离开
> onmouseup & onmousedown
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<p id="pid"\>JavaScript\</p\>
3. \<button id="btn"\>click\</button\>
4. \<script type="text/javascript"\>
5. var btn = document.getElementById("btn");
6. btn.onmousedown = down;
7. btn.onmouseup = up;
8. function up() {
9. var pid = document.getElementById("pid");
10. pid.innerHTML = "up";
11. }
12. function down() {
13. var pid = document.getElementById("pid");
14. pid.innerHTML = "down";
15. }
16. \</script\>
17. \</body\>\</span\>
> onmouseover & onmouseout
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<p id="pid"\>JavaScript\</p\>
3. \<button id="btn"\>click\</button\>
4. \<script type="text/javascript"\>
5. var btn = document.getElementById("btn");
6. btn.onmouseover = over;
7. btn.onmouseout = out;
8. function over() {
9. var pid = document.getElementById("pid");
10. pid.innerHTML = "over";
11. }
12. function out() {
13. var pid = document.getElementById("pid");
14. pid.innerHTML = "out";
15. }
16. \</script\>
17. \</body\>\</span\>
\>文本框事件
> onfocus 输入框聚焦
> onblur 输入框模糊
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<p id="pid"\>JavaScript\</p\>
3. \<input id="input"\>
4. \<script type="text/javascript"\>
5. var inp = document.getElementById("input");
6. inp.onfocus = focus;
7. inp.onblur = blur;
8. function focus() {
9. var pid = document.getElementById("pid");
10. pid.innerHTML = "focus";
11. }
12. function blur() {
13. var pid = document.getElementById("pid");
14. pid.innerHTML = "blur";
15. }
16. \</script\>
17. \</body\>\</span\>
onselect文本框被选中
onchange 改变文本框的内容出发事件
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<p id="pid"\>JavaScript\</p\>
3. \<input id="input" value="change me\~"\>
4. \<script type="text/javascript"\>
5. var inp = document.getElementById("input");
6. inp.onselect = select;
7. inp.onchange = change;
8. function select() {
9. var pid = document.getElementById("pid");
10. pid.innerHTML = "select";
11. }
12. function change() {
13. var pid = document.getElementById("pid");
14. pid.innerHTML = "change";
15. }
16. \</script\>
17. \</body\>\</span\>
**\>\>**确认框
> 语法confirmstr
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<input id="input" type="button" value="点击我"\>
3. \<script type="text/javascript"\>
4. function rec(){
5. var message = confirm("Do you like me?");
6. **if**(message == true)
7. {
8. document.write("么么哒!\~");
9. }
10. **else**
11. {
12. document.write("泥奏凯!");
13. }
14. }
15. var inp = document.getElementById("input");
16. inp.onclick = rec;
17. \</script\>
18. \</body\>\</span\>
**\>\>**提问框
> 语法prompt(str1,str2)
> 说明str1显示在消息对话框中不可修改str2显示在文本框中的默认内容可修改
> 返回值:确定-\>返回文本框中输入的内容;取消-\>返回null
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<input id="input" type="button" value="点击我"\>
3. \<script type="text/javascript"\>
4. function rec(){
5. var score; // score变量用来存储用户输入的成绩值。
6. score = prompt("Your score?");
7. **if**(score \>= 90)
8. {
9. document.write("你很棒!");
10. }
11. **else** **if**(score \>= 75)
12. {
13. document.write("不错哦!");
14. }
15. **else** **if**(score \>= 60)
16. {
17. document.write("要加油!");
18. }
19. **else**
20. {
21. document.write("要努力了哦!");
22. }
23. }
24. var inp = document.getElementById("input");
25. inp.onclick = rec;
26. \</script\>
27. \</body\>\</span\>
\>网页的属性设置
**\>\>**element.getAttribute(name)//element是待设定元素对象id或者标签名name是属性的名字。
**\>\>**element.setAttribute(name,value)//element是id或者标签的名字待设定元素对象name是属性名value是新的属性值
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<p id="pid" title="JavaScript"\>JavaScript\</p\>
3. \<button id="btn"\>click\</button\>
4. \<script type="text/javascript"\>
5. var btn = document.getElementById("btn");
6. btn.onclick = attr;
7. function attr() {
8. var pid = document.getElementById("pid");
9. alert(pid.getAttribute("id"));
10. alert(pid.getAttribute("title"));
11. pid.setAttribute("title", "hello");
12. alert(pid.getAttribute("title"));
13. }
14. \</script\>
15. \</body\>\</span\>
**\>\>**window.open(URL,\<窗口方式\>\<参数字符串\>)//URL打开的网址路径打开方式可以是"_top",
"_blank", "_self"。
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<body\>
2. \<input id="input" type="button" value="点击我,打开新窗口!"\>
3. \<script type="text/javascript"\>
4. function windowOpen(){
5. window.open('http://www.jisuanke.com', '_blank', 'width = 600, height = 400,
top = 100, left = 0');
6. }
7. var inp = document.getElementById("input");
8. inp.onclick = windowOpen;
9. \</script\>
10. \</body\>\</span\>
**\>\>**windows.onload 当页面加载或者刷新时会出现相应的函数。
**[python]** [view
plain](http://blog.csdn.net/estom_yin/article/details/51888110)
[copy](http://blog.csdn.net/estom_yin/article/details/51888110)
1. \<span style="font-size:18px;"\>\<head\>
2. \<meta charset="UTF-8"\>
3. \<title\>load\</title\>
4. \<script type="text/javascript"\>
5. window.onload = load;
6. function load() {
7. alert("load");
8. }
9. \</script\>
10. \</head\>\</span\>

View File

@@ -0,0 +1,138 @@
Bellman-Ford 单源最短路径算法
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径SSSPSingle-Source
Shortest Path的算法。该算法由 Richard Bellman 和 Lester Ford 分别发表于 1958
年和 1956 年,而实际上 Edward F. Moore 也在 1957
年发布了相同的算法,因此,此算法也常被称为 Bellman-Ford-Moore 算法。
Bellman-Ford 算法和 [Dijkstra
算法](http://www.cnblogs.com/gaochundong/p/dijkstra_algorithm.html)同为解决单源最短路径的算法。对于带权有向图
G = (V, E)Dijkstra 算法要求图 G 中边的权值均为非负,而 [Bellman-Ford
算法](http://www.cnblogs.com/gaochundong/p/bellman_ford_algorithm.html)能适应一般的情况(即存在负权边的情况)。一个实现的很好的
Dijkstra 算法比 Bellman-Ford 算法的运行时间要低。
Bellman-Ford 算法采用动态规划Dynamic Programming进行设计实现的时间复杂度为
O(V\*E),其中 V 为顶点数量E 为边的数量。Dijkstra 算法采用贪心算法Greedy
Algorithm范式进行设计普通实现的时间复杂度为 O(V2),若基于 [Fibonacci
heap](http://www.cnblogs.com/gaochundong/p/fibonacci_heap.html)
的最小优先队列实现版本则时间复杂度为 O(E + VlogV)。
Bellman-Ford 算法描述:
1. 创建源顶点 v 到图中所有顶点的距离的集合
distSet为图中的所有顶点指定一个距离值初始均为 Infinite源顶点距离为 0
2. 计算最短路径,执行 V - 1 次遍历;
- 对于图中的每条边:如果起点 u 的距离 d 加上边的权值 w 小于终点 v 的距离
d则更新终点 v 的距离值 d
3. 检测图中是否有负权边形成了环,遍历图中的所有边,计算 u 至 v 的距离,如果对于
v 存在更小的距离,则说明存在环;
伪码实现如下:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 BELLMAN-FORD(G, w, s) 2 INITIALIZE-SINGLE-SOURCE(G, s) 3 for i 1 to \|V[G]\| -
14 do for each edge (u, v) E[G] 5 do RELAX(u, v, w) 6 for each edge (u, v) E[G]
7 do if d[v] \> d[u] + w(u, v) 8 then return FALSE 9 return TRUE
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
Bellman-Ford 算法的运行时间为 O(V\*E),因为第 2 行的初始化占用了 Θ(V),第 3-4
行对边进行了 V - 1 趟操作,每趟操作的运行时间为 Θ(E)。第 6-7 行的 for
循环运行时间为 O(E)。
例如,下面的有向图 G 中包含 5 个顶点和 8 条边。假设源点 为 A。初始化 distSet
所有距离为 INFI源点 A 为 0。
![147026756939.png](media/6b2a27583131a012247ddb212cddcf8b.png)
由于图中有 5 个顶点,按照步骤 1 需要遍历 4 次,第一次遍历的结果如下。
![150096287121.png](media/0c06f40a0719bebffc78d45c50d62e87.png)
第二次遍历的结果如下。
![151019728645.png](media/45f694182b0df6b3ff26904265f1d5ac.png)
以此类推可以得出完全遍历的结果。
C\# 代码实现:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5
namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void
Main(string[] args) 10 { 11 int[,] graph = new int[9, 9] 12 { 13 {0, 4, 0, 0, 0,
0, 0, 8, 0}, 14 {4, 0, 8, 0, 0, 0, 0, 11, 0}, 15 {0, 8, 0, 7, 0, 4, 0, 0, 2}, 16
{0, 0, 7, 0, 9, 14, 0, 0, 0}, 17 {0, 0, 0, 9, 0, 10, 0, 0, 0}, 18 {0, 0, 4, 0,
10, 0, 2, 0, 0}, 19 {0, 0, 0, 14, 0, 2, 0, 1, 6}, 20 {8, 11, 0, 0, 0, 0, 1, 0,
7}, 21 {0, 0, 2, 0, 0, 0, 6, 7, 0} 22 }; 23 24 Graph g = new
Graph(graph.GetLength(0)); 25 for (int i = 0; i \< graph.GetLength(0); i++) 26 {
27 for (int j = 0; j \< graph.GetLength(1); j++) 28 { 29 if (graph[i, j] \> 0)
30 g.AddEdge(i, j, graph[i, j]); 31 } 32 } 33 34 Console.WriteLine("Graph Vertex
Count : {0}", g.VertexCount); 35 Console.WriteLine("Graph Edge Count : {0}",
g.EdgeCount); 36 Console.WriteLine(); 37 38 int[] distSet = g.BellmanFord(0); 39
Console.WriteLine("Vertex\\t\\tDistance from Source"); 40 for (int i = 0; i \<
distSet.Length; i++) 41 { 42 Console.WriteLine("{0}\\t\\t{1}", i, distSet[i]);
43 } 44 45 // build a directed and negative weighted graph 46 Graph
directedGraph = new Graph(5); 47 directedGraph.AddEdge(0, 1, -1); 48
directedGraph.AddEdge(0, 2, 4); 49 directedGraph.AddEdge(1, 2, 3); 50
directedGraph.AddEdge(1, 3, 2); 51 directedGraph.AddEdge(1, 4, 2); 52
directedGraph.AddEdge(3, 2, 5); 53 directedGraph.AddEdge(3, 1, 1); 54
directedGraph.AddEdge(4, 3, -3); 55 56 Console.WriteLine(); 57
Console.WriteLine("Graph Vertex Count : {0}", directedGraph.VertexCount); 58
Console.WriteLine("Graph Edge Count : {0}", directedGraph.EdgeCount); 59
Console.WriteLine(); 60 61 int[] distSet1 = directedGraph.BellmanFord(0); 62
Console.WriteLine("Vertex\\t\\tDistance from Source"); 63 for (int i = 0; i \<
distSet1.Length; i++) 64 { 65 Console.WriteLine("{0}\\t\\t{1}", i, distSet1[i]);
66 } 67 68 Console.ReadKey(); 69 } 70 71 class Edge 72 { 73 public Edge(int
begin, int end, int weight) 74 { 75 this.Begin = begin; 76 this.End = end; 77
this.Weight = weight; 78 } 79 80 public int Begin { get; private set; } 81
public int End { get; private set; } 82 public int Weight { get; private set; }
83 84 public override string ToString() 85 { 86 return string.Format( 87
"Begin[{0}], End[{1}], Weight[{2}]", 88 Begin, End, Weight); 89 } 90 } 91 92
class Graph 93 { 94 private Dictionary\<int, List\<Edge\>\> \_adjacentEdges 95 =
new Dictionary\<int, List\<Edge\>\>(); 96 97 public Graph(int vertexCount) 98 {
99 this.VertexCount = vertexCount;100 } 101 102 public int VertexCount { get;
private set; }103 104 public int EdgeCount 105 { 106 get107 { 108 return
\_adjacentEdges.Values.SelectMany(e =\> e).Count(); 109 } 110 } 111 112 public
void AddEdge(int begin, int end, int weight) 113 { 114 if
(!\_adjacentEdges.ContainsKey(begin)) 115 { 116 var edges = new List\<Edge\>();
117 \_adjacentEdges.Add(begin, edges); 118 } 119 120
\_adjacentEdges[begin].Add(new Edge(begin, end, weight)); 121 } 122 123 public
int[] BellmanFord(int source) 124 { 125 // distSet[i] will hold the shortest
distance from source to i126 int[] distSet = new int[VertexCount];127 128 //
Step 1: Initialize distances from source to all other vertices as INFINITE129
for (int i = 0; i \< VertexCount; i++)130 { 131 distSet[i] = int.MaxValue;132 }
133 distSet[source] = 0;134 135 // Step 2: Relax all edges \|V\| - 1 times. A
simple shortest path from source136 // to any other vertex can have at-most
\|V\| - 1 edges137 for (int i = 1; i \<= VertexCount - 1; i++)138 { 139 foreach
(var edge in \_adjacentEdges.Values.SelectMany(e =\> e)) 140 { 141 int u =
edge.Begin; 142 int v = edge.End; 143 int weight = edge.Weight; 144 145 if
(distSet[u] != int.MaxValue146 && distSet[u] + weight \< distSet[v]) 147 { 148
distSet[v] = distSet[u] + weight; 149 } 150 } 151 } 152 153 // Step 3: check for
negative-weight cycles. The above step guarantees154 // shortest distances if
graph doesn't contain negative weight cycle.155 // If we get a shorter path,
then there is a cycle.156 foreach (var edge in
\_adjacentEdges.Values.SelectMany(e =\> e)) 157 { 158 int u = edge.Begin; 159
int v = edge.End; 160 int weight = edge.Weight; 161 162 if (distSet[u] !=
int.MaxValue163 && distSet[u] + weight \< distSet[v]) 164 { 165
Console.WriteLine("Graph contains negative weight cycle.");166 } 167 } 168 169
return distSet; 170 } 171 } 172 } 173 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
运行结果如下:
![145066285953.png](media/028d88e6512c431a0680a0571d7a5675.png)

View File

@@ -0,0 +1,196 @@
Dijkstra 单源最短路径算法
Dijkstra 算法是一种用于计算带权有向图中单源最短路径SSSPSingle-Source
Shortest Path的算法由计算机科学家 Edsger Dijkstra 于 1956 年构思并于 1959
年发表。其解决的问题是:给定图 G 和源顶点 v找到从 v 至图中所有顶点的最短路径。
![623171289908.gif](media/da34421ea01b4c263b5bba06e67b4c39.gif)
Dijkstra 算法采用贪心算法Greedy
Algorithm范式进行设计。在最短路径问题中对于带权有向图 G = (V, E)Dijkstra
算法的初始实现版本未使用最小优先队列实现,其时间复杂度为 O(V2),基于 [Fibonacci
heap](http://www.cnblogs.com/gaochundong/p/fibonacci_heap.html)
的最小优先队列实现版本,其时间复杂度为 O(E + VlogV)。
![202145193445.png](media/e94c6e5fc490453f5a2d31e744227bcc.png)
[Bellman-Ford
算法](http://www.cnblogs.com/gaochundong/p/bellman_ford_algorithm.html)和
[Dijkstra
算法](http://www.cnblogs.com/gaochundong/p/dijkstra_algorithm.html)同为解决单源最短路径的算法。对于带权有向图
G = (V, E)Dijkstra 算法要求图 G 中边的权值均为非负,而 Bellman-Ford
算法能适应一般的情况(即存在负权边的情况)。一个实现的很好的 Dijkstra 算法比
Bellman-Ford 算法的运行时间 O(V\*E) 要低。
Dijkstra 算法描述:
1. 创建源顶点 v 到图中所有顶点的距离的集合
distSet为图中的所有顶点指定一个距离值初始均为 Infinite源顶点距离为 0
2. 创建 SPTShortest Path Tree集合 sptSet用于存放包含在 SPT 中的顶点;
3. 如果 sptSet 中并没有包含所有的顶点,则:
- 选中不包含在 sptSet 中的顶点 uu 为当前 sptSet 中未确认的最短距离顶点;
- 将 u 包含进 sptSet
- 更新 u 的所有邻接顶点的距离值;
伪码实现如下:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 function Dijkstra(Graph, source): 2 3 dist[source] ← 0 // Distance from source
to source 4 prev[source] ← undefined // Previous node in optimal path
initialization 5 6 for each vertex v in Graph: // Initialization 7 if v ≠ source
// Where v has not yet been removed from Q (unvisited nodes) 8 dist[v] ←
infinity // Unknown distance function from source to v 9 prev[v] ← undefined //
Previous node in optimal path from source 10 end if 11 add v to Q // All nodes
initially in Q (unvisited nodes) 12 end for13 14 while Q is not empty: 15 u ←
vertex in Q with min dist[u] // Source node in first case16 remove u from Q 17
18 for each neighbor v of u: // where v has not yet been removed from Q. 19 alt
← dist[u] + length(u, v) 20 if alt \< dist[v]: // A shorter path to v has been
found 21 dist[v] ← alt 22 prev[v] ← u 23 end if24 end for25 end while26 27
return dist[], prev[] 28 29 end function
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
例如,下面是一个包含 9 个顶点的图,每条边分别标识了距离。
![25024569786.jpeg](media/a3460bbcc926be103e406d581ac4612a.jpeg)
源顶点 source = 0初始时
- sptSet = {false, false, false, false, false, false, false, false, false};
- distSet = {**0**, INF, INF, INF, INF, INF, INF, INF, INF};
将 0 包含至 sptSet 中;
- sptSet = {**true**, false, false, false, false, false, false, false, false};
更新 0 至其邻接节点的距离;
- distSet = {**0**, **4**, INF, INF, INF, INF, INF, **8**, INF};
![31318781591.jpeg](media/1163371b1fee9f8427fd967986a18f51.jpeg)
选择不在 sptSet 中的 Min Distance 的顶点,为顶点 1则将 1 包含至 sptSet
- sptSet = {**true**, **true**, false, false, false, false, false, false,
false};
更新 1 至其邻接节点的距离;
- distSet = {**0**, **4**, **12**, INF, INF, INF, INF, **8**, INF};
![35078621307.jpeg](media/da54c513dbc3845d6ba4009044bb16b7.jpeg)
选择不在 sptSet 中的 Min Distance 的顶点,为顶点 7则将 7 包含至 sptSet
- sptSet = {**true**, **true**, false, false, false, false, false, **true**,
false};
更新 7 至其邻接节点的距离;
- distSet = {**0**, **4**, **12**, INF, INF, INF, **9**, **8**, **15**};
![37061284639.jpeg](media/804412a0012e45a685217d5fd579fe61.jpeg)
选择不在 sptSet 中的 Min Distance 的顶点,为顶点 6则将 6 包含至 sptSet
- sptSet = {**true**, **true**, false, false, false, false, **true**,
**true**, false};
更新 6 至其邻接节点的距离;
- distSet = {**0**, **4**, **12**, INF, INF, **11**, **9**, **8**, **15**};
![38430509082.jpeg](media/c0fd7c31e5058884c3d218930d076182.jpeg)
以此类推,直到遍历结束。
- sptSet = {**true**, **true**, **true**, **true**, **true**, **true**,
**true**, **true**, **true**};
- distSet = {**0**, **4**, **12**, **19**, **21**, **11**, **9**, **8**,
**14**};
![40391919096.jpeg](media/14d56ddfaf928586b3fb2cfdbbd5ec94.jpeg)
最终结果为源顶点 0 至所有顶点的距离:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
Vertex Distance from Source 0 0 1 4 2 12 3 19 4 21 5 11 6 9 7 8 8 14
复制代码
C\#代码实现:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5
namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void
Main(string[] args) 10 { 11 int[,] graph = new int[9, 9] 12 { 13 {0, 4, 0, 0, 0,
0, 0, 8, 0}, 14 {4, 0, 8, 0, 0, 0, 0, 11, 0}, 15 {0, 8, 0, 7, 0, 4, 0, 0, 2}, 16
{0, 0, 7, 0, 9, 14, 0, 0, 0}, 17 {0, 0, 0, 9, 0, 10, 0, 0, 0}, 18 {0, 0, 4, 0,
10, 0, 2, 0, 0}, 19 {0, 0, 0, 14, 0, 2, 0, 1, 6}, 20 {8, 11, 0, 0, 0, 0, 1, 0,
7}, 21 {0, 0, 2, 0, 0, 0, 6, 7, 0} 22 }; 23 24 Graph g = new
Graph(graph.GetLength(0)); 25 for (int i = 0; i \< graph.GetLength(0); i++) 26 {
27 for (int j = 0; j \< graph.GetLength(1); j++) 28 { 29 if (graph[i, j] \> 0)
30 g.AddEdge(i, j, graph[i, j]); 31 } 32 } 33 34 int[] dist = g.Dijkstra(0); 35
Console.WriteLine("Vertex\\t\\tDistance from Source"); 36 for (int i = 0; i \<
dist.Length; i++) 37 { 38 Console.WriteLine("{0}\\t\\t{1}", i, dist[i]); 39 } 40
41 Console.ReadKey(); 42 } 43 44 class Edge 45 { 46 public Edge(int begin, int
end, int distance) 47 { 48 this.Begin = begin; 49 this.End = end; 50
this.Distance = distance; 51 } 52 53 public int Begin { get; private set; } 54
public int End { get; private set; } 55 public int Distance { get; private set;
} 56 } 57 58 class Graph 59 { 60 private Dictionary\<int, List\<Edge\>\>
\_adjacentEdges 61 = new Dictionary\<int, List\<Edge\>\>(); 62 63 public
Graph(int vertexCount) 64 { 65 this.VertexCount = vertexCount; 66 } 67 68 public
int VertexCount { get; private set; } 69 70 public void AddEdge(int begin, int
end, int distance) 71 { 72 if (!\_adjacentEdges.ContainsKey(begin)) 73 { 74 var
edges = new List\<Edge\>(); 75 \_adjacentEdges.Add(begin, edges); 76 } 77 78
\_adjacentEdges[begin].Add(new Edge(begin, end, distance)); 79 } 80 81 public
int[] Dijkstra(int source) 82 { 83 // dist[i] will hold the shortest distance
from source to i 84 int[] distSet = new int[VertexCount]; 85 86 // sptSet[i]
will true if vertex i is included in shortest 87 // path tree or shortest
distance from source to i is finalized 88 bool[] sptSet = new bool[VertexCount];
89 90 // initialize all distances as INFINITE and stpSet[] as false 91 for (int
i = 0; i \< VertexCount; i++) 92 { 93 distSet[i] = int.MaxValue; 94 sptSet[i] =
false; 95 } 96 97 // distance of source vertex from itself is always 0 98
distSet[source] = 0; 99 100 // find shortest path for all vertices101 for (int i
= 0; i \< VertexCount - 1; i++)102 { 103 // pick the minimum distance vertex
from the set of vertices not104 // yet processed. u is always equal to source in
first iteration.105 int u = CalculateMinDistance(distSet, sptSet); 106 107 //
mark the picked vertex as processed108 sptSet[u] = true;109 110 // update dist
value of the adjacent vertices of the picked vertex.111 for (int v = 0; v \<
VertexCount; v++)112 { 113 // update dist[v] only if is not in sptSet, there is
an edge from114 // u to v, and total weight of path from source to v through u
is115 // smaller than current value of dist[v]116 if (!sptSet[v] 117 &&
distSet[u] != int.MaxValue118 && \_adjacentEdges[u].Exists(e =\> e.End == v))
119 { 120 int d = \_adjacentEdges[u].Single(e =\> e.End == v).Distance; 121 if
(distSet[u] + d \< distSet[v]) 122 { 123 distSet[v] = distSet[u] + d; 124 } 125
} 126 } 127 } 128 129 return distSet; 130 } 131 132 /// \<summary\>133 /// A
utility function to find the vertex with minimum distance value, 134 /// from
the set of vertices not yet included in shortest path tree 135 ///
\</summary\>136 private int CalculateMinDistance(int[] distSet, bool[]
sptSet)137 { 138 int minDistance = int.MaxValue;139 int minDistanceIndex =
\-1;140 141 for (int v = 0; v \< VertexCount; v++)142 { 143 if (!sptSet[v] &&
distSet[v] \<= minDistance) 144 { 145 minDistance = distSet[v]; 146
minDistanceIndex = v; 147 } 148 } 149 150 return minDistanceIndex; 151 } 152 }
153 } 154 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码

View File

@@ -0,0 +1,135 @@
Floyd-Warshall 全源最短路径算法
Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E)
上每对顶点间的最短路径问题即全源最短路径问题All-Pairs Shortest Paths
Problem其中图 G 允许存在权值为负的边但不存在权值为负的回路。Floyd-Warshall
算法的运行时间为 Θ(V3)。
Floyd-Warshall 算法由 Robert Floyd 于 1962 年提出,但其实质上与 Bernad Roy 于
1959 年和 Stephen Warshall 于 1962 年提出的算法相同。
解决单源最短路径问题的方案有 [Dijkstra
算法](http://www.cnblogs.com/gaochundong/p/dijkstra_algorithm.html)和
[Bellman-Ford
算法](http://www.cnblogs.com/gaochundong/p/bellman_ford_algorithm.html)对于全源最短路径问题可以认为是单源最短路径问题Single
Source Shortest Paths
Problem的推广即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。更通用的全源最短路径算法包括
- 针对稠密图的 Floyd-Warshall 算法:时间复杂度为 O(V3)
- 针对稀疏图的 Johnson 算法:时间复杂度为 O(V2logV + VE)
最短路径算法中的最优子结构指的是两顶点之间的最短路径包括路径上其它顶点的最短路径。具体描述为:对于给定的带权图
G = (V, E),设 p = \<v1, v2, …,vk\> 是从 v1 到 vk 的最短路径,那么对于任意 i 和
j1 ≤ i ≤ j ≤ kpij = \<vi, vi+1, …, vj\> 为 p 中顶点 vi 到 vj 的子路径,那么
pij 是顶点 vi 到 vj 的最短路径。
![519358786181.png](media/9fd60a2c349a5f3382a3ac71344da53d.png)
Floyd-Warshall 算法的设计基于了如下观察。设带权图 G = (V, E) 中的所有顶点 V =
{1, 2, . . . , n},考虑一个顶点子集 {1, 2, . . . , k}。对于任意对顶点 i,
j考虑从顶点 i 到 j 的所有路径的中间顶点都来自该子集 {1, 2, . . . , k},设 p
是该子集中的最短路径。Floyd-Warshall 算法描述了 p 与 i, j
间最短路径及中间顶点集合 {1, 2, . . . , k - 1} 的关系,该关系依赖于 k 是否是路径
p 上的一个中间顶点。
![440255348005.gif](media/3f6982193bf382027415cc1b54adaa97.gif)
算法伪码如下:
![445190814552.png](media/7909dda678c34ea139bf725689e58761.png)
最短路径算法的设计都使用了松弛relaxation技术。在算法开始时只知道图中边的权值然后随着处理逐渐得到各对顶点的最短路径的信息算法会逐渐更新这些信息每步都会检查是否可以找到一条路径比当前已有路径更短这一过程通常称为松弛relaxation
C\# 代码实现:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5
namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void
Main(string[] args) 10 { 11 int[,] graph = new int[9, 9] 12 { 13 {0, 4, 0, 0, 0,
0, 0, 8, 0}, 14 {4, 0, 8, 0, 0, 0, 0, 11, 0}, 15 {0, 8, 0, 7, 0, 4, 0, 0, 2}, 16
{0, 0, 7, 0, 9, 14, 0, 0, 0}, 17 {0, 0, 0, 9, 0, 10, 0, 0, 0}, 18 {0, 0, 4, 0,
10, 0, 2, 0, 0}, 19 {0, 0, 0, 14, 0, 2, 0, 1, 6}, 20 {8, 11, 0, 0, 0, 0, 1, 0,
7}, 21 {0, 0, 2, 0, 0, 0, 6, 7, 0} 22 }; 23 24 Graph g = new
Graph(graph.GetLength(0)); 25 for (int i = 0; i \< graph.GetLength(0); i++) 26 {
27 for (int j = 0; j \< graph.GetLength(1); j++) 28 { 29 if (graph[i, j] \> 0)
30 g.AddEdge(i, j, graph[i, j]); 31 } 32 } 33 34 Console.WriteLine("Graph Vertex
Count : {0}", g.VertexCount); 35 Console.WriteLine("Graph Edge Count : {0}",
g.EdgeCount); 36 Console.WriteLine(); 37 38 int[,] distSet = g.FloydWarshell();
39 PrintSolution(g, distSet); 40 41 // build a directed and negative weighted
graph 42 Graph directedGraph1 = new Graph(5); 43 directedGraph1.AddEdge(0, 1,
\-1); 44 directedGraph1.AddEdge(0, 2, 4); 45 directedGraph1.AddEdge(1, 2, 3); 46
directedGraph1.AddEdge(1, 3, 2); 47 directedGraph1.AddEdge(1, 4, 2); 48
directedGraph1.AddEdge(3, 2, 5); 49 directedGraph1.AddEdge(3, 1, 1); 50
directedGraph1.AddEdge(4, 3, -3); 51 52 Console.WriteLine(); 53
Console.WriteLine("Graph Vertex Count : {0}", directedGraph1.VertexCount); 54
Console.WriteLine("Graph Edge Count : {0}", directedGraph1.EdgeCount); 55
Console.WriteLine(); 56 57 int[,] distSet1 = directedGraph1.FloydWarshell(); 58
PrintSolution(directedGraph1, distSet1); 59 60 // build a directed and positive
weighted graph 61 Graph directedGraph2 = new Graph(4); 62
directedGraph2.AddEdge(0, 1, 5); 63 directedGraph2.AddEdge(0, 3, 10); 64
directedGraph2.AddEdge(1, 2, 3); 65 directedGraph2.AddEdge(2, 3, 1); 66 67
Console.WriteLine(); 68 Console.WriteLine("Graph Vertex Count : {0}",
directedGraph2.VertexCount); 69 Console.WriteLine("Graph Edge Count : {0}",
directedGraph2.EdgeCount); 70 Console.WriteLine(); 71 72 int[,] distSet2 =
directedGraph2.FloydWarshell(); 73 PrintSolution(directedGraph2, distSet2); 74
75 Console.ReadKey(); 76 } 77 78 private static void PrintSolution(Graph g,
int[,] distSet) 79 { 80 Console.Write("\\t"); 81 for (int i = 0; i \<
g.VertexCount; i++) 82 { 83 Console.Write(i + "\\t"); 84 } 85
Console.WriteLine(); 86 Console.Write("\\t"); 87 for (int i = 0; i \<
g.VertexCount; i++) 88 { 89 Console.Write("-" + "\\t"); 90 } 91
Console.WriteLine(); 92 for (int i = 0; i \< g.VertexCount; i++) 93 { 94
Console.Write(i + "\|\\t"); 95 for (int j = 0; j \< g.VertexCount; j++) 96 { 97
if (distSet[i, j] == int.MaxValue) 98 { 99 Console.Write("INF" + "\\t");100 }
101 else102 { 103 Console.Write(distSet[i, j] + "\\t");104 } 105 } 106
Console.WriteLine(); 107 } 108 } 109 110 class Edge 111 { 112 public Edge(int
begin, int end, int weight) 113 { 114 this.Begin = begin;115 this.End = end;116
this.Weight = weight;117 } 118 119 public int Begin { get; private set; }120
public int End { get; private set; }121 public int Weight { get; private set;
}122 123 public override string ToString() 124 { 125 return string.Format(126
"Begin[{0}], End[{1}], Weight[{2}]",127 Begin, End, Weight); 128 } 129 } 130 131
class Graph 132 { 133 private Dictionary\<int, List\<Edge\>\> \_adjacentEdges134
= new Dictionary\<int, List\<Edge\>\>();135 136 public Graph(int vertexCount)
137 { 138 this.VertexCount = vertexCount;139 } 140 141 public int VertexCount {
get; private set; }142 143 public int EdgeCount 144 { 145 get146 { 147 return
\_adjacentEdges.Values.SelectMany(e =\> e).Count(); 148 } 149 } 150 151 public
void AddEdge(int begin, int end, int weight) 152 { 153 if
(!\_adjacentEdges.ContainsKey(begin)) 154 { 155 var edges = new List\<Edge\>();
156 \_adjacentEdges.Add(begin, edges); 157 } 158 159
\_adjacentEdges[begin].Add(new Edge(begin, end, weight)); 160 } 161 162 public
int[,] FloydWarshell()163 { 164 /\* distSet[,] will be the output matrix that
will finally have the shortest165 distances between every pair of vertices
\*/166 int[,] distSet = new int[VertexCount, VertexCount];167 168 for (int i =
0; i \< VertexCount; i++)169 { 170 for (int j = 0; j \< VertexCount; j++)171 {
172 distSet[i, j] = int.MaxValue;173 } 174 } 175 for (int i = 0; i \<
VertexCount; i++)176 { 177 distSet[i, i] = 0;178 } 179 180 /\* Initialize the
solution matrix same as input graph matrix. Or181 we can say the initial values
of shortest distances are based 182 on shortest paths considering no
intermediate vertex. \*/183 foreach (var edge in
\_adjacentEdges.Values.SelectMany(e =\> e)) 184 { 185 distSet[edge.Begin,
edge.End] = edge.Weight; 186 } 187 188 /\* Add all vertices one by one to the
set of intermediate vertices.189 ---\> Before start of a iteration, we have
shortest distances between all 190 pairs of vertices such that the shortest
distances consider only the 191 vertices in set {0, 1, 2, .. k-1} as
intermediate vertices. 192 ---\> After the end of a iteration, vertex no. k is
added to the set of 193 intermediate vertices and the set becomes {0, 1, 2, ..
k} \*/194 for (int k = 0; k \< VertexCount; k++)195 { 196 // Pick all vertices
as source one by one197 for (int i = 0; i \< VertexCount; i++)198 { 199 // Pick
all vertices as destination for the above picked source200 for (int j = 0; j \<
VertexCount; j++)201 { 202 // If vertex k is on the shortest path from203 // i
to j, then update the value of distSet[i,j]204 if (distSet[i, k] !=
int.MaxValue205 && distSet[k, j] != int.MaxValue206 && distSet[i, k] +
distSet[k, j] \< distSet[i, j]) 207 { 208 distSet[i, j] = distSet[i, k] +
distSet[k, j]; 209 } 210 } 211 } 212 } 213 214 return distSet; 215 } 216 } 217 }
218 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
运行结果如下:
![642245662676.png](media/fa780b4a0df60501e6842a03fc6672f9.png)

View File

@@ -0,0 +1,168 @@
Kruskal 最小生成树算法
对于一个给定的连通的无向图 G = (V, E),希望找到一个无回路的子集 TT 是 E
的子集,它连接了所有的顶点,且其权值之和为最小。
![243448621615.gif](media/a623e7938bac06d833a52b042d1936a5.gif)
因为 T 无回路且连接所有的顶点所以它必然是一棵树称为生成树Spanning
Tree因为它生成了图 G。显然由于树 T 连接了所有的顶点,所以树 T 有 V - 1
条边。一张图 G 可以有很多棵生成树,而把确定权值最小的树 T
的问题称为**最小生成树问题Minimum Spanning Tree**。术语 "最小生成树"
实际上是 "最小权值生成树" 的缩写。
**Kruskal 算法**提供一种在 O(ElogV) 运行时间确定最小生成树的方案。Kruskal
算法基于贪心算法Greedy
Algorithm的思想进行设计其选择的**贪心策略**就是,每次都选择权重最小的但未形成环路的边加入到生成树中。其算法结构如下:
1. 将所有的边按照权重非递减排序;
2. 选择最小权重的边,判断是否其在当前的生成树中形成了一个环路。如果环路没有形成,则将该边加入树中,否则放弃。
3. 重复步骤 2直到有 V - 1 条边在生成树中。
上述步骤 2 中使用了 [Union-Find
算法](http://www.cnblogs.com/gaochundong/p/disjoint_set_forests_heuristics.html)来判断是否存在环路。
例如,下面是一个无向连通图 G。
![27075503530.jpeg](media/3206e454a556f7a6f021c384858fea1c.jpeg)
图 G 中包含 9 个顶点和 14 条边,所以期待的最小生成树应包含 (9 - 1) = 8 条边。
首先对所有的边按照权重的非递减顺序排序:
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
Weight Src Dest 1 7 6 2 8 2 2 6 5 4 0 1 4 2 5 6 8 6 7 2 3 7 7 8 8 0 7 8 1 2 9 3
4 10 5 4 11 1 7 14 3 5
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
然后从排序后的列表中选择权重最小的边。
1\. 选择边 {7, 6},无环路形成,包含在生成树中。
![30493313453.jpeg](media/5098aea20c34b2944158e6b0622954ff.jpeg)
2\. 选择边 {8, 2},无环路形成,包含在生成树中。
![31042845021.jpeg](media/48554fff118a3129374906e0a905d876.jpeg)
3\. 选择边 {6, 5},无环路形成,包含在生成树中。
![31213781976.jpeg](media/e6bcbe8540508ae02a25725926ef375a.jpeg)
4\. 选择边 {0, 1},无环路形成,包含在生成树中。
![31355503672.jpeg](media/a69a98f4ac53d314d8fcfc0aa5fd005e.jpeg)
5\. 选择边 {2, 5},无环路形成,包含在生成树中。
![31473008195.jpeg](media/e95406c1bb140d86030be10b448f868f.jpeg)
6\. 选择边 {8, 6},有环路形成,放弃。
7\. 选择边 {2, 3},无环路形成,包含在生成树中。
![32012226348.jpeg](media/f35f5beffbc44b89085a6241d5ba154e.jpeg)
8\. 选择边 {7, 8},有环路形成,放弃。
9\. 选择边 {0, 7},无环路形成,包含在生成树中。
![32134563187.jpeg](media/44af8a5f1fc67d503c2ffa1f75c39f9c.jpeg)
10\. 选择边 {1, 2},有环路形成,放弃。
11\. 选择边 {3, 4},无环路形成,包含在生成树中。
![32279253440.jpeg](media/0f264c0a5bbd1a01e78bda2fe8ab7db3.jpeg)
12\. 由于当前生成树中已经包含 V - 1 条边,算法结束。
C\# 实现的 Kruskal 算法如下。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5
namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void
Main(string[] args) 10 { 11 Graph g = new Graph(9); 12 g.AddEdge(0, 1, 4); 13
g.AddEdge(0, 7, 8); 14 g.AddEdge(1, 2, 8); 15 g.AddEdge(1, 7, 11); 16
g.AddEdge(2, 3, 7); 17 g.AddEdge(2, 5, 4); 18 g.AddEdge(8, 2, 2); 19
g.AddEdge(3, 4, 9); 20 g.AddEdge(3, 5, 14); 21 g.AddEdge(5, 4, 10); 22
g.AddEdge(6, 5, 2); 23 g.AddEdge(8, 6, 6); 24 g.AddEdge(7, 6, 1); 25
g.AddEdge(7, 8, 7); 26 27 Console.WriteLine(); 28 Console.WriteLine("Graph
Vertex Count : {0}", g.VertexCount); 29 Console.WriteLine("Graph Edge Count :
{0}", g.EdgeCount); 30 Console.WriteLine(); 31 32 Console.WriteLine("Is there
cycle in graph: {0}", g.HasCycle()); 33 Console.WriteLine(); 34 35 Edge[] mst =
g.Kruskal(); 36 Console.WriteLine("MST Edges:"); 37 foreach (var edge in mst) 38
{ 39 Console.WriteLine("\\t{0}", edge); 40 } 41 42 Console.ReadKey(); 43 } 44 45
class Edge 46 { 47 public Edge(int begin, int end, int weight) 48 { 49
this.Begin = begin; 50 this.End = end; 51 this.Weight = weight; 52 } 53 54
public int Begin { get; private set; } 55 public int End { get; private set; }
56 public int Weight { get; private set; } 57 58 public override string
ToString() 59 { 60 return string.Format( 61 "Begin[{0}], End[{1}], Weight[{2}]",
62 Begin, End, Weight); 63 } 64 } 65 66 class Subset 67 { 68 public int Parent {
get; set; } 69 public int Rank { get; set; } 70 } 71 72 class Graph 73 { 74
private Dictionary\<int, List\<Edge\>\> \_adjacentEdges 75 = new
Dictionary\<int, List\<Edge\>\>(); 76 77 public Graph(int vertexCount) 78 { 79
this.VertexCount = vertexCount; 80 } 81 82 public int VertexCount { get; private
set; } 83 84 public IEnumerable\<int\> Vertices { get { return
\_adjacentEdges.Keys; } } 85 86 public IEnumerable\<Edge\> Edges 87 { 88 get {
return \_adjacentEdges.Values.SelectMany(e =\> e); } 89 } 90 91 public int
EdgeCount { get { return this.Edges.Count(); } } 92 93 public void AddEdge(int
begin, int end, int weight) 94 { 95 if (!\_adjacentEdges.ContainsKey(begin)) 96
{ 97 var edges = new List\<Edge\>(); 98 \_adjacentEdges.Add(begin, edges); 99 }
100 101 \_adjacentEdges[begin].Add(new Edge(begin, end, weight)); 102 } 103 104
private int Find(Subset[] subsets, int i) 105 { 106 // find root and make root
as parent of i (path compression)107 if (subsets[i].Parent != i) 108
subsets[i].Parent = Find(subsets, subsets[i].Parent); 109 110 return
subsets[i].Parent; 111 } 112 113 private void Union(Subset[] subsets, int x, int
y) 114 { 115 int xroot = Find(subsets, x); 116 int yroot = Find(subsets, y); 117
118 // Attach smaller rank tree under root of high rank tree119 // (Union by
Rank)120 if (subsets[xroot].Rank \< subsets[yroot].Rank) 121
subsets[xroot].Parent = yroot; 122 else if (subsets[xroot].Rank \>
subsets[yroot].Rank) 123 subsets[yroot].Parent = xroot; 124 125 // If ranks are
same, then make one as root and increment126 // its rank by one127 else128 { 129
subsets[yroot].Parent = xroot; 130 subsets[xroot].Rank++; 131 } 132 } 133 134
public bool HasCycle() 135 { 136 Subset[] subsets = new Subset[VertexCount]; 137
for (int i = 0; i \< subsets.Length; i++)138 { 139 subsets[i] = new Subset();
140 subsets[i].Parent = i; 141 subsets[i].Rank = 0;142 } 143 144 // Iterate
through all edges of graph, find subset of both145 // vertices of every edge, if
both subsets are same,146 // then there is cycle in graph.147 foreach (var edge
in this.Edges)148 { 149 int x = Find(subsets, edge.Begin); 150 int y =
Find(subsets, edge.End); 151 152 if (x == y) 153 { 154 return true;155 } 156 157
Union(subsets, x, y); 158 } 159 160 return false;161 } 162 163 public Edge[]
Kruskal() 164 { 165 // This will store the resultant MST166 Edge[] mst = new
Edge[VertexCount - 1];167 168 // Step 1: Sort all the edges in non-decreasing
order of their weight169 // If we are not allowed to change the given graph, we
can create a copy of170 // array of edges171 var sortedEdges =
this.Edges.OrderBy(t =\> t.Weight);172 var enumerator =
sortedEdges.GetEnumerator(); 173 174 // Allocate memory for creating V
ssubsets175 // Create V subsets with single elements176 Subset[] subsets = new
Subset[VertexCount]; 177 for (int i = 0; i \< subsets.Length; i++)178 { 179
subsets[i] = new Subset(); 180 subsets[i].Parent = i; 181 subsets[i].Rank =
0;182 } 183 184 // Number of edges to be taken is equal to V-1185 int e = 0;186
while (e \< VertexCount - 1)187 { 188 // Step 2: Pick the smallest edge. And
increment the index189 // for next iteration190 Edge nextEdge; 191 if
(enumerator.MoveNext()) 192 { 193 nextEdge = enumerator.Current; 194 195 int x =
Find(subsets, nextEdge.Begin); 196 int y = Find(subsets, nextEdge.End); 197 198
// If including this edge does't cause cycle, include it199 // in result and
increment the index of result for next edge200 if (x != y) 201 { 202 mst[e++] =
nextEdge; 203 Union(subsets, x, y); 204 } 205 else206 { 207 // Else discard the
nextEdge208 } 209 } 210 } 211 212 return mst; 213 } 214 } 215 } 216 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
输出结果如下:
![332306915105.png](media/a2a1f6233a2219cf362ac66675e74c89.png)

View File

@@ -0,0 +1,119 @@
Prim 最小生成树算法
**Prim 算法**是一种解决**最小生成树问题Minimum Spanning Tree**的算法。和
[Kruskal
算法](http://www.cnblogs.com/gaochundong/p/kruskal_minimum_spanning_tree.html)类似Prim
算法的设计也是基于贪心算法Greedy algorithm
Prim
算法的思想很简单一棵生成树必须连接所有的顶点而要保持最小权重则每次选择邻接的边时要选择较小权重的边。Prim
算法看起来非常类似于单源最短路径 [Dijkstra
算法](http://www.cnblogs.com/gaochundong/p/dijkstra_algorithm.html),从源点出发,寻找当前的最短路径,每次比较当前可达邻接顶点中最小的一个边加入到生成树中。
例如,下面这张连通的无向图 G包含 9 个顶点和 14
条边,所以期待的最小生成树应包含 (9 - 1) = 8 条边。
![19422375883.jpeg](media/a3460bbcc926be103e406d581ac4612a.jpeg)
创建 mstSet 包含到所有顶点的距离,初始为 INF源点 0 的距离为 0{0, INF, INF,
INF, INF, INF, INF, INF, INF}。
选择当前最短距离的顶点,即还是顶点 0将 0 加入 MST此时邻接顶点为 1 和 7。
![19592843526.jpeg](media/1163371b1fee9f8427fd967986a18f51.jpeg)
选择当前最小距离的顶点 1将 1 加入 MST此时邻接顶点为 2。
![20132374221.jpeg](media/72b66188c7153bdaaeb94504bf6c8c65.jpeg)
选择 2 和 7 中最小距离的顶点为 7将 7 加入 MST此时邻接顶点为 6 和 8。
![20284419332.jpeg](media/bddb24235aaf81ea66da626756cdfb48.jpeg)
选择 2, 6, 8 中最小距离的顶点为 6将 6 加入 MST此时邻接顶点为 5。
![20454417259.jpeg](media/160a163baff4ec1652cb64f7ff6a8577.jpeg)
重复上面步骤直到遍历完所有顶点为止,会得到如下 MST。
![20597845943.jpeg](media/9e5e619c58d10df405b618a18101fb78.jpeg)
C\# 实现 **Prim 算法**如下。Prim 算法可以达到 O(ElogV)
的运行时间,如果采用斐波那契堆实现,运行时间可以减少到 O(E + VlogV),如果 V
远小于 E 的话,将是对算法较大的改进。
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 5
namespace GraphAlgorithmTesting 6 { 7 class Program 8 { 9 static void
Main(string[] args) 10 { 11 Graph g = new Graph(9); 12 g.AddEdge(0, 1, 4); 13
g.AddEdge(0, 7, 8); 14 g.AddEdge(1, 2, 8); 15 g.AddEdge(1, 7, 11); 16
g.AddEdge(2, 3, 7); 17 g.AddEdge(2, 5, 4); 18 g.AddEdge(3, 4, 9); 19
g.AddEdge(3, 5, 14); 20 g.AddEdge(5, 4, 10); 21 g.AddEdge(6, 5, 2); 22
g.AddEdge(7, 6, 1); 23 g.AddEdge(7, 8, 7); 24 g.AddEdge(8, 2, 2); 25
g.AddEdge(8, 6, 6); 26 27 // sorry, this is an undirect graph, 28 // so, you
know that this is not a good idea. 29 List\<Edge\> edges = g.Edges 30 .Select(e
=\> new Edge(e.End, e.Begin, e.Weight)) 31 .ToList(); 32 foreach (var edge in
edges) 33 { 34 g.AddEdge(edge.Begin, edge.End, edge.Weight); 35 } 36 37
Console.WriteLine(); 38 Console.WriteLine("Graph Vertex Count : {0}",
g.VertexCount); 39 Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount); 40
Console.WriteLine(); 41 42 List\<Edge\> mst = g.Prim(); 43
Console.WriteLine("MST Edges:"); 44 foreach (var edge in mst.OrderBy(e =\>
e.Weight)) 45 { 46 Console.WriteLine("\\t{0}", edge); 47 } 48 49
Console.ReadKey(); 50 } 51 52 class Edge 53 { 54 public Edge(int begin, int end,
int weight) 55 { 56 this.Begin = begin; 57 this.End = end; 58 this.Weight =
weight; 59 } 60 61 public int Begin { get; private set; } 62 public int End {
get; private set; } 63 public int Weight { get; private set; } 64 65 public
override string ToString() 66 { 67 return string.Format( 68 "Begin[{0}],
End[{1}], Weight[{2}]", 69 Begin, End, Weight); 70 } 71 } 72 73 class Graph 74 {
75 private Dictionary\<int, List\<Edge\>\> \_adjacentEdges 76 = new
Dictionary\<int, List\<Edge\>\>(); 77 78 public Graph(int vertexCount) 79 { 80
this.VertexCount = vertexCount; 81 } 82 83 public int VertexCount { get; private
set; } 84 85 public IEnumerable\<int\> Vertices { get { return
\_adjacentEdges.Keys; } } 86 87 public IEnumerable\<Edge\> Edges 88 { 89 get {
return \_adjacentEdges.Values.SelectMany(e =\> e); } 90 } 91 92 public int
EdgeCount { get { return this.Edges.Count(); } } 93 94 public void AddEdge(int
begin, int end, int weight) 95 { 96 if (!\_adjacentEdges.ContainsKey(begin)) 97
{ 98 var edges = new List\<Edge\>(); 99 \_adjacentEdges.Add(begin, edges); 100 }
101 102 \_adjacentEdges[begin].Add(new Edge(begin, end, weight)); 103 } 104 105
public List\<Edge\> Prim() 106 { 107 // Array to store constructed MST108 int[]
parent = new int[VertexCount];109 110 // Key values used to pick minimum weight
edge in cut111 int[] keySet = new int[VertexCount];112 113 // To represent set
of vertices not yet included in MST114 bool[] mstSet = new bool[VertexCount];115
116 // Initialize all keys as INFINITE117 for (int i = 0; i \< VertexCount;
i++)118 { 119 keySet[i] = int.MaxValue;120 mstSet[i] = false;121 } 122 123 //
Always include first 1st vertex in MST.124 // Make key 0 so that this vertex is
picked as first vertex125 keySet[0] = 0;126 parent[0] = -1; // First node is
always root of MST127 128 // The MST will have V vertices129 for (int i = 0; i
\< VertexCount - 1; i++)130 { 131 // Pick thd minimum key vertex from the set of
vertices132 // not yet included in MST133 int u = CalculateMinDistance(keySet,
mstSet); 134 135 // Add the picked vertex to the MST Set136 mstSet[u] = true;137
138 // Update key value and parent index of the adjacent vertices of139 // the
picked vertex. Consider only those vertices which are not yet140 // included in
MST141 for (int v = 0; v \< VertexCount; v++)142 { 143 // graph[u, v] is non
zero only for adjacent vertices of m144 // mstSet[v] is false for vertices not
yet included in MST145 // Update the key only if graph[u, v] is smaller than
key[v]146 if (!mstSet[v] 147 && \_adjacentEdges.ContainsKey(u) 148 &&
\_adjacentEdges[u].Exists(e =\> e.End == v)) 149 { 150 int d =
\_adjacentEdges[u].Single(e =\> e.End == v).Weight; 151 if (d \< keySet[v]) 152
{ 153 keySet[v] = d; 154 parent[v] = u; 155 } 156 } 157 } 158 } 159 160 // get
all MST edges161 List\<Edge\> mst = new List\<Edge\>(); 162 for (int i = 1; i \<
VertexCount; i++)163 mst.Add(\_adjacentEdges[parent[i]].Single(e =\> e.End ==
i)); 164 165 return mst; 166 } 167 168 private int CalculateMinDistance(int[]
keySet, bool[] mstSet)169 { 170 int minDistance = int.MaxValue;171 int
minDistanceIndex = -1;172 173 for (int v = 0; v \< VertexCount; v++)174 { 175 if
(!mstSet[v] && keySet[v] \<= minDistance) 176 { 177 minDistance = keySet[v]; 178
minDistanceIndex = v; 179 } 180 } 181 182 return minDistanceIndex; 183 } 184 }
185 } 186 }
![copycode.gif](media/51e409b11aa51c150090697429a953ed.gif)
复制代码
输出结果如下:
![109289878361.png](media/bc10e6800b78eac0fb9893134db87cca.png)
**参考资料**

View File

@@ -0,0 +1,22 @@
时间复杂度
时间复杂度并不是表示一个程序解决问题需要花多少时间而是当问题规模扩大后程序需要的时间长度增长得有多快。也就是说对于高速处理数据的计算机来说处理某一个特定数据的效率不能衡量一个程序的好坏而应该看当这个数据的规模变大到数百倍后程序运行时间是否还是一样或者也跟着慢了数百倍或者变慢了数万倍。不管数据有多大程序处理花的时间始终是那么多的我们就说这个程序很好具有O(1)的时间复杂度也称常数级复杂度数据规模变得有多大花的时间也跟着变得有多长这个程序的时间复杂度就是O(n)比如找n个数中的最大值而像冒泡排序、插入排序等数据扩大2倍时间变慢4倍的属于O(n\^2)的复杂度。还有一些穷举类的算法所需时间长度成几何阶数上涨这就是O(a\^n)的指数级复杂度甚至O(n!)的阶乘级复杂度。不会存在O(2\*n\^2)的复杂度因为前面的那个“2”是系数根本不会影响到整个程序的时间增长。同样地O
(n\^3+n\^2)的复杂度也就是O(n\^3)的复杂度。因此我们会说一个O(0.01\*n\^3)的程序的效率比O(100\*n\^2)的效率低尽管在n很小的时候前者优于后者但后者时间随数据规模增长得慢最终O(n\^3)的复杂度将远远超过O(n\^2)。我们也说O(n\^100)的复杂度小于O(1.01\^n)的复杂度。
容易看出前面的几类复杂度被分为两种级别其中后者的复杂度无论如何都远远大于前者一种是O(1),O(log(n)),O(n\^a)等我们把它叫做多项式级的复杂度因为它的规模n出现在底数的位置另一种是O(a\^n)和O(n!)型复杂度,它是非多项式级的,其复杂度计算机往往不能承受。当我们在解决一个问题时,我们选择的算法通常都需要是多项式级的复杂度,非多项式级的复杂度需要的时间太多,往往会超时,除非是数据规模非常小。
P类问题的概念
如果一个问题可以找到一个能在多项式的时间里解决它的算法那么这个问题就属于P问题。
NP问题的概念
这个就有点难理解了或者说容易理解错误。在这里强调回到我竭力想澄清的误区上NP问题不是非P类问题。NP问题是指可以在多项式的时间里验证一个解的问题。NP问题的另一个定义是可以在多项式的时间里猜出一个解的问题。比方说我RP很好在程序中需要枚举时我可以一猜一个准。现在某人拿到了一个求最短路径的问题问从起点到终点是否有一条小于100个单位长度的路线。它根据数据画好了图但怎么也算不出来于是来问我你看怎么选条路走得最少我说我RP很好肯定能随便给你指条很短的路出来。然后我就胡乱画了几条线说就这条吧。那人按我指的这条把权值加起来一看神了路径长度98比100小。于是答案出来了存在比100小的路径。别人会问他这题怎么做出来的他就可以说因为我找到了一个比100
小的解。在这个题中找一个解很困难但验证一个解很容易。验证一个解只需要O(n)的时间复杂度也就是说我可以花O(n)的时间把我猜的路径的长度加出来。那么只要我RP好猜得准我一定能在多项式的时间里解决这个问题。我猜到的方案总是最优的不满足题意的方案也不会来骗我去选它。这就是NP问题。当然有不是NP问题的问题即你猜到了解但是没用因为你不能在多项式的时间里去验证它。下面我要举的例子是一个经典的例子它指出了一个目前还没有办法在多项式的时间里验证一个解的问题。很显然前面所说的Hamilton回路是NP问题因为验证一条路是否恰好经过了每一个顶点非常容易。但我要把问题换成这样试问一个图中是否不存在Hamilton回路。这样问题就没法在多项式的时间里进行验证了因为除非你试过所有的路否则你不敢断定它“没有Hamilton回路”。
之所以要定义NP问题是因为通常只有NP问题才可能找到多项式的算法。我们不会指望一个连多项式地验证一个解都不行的问题存在一个解决它的多项式级的算法。相信读者很快明白信息学中的号称最困难的问题——“NP问题”实际上是在探讨NP问题与P类问题的关系。
NPC问题的定义
同时满足下面两个条件的问题就是NPC问题。首先它得是一个NP问题然后所有的NP问题都可以约化到它。证明一个问题是
NPC问题也很简单。先证明它至少是一个NP问题再证明其中一个已知的NPC问题能约化到它由约化的传递性则NPC问题定义的第二条也得以满足至于第一个NPC问题是怎么来的下文将介绍这样就可以说它是NPC问题了。

View File

@@ -0,0 +1,13 @@
问题分析
有若干个城市,任何两个城市之间的距离都是确定的,现要求一旅行商从某城市出发必须经过每一个城市且只在一个城市逗留一次,最后回到出发的城市,问如何事先确定一条最短的线路已保证其旅行的费用最少?
![clipboard.png](media/f8d48508d06e4d604fbccd69b9c5eccf.png)
贪心算法
动态规划算法
分支限界算法
遗传算法

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,273 @@
遗传算法定义
遗传算法Genetic Algorithm,
GA起源于对生物系统所进行的计算机模拟研究。它是模仿自然界生物进化机制发展起来的随机全局搜索和优化方法借鉴了达尔文的进化论和孟德尔的遗传学说。其本质是一种高效、并行、全局搜索的方法能在搜索过程中自动获取和积累有关搜索空间的知识并自适应地控制搜索过程以求得最佳解。
袋鼠跳问题(纯粹是觉得有意思)
**“袋鼠跳”问题**
既然我们把函数曲线理解成一个一个山峰和山谷组成的山脉。那么我们可以设想所得到的每一个解就是一只袋鼠,我们希望它们不断的向着更高处跳去,直到跳到最高的山峰(尽管袋鼠本身不见得愿意那么做)。所以求最大值的过程就转化成一个“袋鼠跳”的过程。
作为对比下面简单介绍“袋鼠跳”的几种方式。
1\. 爬山法(最速上升爬山法):
从搜索空间中随机产生邻近的点,从中选择对应解最优的个体,替换原来的个体,不断重复上述过程。因为爬山法只对“邻近”的点作比较,所以目光比较“短浅”,常常只能收敛到离开初始位置比较近的局部最优解上面。对于存在很多局部最优点的问题,通过一个简单的迭代找出全局最优解的机会非常渺茫。(在爬山法中,袋鼠最有希望到达最靠近它出发点的山顶,但不能保证该山顶是珠穆朗玛峰,或者是一个非常高的山峰。因为一路上它只顾上坡,没有下坡。)
2\. 模拟退火:
这个方法来自金属热加工过程的启发。在金属热加工过程中当金属的温度超过它的熔点Melting
Point原子就会激烈地随机运动。与所有的其它的物理系统相类似原子的这种运动趋向于寻找其能量的极小状态。在这个能量的变迁过程中开始时温度非常高
使得原子具有很高的能量。随着温度不断降低,金属逐渐冷却,金属中的原子的能量就越来越小,最后达到所有可能的最低点。利用模拟退火的时候,让算法从较大的跳跃开始,使到它有足够的“能量”逃离可能“路过”的局部最优解而不至于限制在其中,当它停在全局最优解附近的时候,逐渐的减小跳跃量,以便使其“落脚
”到全局最优解上。(在模拟退火中,袋鼠喝醉了,而且随机地大跳跃了很长时间。运气好的话,它从一个山峰跳过山谷,到了另外一个更高的山峰上。但最后,它渐渐清醒了并朝着它所在的峰顶跳去。)
3\. 遗传算法:
模拟物竞天择的生物进化过程,通过维护一个潜在解的群体执行了多方向的搜索,并支持这些方向上的信息构成和交换。是以面为单位的搜索,比以点为单位的搜索,更能发现全局最优解。(在遗传算法中,有很多袋鼠,它们降落到喜玛拉雅山脉的任意地方。**这些袋鼠并不知道它们的任务是寻找珠穆朗玛峰。**但每过几年,就在一些海拔高度较低的地方射杀一些袋鼠,并希望存活下来的袋鼠是多产的,在它们所处的地方生儿育女*。*)(或者换个说法。从前,有一大群袋鼠,它们被莫名其妙的零散地遗弃于喜马拉雅山脉。于是只好在那里艰苦的生活。海拔低的地方弥漫着一种无色无味的毒气,海拔越高毒气越稀薄。可是可怜的袋鼠们对此**全然不觉**,还是习惯于活蹦乱跳。于是,不断有袋鼠死于海拔较低的地方,而越是在海拔高的袋鼠越是能活得更久,也越有机会生儿育女。就这样经过许多年,这些袋鼠们竟然都不自觉地聚拢到了一个个的山峰上,可是在所有的袋鼠中,只有聚拢到珠穆朗玛峰的袋鼠被带回了美丽的澳洲。)
相关术语
**基因型(genotype)**
性状染色体的内部表现;
**表现型(phenotype)**
染色体决定的性状的外部表现,或者说,根据基因型形成的个体的外部表现;
**进化(evolution)**
种群逐渐适应生存环境,品质不断得到改良。生物的进化是以种群的形式进行的。
**适应度(fitness)**
度量某个物种对于生存环境的适应程度。
**选择(selection)**
> 以一定的概率从种群中选择若干个个体。一般,选择过程是一种基于适应度的优胜劣汰的过程。
**复制(reproduction)**
> 细胞分裂时遗传物质DNA通过复制而转移到新产生的细胞中新细胞就继承了旧细胞的基因。
**交叉(crossover)**
> 两个染色体的某一相同位置处DNA被切断前后两串分别交叉组合形成两个新的染色体。也称基因重组或杂交
**变异(mutation)**
复制时可能(很小的概率)产生某些复制差错,变异产生新的染色体,表现出新的性状。
**编码(coding)**
> DNA中遗传信息在一个长链上按一定的模式排列。遗传编码可看作从表现型到基因型的映射。
**解码(decoding)**
基因型到表现型的映射。
**个体individual**
指染色体带有特征的实体;
**种群population**
个体的集合,该集合内个体数称为种群的大小。
基础定义
Ø**染色体与基因**
染色体chromosome就是问题中个体的某种字符串形式的编码表示。字符串中的字符也就称为基因gene
Ø**个体**
个体就是模拟生物个体而对问题中的对象(一般就是问题的解)的一种称呼,一个个体也就是搜索空间中的一个点。
Ø**种群**
种群(population)就是模拟生物种群而由若干个体组成的群体,
它一般是整个搜索空间的一个很小的子集。
Ø**适应度与适应度函数**
适应度(fitness)就是借鉴生物个体对环境适应程度,而对问题中的个体对象所设计的表征其优劣的一种测度。适应度函数(fitness
function)就是问题中的全体个体与其适应度之间的一个对应关系。它一般是一个实值函数。该函数就是遗传算法中指导搜索的评价函数。
Ø**遗传操作**
亦称遗传算子(genetic operator),就是关于染色体的运算。遗传算法中有三种遗传操作:
● 选择-复制(selection-reproduction)
● 交叉(crossover亦称交换、交配
● 变异(mutation亦称突变)
遗传算子
**● 选择-复制(selection-reproduction)**
通常做法是对于一个规模为N的种群S按每个染色体xi∈S的选择概率P(xi)所决定的选中机会,
分N次从S中随机选定N个染色体, 并进行复制。
![clipboard.png](media/55e7000759c9f607cef012b78f7f8f09.png)
上述公式中f(xi)表示个体的适应度函数xi表示个体p(xi)表示选择概率。
**● 交叉(crossover亦称交换、交配或杂交)**
互换两个染色体某些位上的基因。
例如, 设染色体 s1=01001011, s2=10010101, 交换其后4位基因, 即s1=01000101,
s2=10011011可以看做是原染色体s1和s2的子代染色体。
![clipboard.png](media/3e038dcf92495e474695b88e9fd3fde4.png)
**● 变异(mutation亦称突变)**
就是改变染色体某个(些)位上的基因。
例如, 设染色体 s=11001101将其第三位上的0变为1, 即s=11001101 →11101101=
s。s也可以看做是原染色体s的子代染色体。
![clipboard.png](media/20e94f670319849e1209cbe28e9ec5fe.png)
**● 相关算法**
**选择-复制中的赌轮算法**
![clipboard.png](media/ce1292f05670437ae4832b649bde8b8e.png)
**交叉中的PMX部分匹配法**
PMX操作是Goldberg和Lingle于1985年提出的在PMX操作中先随机产生两个位串交叉点定义这两点之间的区域为一匹配交叉区域并使用位置交换操作来交换两个父串的匹配区域。考虑下面一个实例如两父及匹配区域为
A=9 8 5 \| 4 6 7 \| 1 3 2 0
B=8 6 3 \| 2 0 1 \| 9 5 4 7
首先交换A和B的两个匹配区域得到
A=9 8 5 \| 2 0 1 \| 1 3 2 0
B=8 6 3 \| 4 6 7 \| 9 5 4 7
对于A、B两子串中匹配区域以外出现的遍历重复依据匹配区域内的位置映射关系逐一进行交换对于A有2到40到61到7的位置符号映射对于A的匹配区域以外的201分别以467替换则得
A=9 8 5 \| 2 0 1 \| 7 3 4 6
同理可得:
B=8 0 3 \| 4 6 7 \| 9 5 2 1
这样,每个子串的次序由其父串部分地确定。
**交叉中的OX顺序交叉法**
1985年Davis等人提出了基于路径表示的顺序交叉OX操作OX操作能够保留排列并融合不同排列的有序结构单元。此方法开始也是选择一个匹配区域
A=9 8 5 \| 4 6 7 \| 1 3 2 0
B=8 6 3 \| 2 0 1 \| 9 5 4 7
首先两个交叉点之间的中间段保持不变在其区域外的相应位置标记X得到
A=X X X \| 4 6 7 \| X X X X
B=X X X \| 2 0 1 \| X X X X
其次记录父个体B从第二个交叉点开始城市码的排列顺序当到达表尾时返回表头继续记录城市码直至到达第二个交叉点结束这样便获得了父个体B从第二个交叉点开始的城市码排列顺序为9-5-4-7-8-6-3-2-0-1对于父个体A而言已有城市码467将它们从父个体B的城市码排列顺序中去掉得到排列顺序9-5-8-3-2-0-1再将这个排列顺序复制给父个体A复制的起点也是从第二个交叉点开始以此决定子个体1对应位置的未知码X这样新个体A为:
A=2 0 1 4 6 7 9 5 8 3
同样利用同样的方法可以得到交叉后的B染色体为
B=4 6 7 2 0 1 3 9 8 5
算法步骤
**遗传算法的基本流程图**
![clipboard.png](media/0fc9c39db519692eb9568c5aca664304.png)
**遗传算法步骤用文字描述如下:**
> a)在搜索空间U上定义一个适应度函数f(x)给定种群规模N交叉率Pc和变异率Pm代数T
> b)随机产生U中的N个个体s1, s2, …, sN组成初始种群S={s1, s2, …,
> sN}置代数计数器t=1
> c)计算S中每个个体的适应度f()
> d)若终止条件满足则取S中适应度最大的个体作为所求结果算法结束。
> e)按选择概率P(xi)所决定的选中机会每次从S中随机选定1个个体并将其染色体复制共做N次然后将复制所得的N个染色体组成群体S1
> f)按交叉率Pc所决定的参加交叉的染色体数c从S1中随机确定c个染色体配对进行交叉操作并用产生的新染色体代替原染色体得群体S2
> g)按变异率Pm所决定的变异次数m从S2中随机确定m个染色体分别进行变异操作并用产生的新染色体代替原染色体得群体S3
> h)将群体S3作为新一代种群即用S3代替St = t+1转步3
**遗传算法步骤用代码描述如下:**
> 1\. initiate(); //产生初始化种群
> 2\. evaluation( 0 ); //对初始化种群进行评估、排序
> 3\. for( i = 0 ; i \< MAXloop ; i++ )
> 4\. {
> 5\. cross(); //进行交叉操作
> 6\. evaluation(); //对子种群进行评估、排序
> 7\. selection(); //对父子种群中选择最优的NUM个作为新的父种群
> 8\. if( record() = = 1 ) //满足终止规则1则flag=1并停止循环
> 9\. {
> 10\. break;
> 11\. }
> 12\. mutation(); //变异操作
> 13\. }
**遗传算法中的控制参数**
> Ø种群规模;
> Ø最大换代数;
> Ø交叉率(crossover
> rate)就是参加交叉运算的染色体个数占全体染色体总数的比例记为Pc,取值范围一般为0.40.99
> Ø变异率(mutation
> rate)是指发生变异的基因位数所占全体染色体的基因总位数的比例记为Pm取值范围一般为0.00010.1。
其他思想
**精英主义**
当利用交叉和变异产生新的一代时,我们有很大的可能把在某个中间步骤中得到的最优解丢失。
精英主义的思想是,在每一次产生新的一代时,首先把当前最优解原封不动的复制到新的一代中。然后按照前面所说的那样做就行。精英主义方法可以大幅提高运算速度,因为它可以防止丢失掉找到的最好的解。
精英主义是基本遗传算法的一种优化。为了防止进化过程中产生的最优解被交叉和变异所破坏,可以将每一代中的最优解原封不动的复制到下一代中。
**灾变主义**
遗传算法的局部搜索能力较强,但是很容易陷入局部极值。引用网上的一段原话:
“那么如何解决遗传算法容易陷入局部极值的问题呢让我们来看看大自然提供的方案。六千五百万年以前恐龙和灵长类动物并存恐龙在地球上占绝对统治地位如果恐龙没有灭绝灵长类动物是绝没有可能统治地球的。正是恐龙的灭绝才使灵长类动物有了充分进化的余地事实上地球至少经历了5次物种大灭绝每次物种灭绝都给更加高级的生物提供了充分进化的余地。所以要跳出局部极值就必须杀死当前所有的优秀个体从而让远离当前极值的点有充分的进化余地。这就是灾变的思想。”
灾变就是杀掉最优秀的个体,这样才可能产生更优秀的物种。那何时进行灾变,灾变次数又如何设定?
何时进行灾变可以采用灾变倒计数的方式。如果n代还没有出现比之前更优秀的个体时可以发生灾变。灾变次数可以这样来确定如果若干次灾变后产生的个体的适应度与没灾变前的一样可停止灾变。