1、1、二叉树三种遍历的非递归算法(背诵版)本贴给出二叉树先序、中序、后序三种遍历的非递归算法,此三个算法可视为标准算法,直接用于考研答题。1.先序遍历非递归算法#define maxsize 100typedef structBitree Elemmaxsize;int top;SqStack;void PreOrderUnrec(Bitree t)SqStack s;StackInit(s);p=t;while (p!=null | !StackEmpty(s)while (p!=null) /遍历左子树visite(p-data);push(s,p);p=p-lchild; /endwhil
2、eif (!StackEmpty(s) /通过下一次循环中的内嵌 while 实现右子树遍历p=pop(s);p=p-rchild; /endif /endwhile /PreOrderUnrec2.中序遍历非递归算法#define maxsize 100typedef structBitree Elemmaxsize;int top;SqStack;void InOrderUnrec(Bitree t)SqStack s;StackInit(s);p=t;while (p!=null | !StackEmpty(s)while (p!=null) /遍历左子树push(s,p);p=p-lc
3、hild;/endwhileif (!StackEmpty(s)p=pop(s);visite(p-data); /访问根结点p=p-rchild; /通过下一次循环实现右子树遍历/endif /endwhile/InOrderUnrec3.后序遍历非递归算法#define maxsize 100typedef enumL,R tagtype;typedef struct Bitree ptr;tagtype tag;stacknode;typedef structstacknode Elemmaxsize;int top;SqStack;/后序遍历void PostOrderUnrec(Bi
4、tree t)SqStack s;stacknode x;StackInit(s);p=t;do while (p!=null) /遍历左子树x.ptr = p; x.tag = L; /标记为左子树push(s,x);p=p-lchild;while (!StackEmpty(s) p = x.ptr;visite(p-data); /tag 为 R,表示右子树访问完毕,故访问根结点if (!StackEmpty(s)s.Elems.top.tag =R; /遍历右子树p=s.Elems.top.ptr-rchild; while (!StackEmpty(s);/PostOrderUnre
5、c4.层次遍历算法/ 二叉树的数据结构structBinaryTreeint value; / 不写模板了,暂时用整形代替节点的数据类型BinaryTree *left;BinaryTree *right;BinaryTree*root; / 已知二叉树的根节点/层次遍历voidLevel( const BinaryTree *root )Queue *buf = new Queue(); / 定义一个空队列,假设此队列的节点数据类型也是整形的BinaryTree t; / 一个临时变量buf.push_back(root); /令根节点入队while( buf.empty = false )
6、 / 当队列不为空p = buf.front(); / 取出队列的第一个元素coutvalueleft != NULL ) / 若左子树不空,则令其入队q.push( p-left );if( p-right != NULL ) / 若右子树不空,则令其入队q.push( p-right ); buf.pop(); / 遍历过的节点出队 coutfront=0;(*q)-rear=0;int isFull(Quque *q)if(q-front=(q-rear+1)%MAXSIZE)/判满( 空出一个元素不用) 刘勉刚return 1;elsereturn 0;int insertQue(Qu
7、que *q,int elem)if(isFull(*q)return -1;(*q)-elem(*q)-rear=elem;(*q)-rear=(*q)-rear+1)%MAXSIZE;/插入return0;int isEmpty(Quque *q)if(q-front=q-rear)/判空return 1;elsereturn 0;int deleteQue(Quque * q,int *pelem)if(isEmpty(*q)return 0;*pelem=(*q)-elem(*q)-front;(*q)-front=(*q)-front +1)%MAXSIZE;return0;4、串串
8、一章需要攻破的主要堡垒有:1. 串的基本概念,串与线性表的关系(串是其元素均为字符型数据的特殊线性表) ,空串与空格串的区别,串相等的条件;2. 串的基本操作,以及这些基本函数的使用,包括:取子串,串连接,串替换,求串长等等。运用串的基本操作去完成特定的算法是很多学校在基本操作上的考查重点。3. 顺序串与链串及块链串的区别和联系,实现方式。4. KMP 算法思想。KMP 中 next 数组以及 nextval 数组的求法。明确传统模式匹配算法的不足,明确 next 数组需要改进。可能进行的考查方式是:求 next 和 nextval 数组值,根据求得的 next 或 nextval 数组值给出
9、运用 KMP 算法进行匹配的匹配过程。5、多维数组和广义表矩阵包括:对称矩阵,三角矩阵,具有某种特点的稀疏矩阵等。熟悉稀疏矩阵的三种不同存储方式:三元组,带辅助行向量的二元组,十字链表存储。掌握将稀疏矩阵的三元组或二元组向十字链表进行转换的算法。6、树与二叉树树一章的知识点包括:二叉树的概念、性质和存储结构,二叉树遍历的三种算法(递归与非递归) ,在三种基本遍历算法的基础上实现二叉树的其它算法,线索二叉树的概念和线索化算法以及线索化后的查找算法,最优二叉树的概念、构成和应用,树的概念和存储形式,树与森林的遍历算法及其与二叉树遍历算法的联系,树与森林和二叉树的转换。(1) 二叉树的概念、性质和存
10、储结构考查方法可有:直接考查二叉树的定义,让你说明二叉树与普通双分支树(左右子树无序)的区别;考查满二叉树和完全二叉树的性质,普通二叉树的五个性质:A.第 i 层的最多结点数,B.深度为 k 的二叉树的最多结点数,C.n0=n2+1 的性质,D.n 个结点的完全二叉树的深度,E. 顺序存储二叉树时孩子结点与父结点之间的换算关系(root 从 1 开始,则左为:2*i,右为:2*i+1) 。二叉树的顺序存储和二叉链表存储的各自优缺点及适用场合,二叉树的三叉链表表示方法。(2) 二叉树的三种遍历算法这一知识点掌握的好坏,将直接关系到树一章的算法能否理解,进而关系到树一章的算法设计题能否顺利完成。二
11、叉树的遍历算法有三种:先序,中序和后序。其划分的依据是视其每个算法中对根结点数据的访问顺序而定。不仅要熟练掌握三种遍历的递归算法,理解其执行的实际步骤,并且应该熟练掌握三种遍历的非递归算法。由于二叉树一章的很多算法,可以直接根据三种递归算法改造而来(比如:求叶子个数) ,所以,掌握了三种遍历的非递归算法后,对付诸如:“利用非递归算法求二叉树叶子个数”这样的题目就下笔如有神了。(3) 可在三种遍历算法的基础上改造完成的其它二叉树算法:求叶子个数,求二叉树结点总数,求度为 1 或度为 2 的结点总数,复制二叉树,建立二叉树,交换左右子树,查找值为 n 的某个指定结点,删除值为 n 的某个指定结点,
12、诸如此类等等等等。如果你可以熟练掌握二叉树的递归和非递归遍历算法,那么解决以上问题就是小菜一碟了。(4) 线索二叉树:线索二叉树的引出,是为避免如二叉树遍历时的递归求解。众所周知,递归虽然形式上比较好理解,但是消耗了大量的内存资源,如果递归层次一多,势必带来资源耗尽的危险,为了避免此类情况,线索二叉树便堂而皇之地出现了。对于线索二叉树,应该掌握:线索化的实质,三种线索化的算法,线索化后二叉树的遍历算法,基本线索二叉树的其它算法问题(如:查找某一类线索二叉树中指定结点的前驱或后继结点就是一类常考题) 。(5) 最优二叉树(哈夫曼树):最优二叉树是为了解决特定问题引出的特殊二叉树结构,它的前提是给
13、二叉树的每条边赋予了权值,这样形成的二叉树按权相加之和是最小的。最优二叉树一节,直接考查算法源码的很少,一般是给你一组数据,要求你建立基于这组数据的最优二叉树,并求出其最小权值之和,此类题目不难,属送分题。(6) 树与森林:二叉树是一种特殊的树,这种特殊不仅仅在于其分支最多为 2 以及其它特征,一个最重要的特殊之处是在于:二叉树是有序的!即:二叉树的左右孩子是不可交换的,如果交换了就成了另外一棵二叉树。树与森林的遍历,不像二叉树那样丰富,他们只有两种遍历算法:先根与后根(对于森林而言称作:先序与后序遍历) 。此二者的先根与后根遍历与二叉树中的遍历算法是有对应关系的:先根遍历对应二叉树的先序遍历
14、,而后根遍历对应二叉树的中序遍历。二叉树、树与森林之所以能有以上的对应关系,全拜二叉链表所赐。二叉树使用二叉链表分别存放他的左右孩子,树利用二叉链表存储孩子及兄弟(称孩子兄弟链表) ,而森林也是利用二叉链表存储孩子及兄弟。7、图1. 图的基本概念:图的定义和特点,无向图,有向图,入度,出度,完全图,生成子图,路径长度,回路, (强)连通图, (强)连通分量等概念。2. 图的几种存储形式:邻接矩阵, (逆)邻接表,十字链表及邻接多重表。在考查时,有的学校是给出一种存储形式,要求考生用算法或手写出与给定的结构相对应的该图的另一种存储形式。3. 考查图的两种遍历算法:深度遍历和广度遍历深度遍历和广度
15、遍历是图的两种基本的遍历算法,这两个算法对图一章的重要性等同于“先序、中序、后序遍历”对于二叉树一章的重要性。在考查时,图一章的算法设计题常常是基于这两种基本的遍历算法而设计的,比如:“求最长的最短路径问题”和“判断两顶点间是否存在长为 K 的简单路径问题” ,就分别用到了广度遍历和深度遍历算法。4. 生成树、最小生成树的概念以及最小生成树的构造:PRIM 算法和 KRUSKAL 算法。考查时,一般不要求写出算法源码,而是要求根据这两种最小生成树的算法思想写出其构造过程及最终生成的最小生成树。5. 拓扑排序问题:拓扑排序有两种方法,一是无前趋的顶点优先算法,二是无后继的顶点优先算法。换句话说,
16、一种是“从前向后”的排序,一种是“从后向前”排。当然,后一种排序出来的结果是“逆拓扑有序”的。6. 关键路径问题:这个问题是图一章的难点问题。理解关键路径的关键有三个方面:一是何谓关键路径;二是最早时间是什么意思、如何求;三是最晚时间是什么意思、如何求。简单地说,最早时间是通过“从前向后”的方法求的,而最晚时间是通过“从后向前”的方法求解的,并且,要想求最晚时间必须是在所有的最早时间都已经求出来之后才能进行。在实际设计关键路径的算法时,还应该注意以下这一点:采用邻接表的存储结构,求最早时间和最晚时间要采用不同的处理方法,即:在算法初始时,应该首先将所有顶点的最早时间全部置为 0。关键路径问题是
17、工程进度控制的重要方法,具有很强的实用性。7. 最短路径问题:与关键路径问题并称为图一章的两只拦路虎。概念理解是比较容易的,关键是算法的理解。最短路径问题分为两种:一是求从某一点出发到其余各点的最短路径(单源最短路径) ;二是求图中每一对顶点之间的最短路径。这个问题也具有非常实用的背景特色,一个典型的应该就是旅游景点及旅游路线的选择问题。解决第一个问题用 DIJSKTRA 算法,解决第二个问题用 FLOYD 算法,注意区分。8、查找(search)先弄清楚以下几个概念:关键字、主关键字、次关键字的含义;静态查找与动态查找的含义及区别;平均查找长度 ASL 的概念及在各种查找算法中的计算方法和计
18、算结果,特别是一些典型结构的 ASL 值,应该记住。一般将 search 分为三类:在顺序表上的查找;在树表上的查找;在哈希表上的查找。(1 )线性表上的查找:主要分为三种线性结构:顺序表传统查找方法:逐个比较;有序顺序表二分查找法(注意适用条件以及其递归实现方法) ;索引顺序表对索引结构,采用索引查找算法。注意这三种表下的 ASL 值以及三种算法的实现。(2 )树表上的查找:树表主要分为以下几种:二叉排序树(即二叉查找树) ,平衡二叉查找树(AVL 树) ,B 树,键树。其中,尤以前两种结构为重,也有部分名校偏爱考 B 树的。由于二叉排序树与平衡二叉树是一种特殊的二叉树。二叉排序树,简言之,
19、就是“左小右大” ,它的中序遍历结果是一个递增的有序序列。平衡二叉排序树是二叉排序树的优化,其本质也是一种二叉排序树,只不过,平衡排序二叉树对左右子树的深度有了限定:深度之差的绝对值不得大于 1。对于二叉排序树, “判断某棵二叉树是否二叉排序树”这一算法经常被考到,可用递归,也可以用非递归。平衡二叉树的建立也是一个常考点,但该知识点归根结底还是关注的平衡二叉树的四种调整算法,调整的一个参照是:调整前后的中序遍历结果相同。B 树是二叉排序树的进一步改进,也可以把 B 树理解为三叉、四叉排序树。除 B树的查找算法外,应该特别注意一下 B 树的插入和删除算法,因为这两种算法涉及到 B 树结点的分裂和
20、合并,是一个难点。键树(keywordtree) ,又称数字搜索树(digitalsearch tree)或字符树。trie 树也可说等同于键树或属于键树的一种。键树特别适用于查找英文单词的场合。一般不要求能完整描述算法源码,多是根据算法思想建立键树及描述其大致查找过程。(3 )基于哈希表的查找算法:哈希译自“hash”一词,意为“散列”或“杂凑” 。哈希表查找的基本思想是:根据当前待查找数据的特征,以记录关键字为自变量,设计一个 function,该函数对关键字进行转换后,其解释结果为待查的地址。基于哈希表的考查点有:哈希函数的设计,冲突解决方法的选择及冲突处理过程的描述。9、内部排序考查你
21、对书本上的各种排序算法及其思想以及其优缺点和性能指标(时间复杂度)能否了如指掌。排序方法分类有:插入、选择、交换、归并、计数等五种排序方法。(1 )插入排序中又可分为:直接插入、折半插入、2 路插入(?) 、希尔排序。这几种插入排序算法的最根本的不同点,说到底就是根据什么规则寻找新元素的插入点。直接插入是依次寻找,折半插入是折半寻找,希尔排序,是通过控制每次参与排序的数的总范围“由小到大”的增量来实现排序效率提高的目的。(2 )交换排序,又称冒泡排序,在交换排序的基础上改进又可以得到快速排序。快速排序的思想,一语以敝之:用中间数将待排数据组一分为二。(3 )选择排序可以分为:简单选择、树选择、
22、堆排序。选择排序相对于前面几种排序算法来说,难度大一点。这三种方法的不同点是,根据什么规则选取最小的数。简单选择,是通过简单的数组遍历方案确定最小数;树选择,是通过“锦标赛”类似的思想,让两数相比,不断淘汰较大(小)者,最终选出最小(大)数;而堆排序,是利用堆这种数据结构的性质,通过堆元素的删除、调整等一系列操作将最小数选出放在堆顶。堆排序中的堆建立、堆调整是重要考点。(4 )归并排序,是通过“归并 ”这种操作完成排序的目的,既然是归并就必须是两者以上的数据集合才可能实现归并。所以,在归并排序中,关注最多的就是 2 路归并。算法思想比较简单,有一点,要铭记在心:归并排序是稳定排序。(5 )基数
23、排序,是一种很特别的排序方法,也正是由于它的特殊,所以,基数排序就比较适合于一些特别的场合,比如扑克牌排序问题等。基数排序,又分为两种:多关键字的排序(扑克牌排序) ,链式排序(整数排序) 。基数排序的核心思想也是利用“基数空间”这个概念将问题规模规范、变小,并且,在排序的过程中,只要按照基排的思想,是不用进行关键字比较的,这样得出的最终序列就是一个有序序列。本章各种排序算法的思想以及伪代码实现,及其时间复杂度都是必须掌握的。/稳定性分析/排序算法的稳定性,通俗地讲就是能保证排序前 2 个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。稳定性的好处:若排序算法如果是稳定的,那
24、么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,对基于比较的排序算法而言,元素交换的次数可能会少一些(个人感觉,没有证实)。分析一下常见的排序算法的稳定性,每个都给出简单的理由。(1) 冒泡排序冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,我想你是不会再无聊地把他们俩交换一下的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会
25、交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。(2) 选择排序选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第 n-1 个元素,第 n 个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列 5 8 5 2 9,我们知道第一遍选择第 1 个元素 5 会和 2 交换,那么原序列中 2 个 5 的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。(3
26、) 插入排序插入排序是在一个已经有序的小序列的基础上,一次插入一个元素。当然,刚开始这个有序的小序列只有 1 个元素,就是第一个元素。比较是从有序序列的末尾开始,也就是想要插入的元素和已经有序的最大者开始比起,如果比它大则直接插入在其后面,否则一直往前找直到找到它该插入的位置。如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。(4) 快速排序快速排序有两个方向,左边的 i 下标一直往右走,当 ai acenter_index。如果 i 和 j 都走不动了,i j。交换 a
27、j和 acenter_index,完成一趟快速排序。在中枢元素和 aj交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为 5 3 3 4 3 8 9 10 11,现在中枢元素 5 和3(第 5 个元素,下标从 1 开始计)交换就会把元素 3 的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和 aj 交换的时刻。(5) 归并排序归并排序是把序列递归地分成短序列,递归出口是短序列只有 1 个元素( 认为直接有序)或者 2 个序列(1 次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在 1 个或 2 个元素时, 1 个
28、元素不会交换,2 个元素如果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳定的排序算法。(6) 基数排序基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以其是稳定的排序算法。(7) 希尔排序(shell)
29、希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比 o(n2)好一些。由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell 排序是不稳定的。(8) 堆排序我们知道堆的结构是节点 i 的孩子为 2*i 和 2*i+1 节点,大顶堆要求父节点大于等于其 2 个子节点,小顶堆要求父节点小于等于其 2 个子节点。在一个长为
30、n 的序列,堆排序的过程是从第 n/2 开始和其子节点共 3 个值选择最大( 大顶堆)或者最小 (小顶堆),这 3 个元素之间的选择当然不会破坏稳定性。但当为 n /2-1, n/2-2, .1 这些个父节点选择元素时,就会破坏稳定性。有可能第 n/2 个父节点交换把后面一个元素交换过去了,而第 n/2-1 个父节点把后面一个相同的元素没有交换,那么这 2 个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法。/冒泡排序插入排序二路插入排序希尔排序快速排序选择排序归并排序堆排序算法的 C/C+实现/#include using namespace std;/交换两个数的值void
31、 swap(int tmp=a;a=b;b=tmp;/屏幕输出数组void display(int array,int len)cout= 0;i- )for(int j = 0;j arrayj+1)swap(arrayj,arrayj+1);/*直接插入排序算法思想:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有 n-1 个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复 n-1 次可完成排序过程。时间复杂度 o(n2)空间复杂度 o(1)比较次数 n(n+1)/2*/void inser
32、t_sort(int array,int len)int tmp,i,j;for(i = 1;i = 0;j-)/往后移if (arrayj tmp)arrayj+1 =arrayj;elsearrayj+1 = tmp;break;if(j = -1)arrayj+1 = tmp;/*2-路插入排序算法思想:增加一个辅助空间 d,把 r1赋值给 d1,并将 d1看成是排好序后处于中间位置的记录。然后从 r2开始依次插入到 d1之前或之后的有序序列中。时间复杂度 o(n2)空间复杂度 o(1)比较次数 n(n+1)/2*/void bi_insert_sort(int array,int le
33、n)int* arr_d = (int*)malloc(sizeof(int) * len);arr_d0 = array0;int head = 0,tail = 0;for (int i = 1;i arr_d0)int j;for ( j= tail;j0;j-)if (arrayi arr_dj)arr_dj-1 =arr_dj;elsebreak;arr_dj-1 = arrayi;head -= 1;for (int i = 0;i = len) pos -= len;arrayi = arr_dpos;free(arr_d);/*希尔排序算法思想:先将整个待排序记录分割成若干子序
34、列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序时间复杂度 o(n2)空间复杂度 o(1)比较次数 ?*/void shell_insert(int array,int d,int len)int tmp,j;for (int i = d;i = 0 /*快速排序算法思想:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合成为原问题的解。时间复杂度 o(nlogn)空间复杂度 o(logn)比较次数 ?*/void quick_sort(int array,int low,int high)if (low
35、= pivotkey)-high;swap(arraylow,arrayhigh);while(low arrayi)ret = i;return ret;void select_sort(int array,int len)for (int i = 0; i = arrayj) break;arrayi = arrayj; i = j;arrayi = rc;void heap_sort(int array,int len)int i;for(i = (len-1)/2; i = 0; i-)heap_adjust(array,i,len);for( i = (len-1); i 0; i-
36、)swap(array0,arrayi); /弹出最大值,重新对 i-1 个元素建堆heap_adjust(array,0,i-1);int main() int array = 45, 56, 76, 234, 1, 34,23, 2, 3, 55, 88, 100;int len = sizeof(array)/sizeof(int);/bubble_sort(array,len); /冒泡排序/*insert_sort(array,len);*/ /插入排序/*bi_insert_sort(array,len);*/ /二路插入排序/*shell_sort(array,len);*/ /
37、希尔排序/*quick_sort(array,0,len-1);*/ /快速排序/*select_sort(array,len);*/ /选择排序/*merge_sort(array,0,len-1);*/ /归并排序heap_sort(array,len); /堆排序display(array,len);return 0;对排序算法的总结按平均时间将排序分为四类:(1 )平方阶(O(n2)排序一般称为简单排序,例如直接插入、直接选择和冒泡排序;(2 )线性对数阶(O(nlgn) 排序如快速、堆和归并排序;(3 ) O(n1+)阶排序是介于 0 和 1 之间的常数,即 0 IP 地址寻找)以及
38、许多其他协议。一旦从应用程序来的数据被编码成一个标准的应用层协议,它将被传送到 IP 栈的下一层。在传输层,应用程序最常用的是 TCP 或者 UDP,并且服务器应用程序经常与一个公开的端口号相联系。服务器应用程序的端口由 InternetAssigned Numbers Authority(IANA)正式地分配,但是现今一些新协议的开发者经常选择它们自己的端口号。由于在同一个系统上很少超过少数几个的服务器应用,端口冲突引起的问题很少。应用软件通常也允许用户强制性地指定端口号作为运行参数。连结外部的客户端程序通常使用系统分配的一个随机端口号。监听一个端口并且然后通过服务器将那个端口发送到应用的另
39、外一个副本以建立对等连结(如 IRC 上的 dcc 文件传输)的应用也可以使用一个随机端口,但是应用程序通常允许定义一个特定的端口范围的规范以允许端口能够通过实现网络地址转换(NAT)的路由器映射到内部。每一个应用层(TCP/IP 参考模型的最高层)协议一般都会使用到两个传输层协议之一:面向连接的 TCP 传输控制协议和无连接的包传输的 UDP 用户数据报文协议。常用的应用层协议有:运行在 TCP 协议上的协议:HTTP(HypertextTransfer Protocol,超文本传输协议) ,主要用于普通浏览。HTTPS( HypertextTransfer Protocol over Se
40、cure Socket Layer, or HTTP over SSL,安全超文本传输协议),HTTP 协议的安全版本。FTP(File Transfer Protocol,文件传输协议) ,由名知义,用于文件传输。POP3(PostOffice Protocol, version 3,邮局协议) ,收邮件用。SMTP( SimpleMail Transfer Protocol,简单邮件传输协议) ,用来发送电子邮件。TELNET(Teletypeover the Network,网络电传) ,通过一个终端(terminal)登陆到网络。SSH(Secure Shell,用于替代安全性差的 T
41、ELNET) ,用于加密安全登陆。运行在 UDP 协议上的协议:BOOTP( BootProtocol,启动协议) ,应用于无盘设备。NTP(Network Time Protocol,网络时间协议) ,用于网络同步。其他:DNS(Domain Name Service,域名服务) ,用于完成地址查找,邮件转发等工作(运行在TCP 和 UDP 协议上) 。ECHO(EchoProtocol,回绕协议) ,用于查错及测量应答时间(运行在 TCP 和 UDP 协议上) 。SNMP( SimpleNetwork Management Protocol,简单网络管理协议) ,用于网络信息的收集和网络管
42、理。DHCP(DynamicHost Configuration Protocol,动态主机配置协议) ,动态配置 IP 地址。ARP(Address Resolution Protocol,地址解析协议) ,用于动态解析以太网硬件的地址。传输层传输层的协议,能够解决诸如可靠性(“数据是否已经到达目的地?” )和保证数据按照正确的顺序到达这样的问题。在 TCP/IP 协议组中,传输协议也包括所给数据应该送给哪个应用程序。在 TCP/IP 协议组中技术上位于这个层的动态路由协议通常被认为是网络层的一部分;一个例子就是 OSPF(IP 协议 89) 。TCP(IP 协议 6)是一个“可靠的” 、面
43、向连结的传输机制,它提供一种可靠的字节流保证数据完整、无损并且按顺序到达。TCP 尽量连续不断地测试网络的负载并且控制发送数据的速度以避免网络过载。另外,TCP 试图将数据按照规定的顺序发送。这是它与 UDP 不同之处,这在实时数据流或者路由高网络层丢失率应用的时候可能成为一个缺陷。较新的 SCTP 也是一个 “可靠的 ”、面向连结的传输机制。它是面向纪录而不是面向字节的,它在一个单独的连结上提供了通过多路复用提供的多个子流。它也提供了多路自寻址支持,其中连结终端能够被多个 IP 地址表示(代表多个物理接口) ,这样的话即使其中一个连接失败了也不中断。它最初是为电话应用开发的(在 IP 上传输
44、 SS7) ,但是也可以用于其他的应用。UDP(IP 协议号 17)是一个无连结的数据报协议。它是一个“best effort”或者“不可靠”协议不是因为它特别不可靠,而是因为它不检查数据包是否已经到达目的地,并且不保证它们按顺序到达。如果一个应用程序需要这些特点,它必须自己提供或者使用 TCP。UDP 的典型性应用是如流媒体(音频和视频等)这样按时到达比可靠性更重要的应用,或者如 DNS 查找这样的简单查询响应应用,如果建立可靠的连结所作的额外工作将是不成比例地大。DCCP 目前正由 IEFT 开发。它提供 TCP 流动控制语义,但对于用户来说保留了 UDP 的数据报服务模型。TCP 和 U
45、DP 都用来支持一些高层的应用。任何给定网络地址的应用通过它们的 TCP 或者UDP 端口号区分。根据惯例使一些大众所知的端口与特定的应用相联系。RTP 是为如音频和视频流这样的实时数据设计的数据报协议。RTP 是使用 UDP 包格式作为基础的会话层,然而据说它位于因特网协议栈的传输层。网络互连层正如最初所定义的,网络层解决在一个单一网络上传输数据包的问题。类似的协议有 X.25和 ARPANET 的 Host/IMP Protocol。随着因特网思想的出现,在这个层上添加了附加的功能,也就是将数据从源网络传输到目的网络。这就牵涉到在网络组成的网上选择路径将数据包传输,也就是因特网。在因特网协
46、议组中,IP 完成数据从源发送到目的基本任务。IP 能够承载多种不同的高层协议的数据;这些协议使用一个唯一的 IP 协议号进行标识。ICMP 和 IGMP 分别是 1 和 2。一些 IP 承载的协议,如 ICMP(用来发送关于 IP 发送的诊断信息)和 IGMP(用来管理多播数据) ,它们位于 IP 层之上但是完成网络层的功能,这表明了因特网和 OSI 模型之间的不兼容性。所有的路由协议,如 BGP、 OSPF、和 RIP 实际上也是网络层的一部分,尽管似乎它们应该属于更高的协议栈。网络接口层网络接口层实际上并不是因特网协议组中的一部分,但是它是数据包从一个设备的网络层传输到另外一个设备的网络
47、层的方法。这个过程能够在网卡的软件驱动程序中控制,也可以在韧体或者专用芯片中控制。这将完成如添加报头准备发送、通过物理媒介实际发送这样一些数据链路功能。另一端,链路层将完成数据帧接收、去除报头并且将接收到的包传到网络层。然而,链路层并不经常这样简单。它也可能是一个虚拟专有网络(VPN)或者隧道,在这里从网络层来的包使用隧道协议和其他(或者同样的)协议组发送而不是发送到物理的接口上。VPN 和隧道通常预先建好,并且它们有一些直接发送到物理接口所没有的特殊特点(例如,它可以加密经过它的数据) 。由于现在链路“层”是一个完整的网络,这种协议组的递归使用可能引起混淆。但是它是一个实现常见复杂功能的一个优秀方法。 (尽管需要注意预防一个已经封装并且经隧道发送下去的数据包进行再次地封装和发送) 。/