收藏 分销(赏)

清华大学新版数据结构考研要点(新版).doc

上传人:精**** 文档编号:2132376 上传时间:2024-05-17 格式:DOC 页数:29 大小:1.65MB 下载积分:10 金币
下载 相关 举报
清华大学新版数据结构考研要点(新版).doc_第1页
第1页 / 共29页
清华大学新版数据结构考研要点(新版).doc_第2页
第2页 / 共29页


点击查看更多>>
资源描述
<p>1、数据(Data) :是客观事物的符号表示。在计算机科学中指的是所有能输入到计算机中并被计算机程序处理的符号的总称。 数据元素(Data Element) :是数据的基本单位,在程序中通常作为一个整体来进行考虑和处理。 一个数据元素可由若干个数据项(Data Item)组成。数据项是数据的不可分割的最小单位。数据项是对客观事物某一方面特性的数据描述。 &nbsp; &nbsp; &nbsp; 数据对象(Data Object):是性质相同的数据元素的集合,是数据的一个子集。如字符集合C={‘A’,’B’,’C,…} 。 数据结构(Data Structure):是指相互之间具有(存在)一定联系(关系)的数据元素的集合。元素之间的相互联系(关系)称为逻辑结构。数据元素之间的逻辑结构有四种基本类型,如图1-3所示。 ① 集合:结构中的数据元素除了“同属于一个集合”外,没有其它关系。 ② 线性结构:结构中的数据元素之间存在一对一的关系。 ③ 树型结构:结构中的数据元素之间存在一对多的关系。 ④ 图状结构或网状结构:结构中的数据元素之间存在多对多的关系。 2、顺序结构:数据元素存放的地址是连续的; 链式结构:数据元素存放的地址是否连续没有要求。 数据的逻辑结构和物理结构是密不可分的两个方面,一个算法的设计取决于所选定的逻辑结构,而算法的实现依赖于所采用的存储结构。 &nbsp; 在C语言中,用一维数组表示顺序存储结构;用结构体类型表示链式存储结构。 3、C语言中用带指针的结构体类型来描述 typedef &nbsp;struct &nbsp;Lnode { &nbsp; ElemType &nbsp;data; &nbsp; &nbsp; /*数据域,保存结点的值 */ struct &nbsp; Lnode &nbsp;*next; &nbsp; &nbsp; &nbsp;/*指针域*/ }LNode; &nbsp; &nbsp; &nbsp; &nbsp;/*结点的类型 */ 4、循环队列为空:front=rear 。 &nbsp; 循环队列满:(rear+1)%MAX_QUEUE_SIZE =front。 5、性质1:在非空二叉树中,第i层上至多有2i-1个结点(i≧1)。 性质2:深度为k的二叉树至多有2k-1个结点(k≧1) 。 性质3:对任何一棵二叉树,若其叶子结点数为n0,度为2的结点数为n2,则n0=n2+1。 一棵深度为k且有2k-1个结点的二叉树称为满二叉树(Full Binary Tree)。 完全二叉树的特点:若完全二叉树的深度为k ,则所有的叶子结点都出现在第k层或k-1层。对于任一结点,如果其右子树的最大层次为l,则其左子树的最大层次为l或l+1。 性质4:n个结点的完全二叉树深度为:ë㏒2nû +1。 性质5:若对一棵有n个结点的完全二叉树(深度为└㏒2n┘+1)的结点按层(从第1层到第ë㏒2nû +1层)序自左至右进行编号,则对于编号为i(1≦i≦n)的结点: ⑴ 若i=1:则结点i是二叉树的根,无双亲结点;否则,若i&gt;1,则其双亲结点编号是 ëi/2û 。 ⑵ 如果2i&gt;n:则结点i为叶子结点,无左孩子;否则,其左孩子结点编号是2i。 ⑶ 如果2i+1&gt;n:则结点i无右孩子;否则,其右孩子结点编号是2i+1。 6、线索二叉树:设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用。则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息。 7、Huffman树:具有n个叶子结点(每个结点的权值为wi) 的二叉树不止一棵,但在所有的这些二叉树中,必定存在一棵WPL值最小的树,称这棵树为Huffman树(或称最优树) 。 8、完全无向图:对于无向图,若图中顶点数为n ,用e表示边的数目,则e Î[0,n(n-1)/2] 。具有n(n-1)/2条边的无向图称为完全无向图。 完全有向图:对于有向图,若图中顶点数为n ,用e表示弧的数目,则eÎ[0,n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图。 生成树、生成森林:一个连通图(无向图)的生成树是一个极小连通子图,它含有图中全部n个顶点和只有足以构成一棵树的n-1条边,称为图的生成树 关于无向图的生成树的几个结论: 1) 一棵有n个顶点的生成树有且仅有n-1条边; 2) 如果一个图有n个顶点和小于n-1条边,则是非连通图; 3) 如果多于n-1条边,则一定有环; 4) 有n-1条边的图不一定是生成树。 9、最小生成树(Minimum Spanning Tree) :带权连通图中代价最小的生成树称为最小生成树。 最小生成树在实际中具有重要用途,如设计通信网。设图的顶点表示城市,边表示两个城市之间的通信线路,边的权值表示建造通信线路的费用。n个城市之间最多可以建n´(n-1)/2条线路,如何选择其中的n-1条,使总的建造费用最低? 10、工程完成最短时间:从起点到终点的最长路径长度(路径上各活动持续时间之和) 。长度最长的路径称为关键路径,关键路径上的活动称为关键活动。关键活动是影响整个工程的关键。 11、查找方法比较 顺序查找 折半查找 分块查找 ASL 最大 最小 两者之间 表结构 有序表、无序表 有序表 分块有序表 存储结构 顺序存储结构 线性链表 顺序存储结构 顺序存储结构 线性链表 12、在随机情况下,二叉排序树的平均查找长度ASL和㏒(n)(树的深度)是等数量级的。 二叉排序树(Binary Sort Tree或Binary Search Tree) 的定义为:二叉排序树或者是空树,或者是满足下列性质的二叉树。 (1) :若左子树不为空,则左子树上所有结点的值(关键字)都小于根结点的值; (2) :若右子树不为空,则右子树上所有结点的值(关键字)都大于根结点的值; (3) :左、右子树都分别是二叉排序树。 结论:若按中序遍历一棵二叉排序树,所得到的结点序列是一个递增序列。 13、平衡二叉树或者是空树,或者是满足下列性质的二叉树。 ⑴:左子树和右子树深度之差的绝对值不大于1; ⑵:左子树和右子树也都是平衡二叉树。 &nbsp; &nbsp;平衡因子(Balance Factor) :二叉树上结点的左子树的深度减去其右子树深度称为该结点的平衡因子。 平衡二叉排序树上进行查找的平均查找长度和㏒2n是一个数量级的,平均时间复杂度为O(㏒2n)。 四种平衡化旋转,其正确性容易由“遍历所得中序序列不变”来证明。并且,无论是哪种情况,平衡化旋转处理完成后,形成的新子树仍然是平衡二叉排序树,且其深度和插入前以a为根结点的平衡二叉排序树的深度相同。所以,在平衡二叉排序树上因插入结点而失衡,仅需对失衡子树做平衡化旋转处理。 14、一棵m阶B_树,或者是空树,或者是满足以下性质的m叉树: ⑴ 根结点或者是叶子,或者至少有两棵子树,至多有m棵子树; ⑵ 除根结点外,所有非终端结点至少有ém/2ù棵子树,至多有m棵子树; ⑶ 所有叶子结点都在树的同一层上; ⑷ 每个结点应包含如下信息: &nbsp; &nbsp; &nbsp; (n,A0,K1,A1,K2,A2,… ,Kn,An) 其中Ki(1≤i≤n)是关键字,且Ki&lt;Ki+1 (1≤i≤n-1);Ai(i=0,1,… ,n)为指向孩子结点的指针,且Ai-1所指向的子树中所有结点的关键字都小于Ki ,Ai所指向的子树中所有结点的关键字都大于Ki ;n是结点中关键字的个数,且ëm/2û-1≤n≤m-1,n+1为子树的棵数。 根据m阶B_树的定义,第一层上至少有1个结点,第二层上至少有2个结点;除根结点外,所有非终端结点至少有ém/2ù棵子树,…,第h层上至少有ém/2ùh-2个结点。在这些结点中:根结点至少包含1个关键字,其它结点至少包含ém/2ù-1个关键字,设s=ém/2ù,则总的关键字数目n满足: 因此有: h≦1+ ㏒s((n+1)/2)=1+㏒ém/2ù((n+1)/2) &nbsp; &nbsp; &nbsp; &nbsp;即在含有n个关键字的B_树上进行查找时,从根结点到待查找记录关键字的结点的路径上所涉及的结点数不超过1+ ㏒ém/2ù((n+1)/2) 。 15、m阶B+树。 它与B_树的主要不同是叶子结点中存储记录。在B+树中,所有的非叶子结点可以看成是索引,而其中的关键字是作为“分界关键字”,用来界定某一关键字的记录所在的子树。一棵m阶B+树与m阶B_树的主要差异是: ⑴ 若一个结点有n棵子树,则必含有n个关键字; ⑵ 所有叶子结点中包含了全部记录的关键字信息以及这些关键字记录的指针,而且叶子结点按关键字的大小从小到大顺序链接; ⑶ &nbsp;所有的非叶子结点可以看成是索引的部分,结点中只含有其子树的根结点中的最大(或最小)关键字。 16、哈希函数:在记录的关键字与记录的存储地址之间建立的一种对应关系叫哈希函数。 哈希函数是一种映象,是从关键字空间到存储地址空间的一种映象。可写成:addr(ai)=H(ki) ,其中i是表中一个元素,addr(ai)是ai的地址, ki是ai的关键字。 哈希表:应用哈希函数,由记录的关键字确定记录在表中的地址,并将记录放入此地址,这样构成的表叫哈希表。 哈希查找(又叫散列查找):利用哈希函数进行查找的过程叫哈希查找。 例1 :设散列表长为7,记录关键字组为:15, 14, 28, 26, 56, 23,散列函数:H(key)=key &nbsp; MOD &nbsp;7,冲突处理采用线性探测法。 解:H(15)=15 &nbsp;MOD 7=1 &nbsp; &nbsp; &nbsp; &nbsp; H(14)=14 &nbsp;MOD 7=0 H(28)=28 &nbsp;MOD 7=0 &nbsp;冲突 &nbsp; H1(28)=1 &nbsp;又冲突H2(28)=2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; H(26)=26 &nbsp;MOD 7=5 H(56)=56 &nbsp;MOD 7=0 &nbsp; &nbsp; 冲突 &nbsp; &nbsp; &nbsp;H1(56)=1 &nbsp; &nbsp; 又冲突 H2(56)=2 &nbsp; 又冲突 &nbsp; &nbsp;H3(56)=3 H(23)=23 &nbsp;MOD 7=2 &nbsp; &nbsp; 冲突 &nbsp; &nbsp; &nbsp;H1(23)=3 &nbsp; &nbsp; 又冲突 H3(23)=4 各种散列函数所构造的散列表的ASL如下: 17、排序的稳定性 &nbsp; &nbsp;若记录序列中有两个或两个以上关键字相等的记录: Ki =Kj(i≠j,i, j=1, 2, … n),且在排序前Ri先于Rj(i&lt;j),排序后的记录序列仍然是Ri先于Rj,称排序方法是稳定的,否则是不稳定的。 排序的分类 &nbsp; 待排序的记录数量不同,排序过程中涉及的存储器的不同,有不同的排序分类。 ① &nbsp;待排序的记录数不太多:所有的记录都能存放在内存中进行排序,称为内部排序; ② &nbsp;待排序的记录数太多:所有的记录不可能存放在内存中, 排序过程中必须在内、外存之间进行数据交换,这样的排序称为外部排序。 18、插入排序采用的是以 “玩桥牌者”的方法为基础的。即在考察记录Ri之前,设以前的所有记录R1, R2 ,…., Ri-1已排好序,然后将Ri插入到已排好序的诸记录的适当位置。 ============================================================================ 最基本的插入排序是直接插入排序(Straight Insertion Sort) 。 ⑴ 最好情况:若待排序记录按关键字从小到大排列(正序),算法中的内循环无须执行,则一趟排序时:关键字比较次数1次,记录移动次数2次 ⑵ 最坏情况:若待排序记录按关键字从大到小排列(逆序),则一趟排序时:算法中的内循环体执行i-1,关键字比较次数i次,记录移动次数i+1。 一般地,认为待排序的记录可能出现的各种排列的概率相同,则取以上两种情况的平均值,作为排序的关键字比较次数和记录移动次数,约为n2/4,则复杂度为O(n2) 。 算法实现 void straight_insert_sort(Sqlist *L) { &nbsp;int i, j ; for (i=2; i&lt;=l-&gt;length; i++) { &nbsp;L-&gt;R[0]=L-&gt;R[i]; j=i-1; &nbsp; &nbsp; /* &nbsp; 设置哨兵 &nbsp; */ while( LT(L-&gt;R[0].key, L-&gt;R[j].key) ) &nbsp; &nbsp; { &nbsp; L-&gt;R[j+1]=L-&gt;R[j]; &nbsp; &nbsp; &nbsp; &nbsp; j--; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* &nbsp; 查找插入位置 &nbsp; */ L-&gt;R[j+1]=L-&gt;R[0]; &nbsp; &nbsp; &nbsp;/* &nbsp; 插入到相应位置 &nbsp; */ } } ===============================================================================折半插入排序 当将待排序的记录R[i] 插入到已排好序的记录子表R[1…i-1]中时,由于R1, R2 ,…, Ri-1已排好序,则查找插入位置可以用“折半查找”实现,则直接插入排序就变成为折半插入排序。 从时间上比较,折半插入排序仅仅减少了关键字的比较次数,却没有减少记录的移动次数,故时间复杂度仍然为O(n2) 。 排序示例: 设有一组关键字30, 13, 70, 85, 39, 42, 6, 20,采用折半插入排序方法排序的过程 ⑴ 算法实现 void Binary_insert_sort(Sqlist *L) { &nbsp;int i, j, low, high, mid ; for (i=2; i&lt;=l-&gt;length; i++) { &nbsp;L-&gt;R[0]=L-&gt;R[i]; &nbsp; &nbsp; &nbsp;/* &nbsp; 设置哨兵 &nbsp; */ &nbsp; &nbsp;low=1 ; high=i-1 ; while (low&lt;=high) if=&quot;&quot; l-=&quot;&quot;&gt;R[0].key, L-&gt;R[mid].key) ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;high=mid-1 ; &nbsp; &nbsp; &nbsp; &nbsp;else &nbsp; low=mid+1 ; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; /* &nbsp; 查找插入位置 &nbsp; */ for (j=i-1; j&gt;=high+1; j--) L-&gt;R[j+1]=L-&gt;R[j]; L-&gt;R[high+1]=L-&gt;R[0]; &nbsp;/* &nbsp; 插入到相应位置 &nbsp; */ }} ============================================================================== 2-路插入排序 排序示例:设有初始关键字集合{49, 38, 65, 13, 97, 27, 76} ,采用2-路插入排序的过程 例:设有关键字集合{49, 38, 65, 97, 76, 13, 27, 49} ,采用表插入排序的过程 ===============================================================================希尔排序(Shell Sort,又称缩小增量法)是一种分组插入排序方法。 排序示例 设有10个待排序的记录,关键字分别为9, 13, 8, 2, 5, 13, 7, 1, 15, 11,增量序列是5, 3, 1,希尔排序的过程: 算法实现 &nbsp; &nbsp; &nbsp; 先给出一趟希尔排序的算法,类似直接插入排序。 void shell_pass(Sqlist *L, int d) &nbsp; /* &nbsp;对顺序表L进行一趟希尔排序, 增量为d &nbsp;*/ { &nbsp;int j, k ; for (j=d+1; j&lt;=l-&gt;length; j++) { &nbsp;L-&gt;R[0]=L-&gt;R[j] ; &nbsp; &nbsp; &nbsp; &nbsp;/* &nbsp;设置监视哨兵 &nbsp;*/ k=j-d ; while (k&gt;0&amp;&amp;LT(L-&gt;R[0].key, L-&gt;R[k].key) ) &nbsp; &nbsp;{L-&gt;R[k+d]=L-&gt;R[k] ; k=k-d ; &nbsp; } L-&gt;R[k+j]=L-&gt;R[0] ; } } void shell_sort(Sqlist *L, int dk[], int t) &nbsp; &nbsp; /* &nbsp; &nbsp;按增量序列dk[0 … t-1],对顺序表L进行希尔排序 &nbsp; */ { &nbsp;int m ; for (m=0; m&lt;=t; m++) shll_pass(L, dk[m]) ; } ===============================================================================冒泡排序 排序示例 : &nbsp; &nbsp; &nbsp; 设有9个待排序的记录,关键字分别为23, 38, 22, 45, 23, 67, 31, 15, 41,冒泡排序的过程: void Bubble_Sort(Sqlist *L) { &nbsp;int j ,k , flag ; for (j=0; j</p><l->length; j++) &nbsp; &nbsp; &nbsp; /* &nbsp; 共有n-1趟排序 &nbsp; */ { &nbsp;flag=TRUE ; for (k=1; k&lt;=l-&gt;length-j; k++) &nbsp; /* &nbsp; 一趟排序 &nbsp; */ &nbsp; &nbsp;if (LT(L-&gt;R[k+1].key, L-&gt;R[k].key ) ) &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; flag=FALSE ; L-&gt;R[0]=L-&gt;R[k] ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;L-&gt;R[k]=L-&gt;R[k+1] ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;L-&gt;R[k+1]=L-&gt;R[0] ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp;if &nbsp;(flag==TRUE) &nbsp;break ; } } 算法分析: 时间复杂度: 最好情况(正序):比较次数:n-1;移动次数:0; 最坏情况(逆序): 故时间复杂度:T(n)=O(n²) 空间复杂度:S(n)=O(1) ===============================================================================快速排序的平均时间复杂度是:T(n)=O(n㏒2n) &nbsp; &nbsp; &nbsp; 从所需要的附加空间来看,快速排序算法是递归调用,系统内用堆栈保存递归参数,当每次划分比较均匀时,栈的最大深度为[㏒2n]+1 。 &nbsp; &nbsp; &nbsp;∴ 快速排序的空间复杂度是:S(n)=O(㏒2n) &nbsp; &nbsp; &nbsp;从排序的稳定性来看,快速排序是不稳定的。 一趟排序示例 &nbsp; &nbsp; &nbsp; 设有6个待排序的记录,关键字分别为29, 38, 22, 45, 23, 67,一趟快速排序的过程 算法实现 ⑴ &nbsp;一趟快速排序算法的实现 int &nbsp;quick_one_pass(Sqlist &nbsp;*L , int low, int high) L-&gt;R[0]=L-&gt;R[i] ; &nbsp; &nbsp; &nbsp; /* &nbsp; R[0]作为临时单元和哨兵 &nbsp;*/ do { while (LQ(L-&gt;R[0].key, L-&gt;R[j].key)&amp;&amp;(j&gt;i)) &nbsp; &nbsp; j-- ; if &nbsp;(j&gt;i) &nbsp;{ &nbsp;L-&gt;R[i]=L-&gt;R[j] ; i++; &nbsp; } while (LQ(L-&gt;R[i].key, L-&gt;R[0].key)&amp;&amp;(j&gt;i)) &nbsp; &nbsp; &nbsp;i++ ; if &nbsp;(j&gt;i) &nbsp;{ &nbsp;L-&gt;R[j]=L-&gt;R[i] ; j--; &nbsp; } } while(i!=j) ; &nbsp; &nbsp;/* &nbsp; i=j时退出扫描 &nbsp;*/ L-&gt;R[i]=L-&gt;R[0] ; return(i) ; } 递归算法 void &nbsp;quick_Sort(Sqlist &nbsp;*L , int low, int high) { &nbsp;int k ; if &nbsp;(low&lt;high) { &nbsp;k=quick_one_pass(L, low, high); quick_Sort(L, low, k-1); quick_Sort(L, k+1, high); } &nbsp; &nbsp; /* &nbsp; 序列分为两部分后分别对每个子序列排序 &nbsp; */ } 非递归算法 # define &nbsp;MAX_STACK &nbsp;100 void &nbsp;quick_Sort(Sqlist &nbsp;*L , int low, int high) { &nbsp;int k , stack[MAX_STACK] , &nbsp;top=0; do { &nbsp;while &nbsp;(low&lt;high) { &nbsp;k=quick_one_pass(L,low,high); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;stack[++top]=high ; &nbsp;stack[++top]=k+1 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* &nbsp;第二个子序列的上,下界分别入栈 &nbsp;*/ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;high=k-1 ; &nbsp; } if (top!=0) &nbsp; { &nbsp;low=stack[top--] ; high=stack[top--] ; &nbsp;} &nbsp; &nbsp; }while (top!=0&amp;&amp;low&lt;high) ; } ==============================================================================简单选择排序(Simple Selection Sort ,又称为直接选择排序) 排序示例 &nbsp; &nbsp; &nbsp; 例:设有关键字序列为:7, 4, -2, 19, 13, 6,直接选择排序的过程 算法实现 void simple_selection_sort(Sqlist *L) { &nbsp;int m, n , k; for (m=1; m<l->length; m++) { &nbsp;k=m ; for &nbsp;(n=m+1; n&lt;=l-&gt;length; n++) &nbsp; &nbsp; &nbsp; if &nbsp;( LT(L-&gt;R[n].key, L-&gt;R[k].key) ) &nbsp;k=n ; if &nbsp;(k!=m) &nbsp; &nbsp; &nbsp;/* &nbsp; 记录交换 &nbsp; */ &nbsp; &nbsp;{ &nbsp;L-&gt;R[0]=L-&gt;R[m]; L-&gt;R[m]=L-&gt;R[k]; &nbsp; &nbsp; &nbsp; &nbsp;L-&gt;R[k]=L-&gt;R[0]; &nbsp; &nbsp; } } } 算法分析 &nbsp; &nbsp;整个算法是二重循环:外循环控制排序的趟数,对n个记录进行排序的趟数为n-1趟;内循环控制每一趟的排序。 进行第i趟排序时,关键字的比较次数为n-i,则: ∴ &nbsp;时间复杂度是:T(n)=O(n2) &nbsp; &nbsp;空间复杂度是:S(n)=O(1) &nbsp; &nbsp; &nbsp;从排序的稳定性来看,直接选择排序是不稳定的。 ===============================================================================堆的定义 &nbsp; &nbsp; &nbsp; &nbsp;是n个元素的序列H={k1, k2 , … kn} ,满足: 堆的性质 ① &nbsp;堆是一棵采用顺序存储结构的完全二叉树, k1是根结点; ② &nbsp;堆的根结点是关键字序列中的最小(或最大)值,分别称为小(或大)根堆; ③ &nbsp;从根结点到每一叶子结点路径上的元素组成的序列都是按元素值(或关键字值)非递减(或非递增)的; (4)堆中的任一子树也是堆。 &nbsp;利用堆顶记录的关键字值最小(或最大)的性质,从当前待排序的记录中依次选取关键字最小(或最大)的记录,就可以实现对数据记录的排序,这种排序方法称为堆排序。 堆排序思想 ① &nbsp;对一组待排序的记录,按堆的定义建立堆; ② &nbsp;将堆顶记录和最后一个记录交换位置,则前n-1个记录是无序的,而最后一个记录是有序的; ③ &nbsp;堆顶记录被交换后,前n-1个记录不再是堆,需将前n-1个待排序记录重新组织成为一个堆,然后将堆顶记录和倒数第二个记录交换位置,即将整个序列中次小关键字值的记录调整(排除)出无序区; ④ &nbsp;重复上述步骤,直到全部记录排好序为止。 结论:排序过程中,若采用小根堆,排序后得到的是非递减序列;若采用大根堆,排序后得到的是非递增序列。 堆排序算法实现 &nbsp; &nbsp; &nbsp; &nbsp;堆的根结点是关键字最小的记录,输出根结点后,是以序列的最后一个记录作为根结点,而原来堆的左、右子树都是堆,则进行一次筛选就可以成为堆。 void &nbsp;Heap_Sort(Sqlist *H) { &nbsp;int j ; for (j=H-&gt;length/2; j&gt;0; j--) Heap_adjust(H, j , H-&gt;length) ; &nbsp; /* &nbsp; 初始建堆 &nbsp; */ for (j=H-&gt;length/2; j&gt;=1; j--) { &nbsp;H-&gt;R[0]=H-&gt;R[1] ; H-&gt;R[1]=H-&gt;R[j] ; H-&gt;R[j]=H-&gt;R[0] ; &nbsp; /* &nbsp; 堆顶与最后一个交换 &nbsp; */ Heap_adjust(H, 1, j-1) ; } } 堆排序的比较次数的数量级为: T(n)=O(n㏒2n);而附加空间就是交换时所用的临时空间,故空间复杂度为: S(n)=O(1) ===============================================================================归并(Merging) :是指将两个或两个以上的有序序列合并成一个有序序列。若采用线性表(无论是那种存储结构)易于实现,其时间复杂度为O(m+n) 。 归并排序的算法 &nbsp; &nbsp; &nbsp; &nbsp;开始归并时,每个记录是长度为1的有序子序列,对这些有序子序列逐趟归并,每一趟归并后有序子序列的长度均扩大一倍;当有序子序列的长度与整个记录序列长度相等时,整个记录序列就成为有序序列。算法是: void Merge_sort(Sqlist *L, RecType DR[]) { &nbsp;int d=1 ; while(d<l->length) { &nbsp; Merge_pass(L-&gt;R, DR, d, L-&gt;length) ; Merge_pass(DR, L-&gt;R, 2*d, L-&gt;length) ; d=4*d ; } } 具有n个待排序记录的归并次数是㏒2n,而一趟归并的时间复杂度为O(n),则整个归并排序的时间复杂度无论是最好还是最坏情况均为O(n㏒2n)。在排序过程中,使用了辅助向量DR,大小与待排序记录空间相同,则空间复杂度为O(n)。归并排序是稳定的。 ===============================================================================各种内部排序的比较: 各种内部排序按所采用的基本思想(策略)可分为:插入排序、交换排序、选择排序、归并排序和基数排序,它们的基本策略分别是: 1 &nbsp;插入排序:依次将无序序列中的一个记录,按关键字值的大小插入到已排好序一个子序列的适当位置,直到所有的记录都插入为止。具体的方法有:直接插入、表插入、2-路插入和shell排序。 2 &nbsp;交换排序:对于待排序记录序列中的记录,两两比较记录的关键字,并对反序的两个记录进行交换,直到整个序列中没有反序的记录偶对为止。具体的方法有:冒泡排序、快速排序。 3 &nbsp;选择排序:不断地从待排序的记录序列中选取关键字最小的记录,放在已排好序的序列的最后,直到所有记录都被选取为止。具体的方法有:简单选择排序、堆排序。 4 &nbsp;归并排序:利用“归并”技术不断地对待排序记录序列中的有序子序列进行合并,直到合并为一个有序序列为止。 5 &nbsp;基数排序:按待排序记录的关键字的组成成分(“位”)从低到高(或从高到低)进行。每次是按记录关键字某一“位”的值将所有记录分配到相应的桶中,再按桶的编号依次将记录进行收集,最后得到一个有序序列。 &nbsp;各种内部排序方法的性能比较如下表。 文件的基本概念 ⑴ &nbsp;数据项(Item或field) :数据文件中最小的基本单位,反映实体某一方面的特征—属性的数据表示。 ⑵ 记录(Record) :一个实体的所有数据项的集合。 用来标识一个记录的数据项集合(一个或多个)称为关键字项(Key) ,关键字项的值称为关键字;能唯一标识一个记录的关键字称为主关键字(Primary Key),其它的关键字称为次关键字(Secondary Key) 。 利用外存对数据文件进行排序称为外部排序。 算法部分: 素数的判断算法。 Void prime( int n) /* &nbsp;n是一个正整数 &nbsp;*/ { &nbsp; int i=2 ; while ( (n% i)!=0 &amp;&amp; i*1.0&lt; sqrt(n) ) &nbsp; i++ ; if (i*1.0&gt;sqrt(n) ) printf(“&amp;d 是一个素数\n” , n) ; else printf(“&amp;d 不是一个素数\n” , n) ; } ---------------------------------------------------------------------- 冒泡排序法。 Void bubble_sort(int a[],int n) { &nbsp; change=false; for (i=n-1; change=TURE; i&gt;1 &amp;&amp; change; --i) for (j=0; j<i; if="">a[j+1]) &nbsp; &nbsp;{ &nbsp; &nbsp; a[j] ←→a[j+1] ; &nbsp; change=TURE ; } } – &nbsp; &nbsp;最好情况:0次 – &nbsp; 最坏情况:1+2+3+⋯+n-1=n(n-1)/2 – &nbsp;平均时间复杂度为: O(n2) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; --------------------------------------------------------------------------------- 算法说明 :算法中pa ,pb分别是待考察的两个链表的当前结点,pc是合并过程中合并的链表的最后一个结点。 LNode &nbsp;*Merge_LinkList(LNode *La, LNode *Lb) &nbsp; &nbsp; &nbsp;/* &nbsp;合并以La, Lb为头结点的两个有序单链表 &nbsp; */ { LNode *Lc, &nbsp;*pa , &nbsp;*pb , &nbsp;*pc, *ptr ; Lc=La ; &nbsp;pc=La &nbsp;; &nbsp; &nbsp;pa=La-&gt;next ; &nbsp;pb=Lb-&gt;next &nbsp;; while (pa!=NULL &amp;&amp; pb!=NULL) { &nbsp;if &nbsp;(pa-&gt;data<pb->data) &nbsp; &nbsp; &nbsp; { &nbsp; pc-&gt;next=pa ; &nbsp;pc=pa ; &nbsp; pa=pa-&gt;next &nbsp;; &nbsp; } /* &nbsp;将pa所指的结点合并,pa指向下一个结点 &nbsp;*/ if &nbsp;(pa-&gt;data&gt;pb-&gt;data) &nbsp; &nbsp; &nbsp; { &nbsp; pc-&gt;next=pb ; &nbsp;pc=pb ; &nbsp; pb=pb-&gt;next &nbsp;; &nbsp; } /* &nbsp;将pa所指的结点合并,pa指向下一个结点 &nbsp;*/ &nbsp; &nbsp; if &nbsp;(pa-&gt;data==pb-&gt;data) &nbsp; &nbsp; &nbsp; { &nbsp; pc-&gt;next=pa ; &nbsp;pc=pa ; &nbsp; pa=pa-&gt;next &nbsp;; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ptr=pb ; pb=pb-&gt;next ; free(ptr) ; &nbsp; } /* &nbsp;将pa所指的结点合并,pb所指结点删除 &nbsp;*/ } if &nbsp;(pa!=NULL) &nbsp;pc-&gt;next=pa ; else &nbsp; pc-&gt;next=pb ; &nbsp; &nbsp; /*将剩余的结点链上*/ free(Lb) ; return(Lc) ; } 采用静态顺序栈方式实现 &nbsp; void conversion(int &nbsp;n , int &nbsp;d) /*将十进制整数N转换为d(2或8)进制数*/ { &nbsp;SqStack &nbsp;S ; &nbsp; &nbsp;int k, *e ; S=Init_Stack(); while &nbsp;(n&gt;0) &nbsp; { &nbsp;k=n%d ; push(S , k) ; n=n/d ; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/* &nbsp;求出所有的余数,进栈 &nbsp;*/ while &nbsp;(S.top!=0) &nbsp; &nbsp; /* &nbsp;栈不空时出栈,输出 &nbsp;*/ { &nbsp; pop(S, e) ; printf(“%1d” , *e) ; } } &nbsp; &nbsp; &nbsp; 求转置矩阵的算法如下: void TransMatrix(TMatrix a , TMatrix b) { &nbsp; int p , q , col ; b.rn= ; &nbsp;=a.rn ; &nbsp;b.tn=a.tn ; /* &nbsp; &nbsp;置三元组表b.data的行、列数和非0元素个数 */ if &nbsp;(b.tn==0) &nbsp; &nbsp;printf(“ The Matrix A=0\n” ); else { &nbsp; q=0; for &nbsp;(col=1; col&lt;= ; col++) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* &nbsp; 每循环一次找到转置后的一个三元组 &nbsp;*/ for &nbsp;(p=0 ;p<a.tn if="" .col="a.data[p].row" .row="a.data[p].col" .value="a.data[p].value;" void="" btnode="" t-="">data) ; &nbsp; &nbsp; &nbsp; /* &nbsp;访问根结点 &nbsp;*/ PreorderTraverse(T-&gt;Lchild) ; PreorderTraverse(T-&gt;Rchild) ; &nbsp; &nbsp; } } 非递归算法 设T是指向二叉树根结点的指针变量,非递归算法是: 若二叉树为空,则返回;否则,令p=T; ⑴ 访问p所指向的结点; ⑵ q=p-&gt;Rchild ,若q不为空,则q进栈; ⑶ p=p-&gt;Lchild ,若p不为空,转(1),否则转(4); ⑷ &nbsp;退栈到p ,转(1),直到栈空为止。 算法实现: #define &nbsp;MAX_NODE &nbsp;50 void &nbsp;PreorderTraverse( BTNode &nbsp;*T) { &nbsp;BTNode &nbsp;*Stack[MAX_NODE] ,*p=T, *q ; int &nbsp;top=0 ; if &nbsp;(T==NULL) &nbsp;printf(“ Binary Tree is Empty!\n”) ; else { &nbsp;do &nbsp; &nbsp; &nbsp; &nbsp;{ visit( p-&gt; data ) ; &nbsp; q=p-&gt;Rchild ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if &nbsp;( q!=NULL ) &nbsp;stack[++top]=q ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;p=p-&gt;Lchild ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (p==NULL) { p=stack[top] ; &nbsp;top-- ; } &nbsp; &nbsp; &nbsp; } &nbsp; while (p!=NULL) ; } } ==============================================================================中序遍历的递归算法 void &nbsp;InorderTraverse(BTNode &nbsp;*T)</a.tn></pb-></i;></l-><!--=l---></l-><!--=l---></l-><!--=l---><!--=high)--><!--=l---><!--=l--->
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 考试专区 > 研究生考试

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服