算法学习-近期两周的任务
目录:
一 跳表
二 KMP
三优先队列 堆排序 2016/3/13 完成
四 三元组存储矩阵和转置等稀疏矩阵的运算
五单向链表操作 :
1.单链表反转
2.找出单链表的倒数第4个元素
3.找出单链表的中间元素
4.删除无头单链表的一个节点
5.两个不交叉的有序链表的合并
6.有个二级单链表,其中每个元素都含有一个指向一个单链表的指针。写程序把这个二级链表称一级单链表。
7.单链表交换任意两个元素(不包括表头)
8.判断单链表是否有环?如何找到环的“起始”点?如何知道环的长度?
9.判断两个单链表是否相交
10.两个单链表相交,计算相交点
11.用链表模拟大整数加法运算
12.单链表排序
13.删除单链表中重复的元素
结合算法导论,和博客的相关习题,写出简单的报告文档,相应的笔记,练习笔记
六,解决基本的二叉树的操作,B树,AVL,红黑树,
详细过程:
一:SKIPLIST
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <time.h> 5 #include <string.h> 6 #include <malloc.h> 7 8 #define MAXLEVEL 10 9 #define null 0 10 11 using namespace std; 12 13 //定义结构体 14 struct node 15 { 16 int key; 17 struct node * forward[MAXLEVEL]; 18 }; 19 //函数声明 20 struct node* initialization(int& level,int &total);//函数初始化 21 struct node* search(struct node*head,int key,int level);//检索指定节点的函数 22 void insert(struct node* head,int key,int &level);//插入指定节点函数 23 int deletenode(struct node* head,int &level);//删除指定节点函数 24 void print(struct node* head);//输出函数 25 int randx(int &level);//随机级数产生函数 26 27 //主函数 28 int main() 29 { 30 freopen("skiplist.txt","r",stdin); 31 printf("欢迎使用跳表程序\n"); 32 printf("创建跳表\n"); 33 int level,key,total; 34 struct node*head=null; 35 srand((unsigned int)time(null)); 36 head = initialization(level,total); 37 printf("输入跳表节点个数\n"); 38 scanf("%d",&total); 39 for (int i=0;i<total;i++) 40 { 41 printf("输入跳表第%d个节点\n",i+1); 42 scanf("%d",&key); 43 insert(head,key,level); 44 printf("插入节点:%d\n",key); 45 } 46 printf("当前跳表是:\n"); 47 print(head); 48 int reply; 49 for(;;) 50 {//循环输入 51 printf("程序功能\n"); 52 printf("1:节点插入\n"); 53 printf("2:节点检索\n"); 54 printf("3:节点删除\n"); 55 printf("4:节点遍历\n"); 56 printf("5:退出\n"); 57 scanf("%d",&reply); 58 switch(reply) 59 { 60 case 1: 61 { 62 printf("输入要插入的节点关键值\n"); 63 scanf("%d",&key); 64 insert(head,key,level); 65 printf("插入成功,当前遍历结果是\n"); 66 print(head); 67 total++; 68 printf("现有节点数目:%d\n",total); 69 break; 70 } 71 case 2: 72 { 73 printf("输入检索的节点值\n"); 74 scanf("%d",&key); 75 struct node* p=search(head,key,level); 76 if (p) 77 { 78 printf("\n找到要检索的值%d\n",p->key); 79 } 80 else printf("\n没有找到\n"); 81 break; 82 } 83 case 3: 84 { 85 int del; 86 del = deletenode(head,level); 87 if (del==1) 88 {//返回为1,表示删除成功 89 printf("删除成功,当前跳表为:\n"); 90 print(head); 91 printf("现有节点为:"); 92 total--; 93 printf("%d\n",total); 94 break; 95 } 96 else printf("没有要删除的节点\n"); 97 break; 98 } 99 case 4: 100 { 101 printf("当前跳表为:\n"); 102 print(head); 103 break; 104 } 105 case 5: 106 return 0; 107 break; 108 default: 109 printf("输入错误,请重新输入\n"); 110 } 111 } 112 fclose(stdin); 113 return 0; 114 } 115 116 //子函数的定义 117 118 struct node* initialization(int& level,int &total)//函数初始化 119 { 120 struct node *head; 121 head = new (struct node); 122 for (int i=0;i<MAXLEVEL;i++) 123 head->forward[i]=0;//头结点的初始设置 124 head->key=0; 125 level=0;//当前节点级数为0 126 total =0;//当前节点个数为0 127 return head; 128 } 129 struct node* search(struct node*head,int key,int level)//检索指定节点的函数 130 { 131 struct node*p; 132 p=head; 133 printf("检索路径上的节点序列是\n"); 134 for (int i=level;i>=0;i--) 135 {//从最高一级开始 136 while ((p->forward[i]!=0) && (p->forward[i]->key < key)) 137 { 138 printf("%d ",p->forward[i]->key); 139 p=p->forward[i]; 140 } 141 } 142 p=p->forward[0];//回到零级链,当前p为空,或者指向比搜索关键字小的前一个节点 143 if (p==0) return 0; 144 else if (p->key==key) return p; 145 else return 0; 146 } 147 void insert(struct node* head,int key,int &level)//插入指定节点函数 148 { 149 struct node *p,*updata[MAXLEVEL]; 150 p=head; 151 int i; 152 int newlevel=randx(level);//获得新的级数 153 for (i=level;i>=0;i--) 154 { 155 while ((p->forward[i] != 0) && (p->forward[i]->key < key)) 156 p=p->forward[i]; 157 updata[i]=p;//uodata[i]记录了搜索中各级走过的比插入节点数值小的最大节点位置 158 } 159 160 p= new (struct node); 161 p->key=key;//插入数值 162 for (i=0;i<MAXLEVEL;i++) 163 p->forward[i]=0;//初始化 164 for (i=0; i <= newlevel; i++) 165 {//从零级各个级数进行更新,两个指针的转换 166 p->forward[i]=updata[i]->forward[i]; 167 updata[i]->forward[i]=p; 168 } 169 } 170 int deletenode(struct node* head,int &level)//删除指定节点函数 171 { 172 int delkey; 173 printf("输入要删除的节点数值\n"); 174 scanf("%d",&delkey); 175 struct node * r=search(head,delkey,level); 176 if (r)//非空 177 { 178 struct node *pre,*cur; 179 for (int i=level;i>=0;i--) 180 { 181 pre=cur=head; 182 while (cur!=r && cur!=null) 183 { 184 pre=cur;//记录当前节点 185 cur=cur->forward[i];//节点后移 186 } 187 if (cur) 188 { 189 if (pre==head && i==level && cur->forward[i]==0) level--;//只有一个节点 190 else pre->forward[i]=cur->forward[i]; 191 } 192 } 193 delete r; 194 return 1; 195 } 196 else return 0; 197 } 198 void print(struct node* head)//输出函数 199 { 200 struct node*p=head->forward[0]; 201 if(p) 202 { 203 while (p) 204 { 205 printf("%d ",p->key); 206 p=p->forward[0]; 207 } 208 } 209 else printf("空表\n"); 210 } 211 int randx(int &level)//随机级数产生函数 212 { 213 int i,j,t; 214 t=rand(); 215 for (i=0,j=2;i<10;i++,j += j) 216 { 217 if (t > RAND_MAX/j) break; 218 } 219 if (i > level) level = i; 220 return i; 221 } 222
二 KMP:
假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表 示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就 说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置 (减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 1 2 3 4 5 6 7
此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j'。j'可能是多少呢?仔细想一下,我 们发现,j'必须要使得B[1..j]中的头j'个字母和末j'个字母完全相等(这样j变成了j'后才能继续保持i和j的性质)。这个j'当然要越大越 好。在这里,B [1..5]="ababa",头3个字母和末3个字母都是"aba"。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4:
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 1 2 3 4 5 6 7
从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数
组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况:
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 1 2 3 4 5 6 7
由于P[5]=3,因此新的j=3:
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 1 2 3 4 5 6 7
这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]:
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 1 2 3 4 5 6 7
现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0:
i = 1 2 3 4 5 6 7 8 9 ……
A = a b a b a b a a b a b …
B = a b a b a c b
j = 0 1 2 3 4 5 6 7
终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。
这个过程的代码很短(真的很短),我们在这里给出:
j:=0;
for i:=1 to n do
begin
while (j>0) and (B[j+1]<>A[i]) do j:=P[j];
if B[j+1]=A[i] then j:=j+1;
if j=m then
begin
writeln('Pattern occurs with shift ',i-m);
j:=P[j];
end;
end;
最后的j:=P[j]是为了让程序继续做下去,因为我们有可能找到多处匹配。
这个程序或许比想像中的要简单,因为对于i值的不断增加,代码用的是for循环
。因此,这个代码可以这样形象地理解:扫描字符串A,并更新可以匹配到B的什么位置。
现在,我们还遗留了两个重要的问题:一,为什么这个程序是线性的;二,如何快速预处理P数组。
为什么这个程序是O(n)
的?其实,主要的争议在于,while循环使得执行次数出现了不确定因素。我们将用到时间复杂度的摊还分析中的主要策略,简单地说就是通过观察某一个变量
或函数值的变化来对零散的、杂乱的、不规则的执行次数进行累计。KMP的时间复杂度分析可谓摊还分析的典型。我们从上述程序的j
值入手。每一次执行while循环都会使j减小(但不能减成负的),而另外的改变j值的地方只有第五行。每次执行了这一行,j都只能加1;因此,整个过程
中j最多加了n个1。于是,j最多只有n次减小的机会(j值减小的次数当然不能超过n,因为j永远是非负整数)。这告诉我们,while循环总共最多执行
了n次。按照摊还分析的说法,平摊到每次for循环中后,一次for循环的复杂度为O(1)。整个过程显然是O(n)的。这样的分析对于后面P数组预处理
的过程同样有效,同样可以得到预处理过程的复杂度为O(m)。
预处理不需要按照P的定义写成O(m^2)甚至O(m^3)的。我们可以通
过P[1],P[2],…,P[j-1]的值来获得P[j]的值。对于刚才的B="ababacb",假如我们已经求出了P[1],P[2],P[3]和
P[4],看看我们应该怎么求出P[5]和P[6]。P[4]=2,那么P
[5]显然等于P[4]+1,因为由P[4]可以知道,B[1,2]已经和B[3,4]相等了,现在又有B[3]=B[5],所以P[5]可以由P[4]
后面加一个字符得到。P[6]也等于P[5]+1吗?显然不是,因为B[ P[5]+1
]<>B[6]。那么,我们要考虑“退一步”了。我们考虑P[6]是否有可能由P[5]的情况所包含的子串得到,即是否P[6]=P[
P[5] ]+1。这里想不通的话可以仔细看一下:
1 2 3 4 5 6 7
B = a b a b a c b
P = 0 0 1 2 3 ?
P[5]=3是因为B[1..3]和B[3..5]都是"aba";而P[3]=1则告诉我们,B[1]、B[3]和B[5]都是"a"。
既然P[6]不能由P[5]得到,或许可以由P[3]得到(如果B[2]恰好和B[6]相等的话,P[6]就等于P[3]+1了)。显然,P[6]也不能
通过P[3]得到,因为B[2]<>B[6]。事实上,这样一直推到P[1]也不行,最后,我们得到,P[6]=0。
怎么这个预处理过程跟前面的KMP主程序这么像呢?其实,KMP的预处理本身就是一个B串“自我匹配”的过程。它的代码和上面的代码神似:
P[1]:=0;
j:=0;
for i:=2 to m do
begin
while (j>0) and (B[j+1]<>B[i]) do j:=P[j];
if B[j+1]=B[i] then j:=j+1;
P[i]:=j;
end;
最后补充一点:由于KMP算法只预处理B串,因此这种算法很适合这样的问题:给定一个B串和一群不同的A串,问B是哪些A串的子串
1 假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。 2 3 i = 1 2 3 4 5 6 7 8 9 …… 4 A = a b a b a b a a b a b … 5 B = a b a b a c b 6 j = 1 2 3 4 5 6 7 7 8 此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j'。j'可能是多少呢?仔细想一下,我们发现,j'必须要使得B[1..j]中的头j'个字母和末j'个字母完全相等(这样j变成了j'后才能继续保持i和j的性质)。这个j'当然要越大越好。在这里,B [1..5]="ababa",头3个字母和末3个字母都是"aba"。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4: 9 10 i = 1 2 3 4 5 6 7 8 9 …… 11 A = a b a b a b a a b a b … 12 B = a b a b a c b 13 j = 1 2 3 4 5 6 7 14 15 从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。 16 再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况: 17 18 i = 1 2 3 4 5 6 7 8 9 …… 19 A = a b a b a b a a b a b … 20 B = a b a b a c b 21 j = 1 2 3 4 5 6 7 22 23 由于P[5]=3,因此新的j=3: 24 25 i = 1 2 3 4 5 6 7 8 9 …… 26 A = a b a b a b a a b a b … 27 B = a b a b a c b 28 j = 1 2 3 4 5 6 7 29 30 这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]: 31 32 i = 1 2 3 4 5 6 7 8 9 …… 33 A = a b a b a b a a b a b … 34 B = a b a b a c b 35 j = 1 2 3 4 5 6 7 36 37 现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0: 38 39 i = 1 2 3 4 5 6 7 8 9 …… 40 A = a b a b a b a a b a b … 41 B = a b a b a c b 42 j = 0 1 2 3 4 5 6 7 43 44 终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。 45 这个过程的代码很短(真的很短),我们在这里给出: 46 47 j:=0; 48 for i:=1 to n do 49 begin 50 while (j>0) and (B[j+1]<>A[i]) do j:=P[j]; 51 if B[j+1]=A[i] then j:=j+1; 52 if j=m then 53 begin 54 writeln('Pattern occurs with shift ',i-m); 55 j:=P[j]; 56 end; 57 end; 58 59 最后的j:=P[j]是为了让程序继续做下去,因为我们有可能找到多处匹配。 60 这个程序或许比想像中的要简单,因为对于i值的不断增加,代码用的是for循环 61 。因此,这个代码可以这样形象地理解:扫描字符串A,并更新可以匹配到B的什么位置。 62 63 现在,我们还遗留了两个重要的问题:一,为什么这个程序是线性的;二,如何快速预处理P数组。 64 为什么这个程序是O(n)的?其实,主要的争议在于,while循环使得执行次数出现了不确定因素。我们将用到时间复杂度的摊还分析中的主要策略,简单地说就是通过观察某一个变量或函数值的变化来对零散的、杂乱的、不规则的执行次数进行累计。KMP的时间复杂度分析可谓摊还分析的典型。我们从上述程序的j 值入手。每一次执行while循环都会使j减小(但不能减成负的),而另外的改变j值的地方只有第五行。每次执行了这一行,j都只能加1;因此,整个过程中j最多加了n个1。于是,j最多只有n次减小的机会(j值减小的次数当然不能超过n,因为j永远是非负整数)。这告诉我们,while循环总共最多执行了n次。按照摊还分析的说法,平摊到每次for循环中后,一次for循环的复杂度为O(1)。整个过程显然是O(n)的。这样的分析对于后面P数组预处理的过程同样有效,同样可以得到预处理过程的复杂度为O(m)。 65 预处理不需要按照P的定义写成O(m^2)甚至O(m^3)的。我们可以通过P[1],P[2],…,P[j-1]的值来获得P[j]的值。对于刚才的B="ababacb",假如我们已经求出了P[1],P[2],P[3]和P[4],看看我们应该怎么求出P[5]和P[6]。P[4]=2,那么P [5]显然等于P[4]+1,因为由P[4]可以知道,B[1,2]已经和B[3,4]相等了,现在又有B[3]=B[5],所以P[5]可以由P[4] 后面加一个字符得到。P[6]也等于P[5]+1吗?显然不是,因为B[ P[5]+1 ]<>B[6]。那么,我们要考虑“退一步”了。我们考虑P[6]是否有可能由P[5]的情况所包含的子串得到,即是否P[6]=P[ P[5] ]+1。这里想不通的话可以仔细看一下: 66 67 1 2 3 4 5 6 7 68 B = a b a b a c b 69 P = 0 0 1 2 3 ? 70 71 P[5]=3是因为B[1..3]和B[3..5]都是"aba";而P[3]=1则告诉我们,B[1]、B[3]和B[5]都是"a"。既然P[6]不能由P[5]得到,或许可以由P[3]得到(如果B[2]恰好和B[6]相等的话,P[6]就等于P[3]+1了)。显然,P[6]也不能通过P[3]得到,因为B[2]<>B[6]。事实上,这样一直推到P[1]也不行,最后,我们得到,P[6]=0。 72 怎么这个预处理过程跟前面的KMP主程序这么像呢?其实,KMP的预处理本身就是一个B串“自我匹配”的过程。它的代码和上面的代码神似: 73 74 P[1]:=0; 75 j:=0; 76 for i:=2 to m do 77 begin 78 while (j>0) and (B[j+1]<>B[i]) do j:=P[j]; 79 if B[j+1]=B[i] then j:=j+1; 80 P[i]:=j; 81 end; 82 83 最后补充一点:由于KMP算法只预处理B串,因此这种算法很适合这样的问题:给定一个B串和一群不同的A串,问B是哪些A串的子
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <string.h> 5 6 using namespace std; 7 const int N=500; 8 int next[N];//next[j]的数值使是B的子串B[j]的前缀和后缀相等的最大值 9 char A[N],B[N];//看A串是否包含B串 10 11 void Next(char B[N]);//对子串预处理,Next[j]应该是所有满足B[1..Next[j]]=B[j-Next[j]+1..j]的最大值 12 void KMP(char A[N],char B[N]);//匹配字符串,找出匹配位置 13 14 void Next(char B[N]) 15 {//实际是B串的自我匹配过程 16 next[1]=0; 17 int j=0; 18 int m=strlen(B); 19 for (int i=1;i<m;i++) 20 { 21 while (j>0 && B[j] != B[i]) j=next[j];//回溯一次, 22 if (B[j] == B[i]) j++; 23 next[j]=j; 24 } 25 } 26 void KMP(char A[N],char B[N]) 27 { 28 int m=strlen(B); 29 int n=strlen(A); 30 int j=0; 31 for (int i=0;i<n;i++) 32 { 33 while (j>0 && A[i] != B[j]) j=next[j];//对于B串而言,next[j]之前的匹配成功 34 if (B[j]==A[i]) j++;//又有一个字符匹配成功 35 36 if (j==m) 37 { 38 printf("匹配完成,移动%d\n",i-m); 39 j=next[j];//更新可以匹配子串B的位置 40 } 41 } 42 } 43 int main() 44 { 45 printf("输入目标串:\n"); 46 scanf("%s",A); 47 printf("输入子串:\n"); 48 scanf("%s",B); 49 KMP(A,B); 50 51 return 0; 52 }
三:根据算法导论堆排序,最大堆完成最大优先队列
1 //he chengquan 2 //priority_queue No1.0 数组型最大堆实现最大优先队列 3 4 #include <iostream> 5 #include <cstdio> 6 #include <cstdlib> 7 #include <algorithm> 8 using namespace std; 9 10 const int N = 100;//最多数据元素 11 int A[N+1];//数组表示堆,A[0]表示实际数组元素个数 12 13 //返回下标为i的节点的父节点的下标,入口参数i 14 int Parent(int i) 15 { 16 return i>>1;//右移一位 17 } 18 19 //返回下标为i的节点的左儿子的下标,,入口参数i 20 int Left(int i) 21 { 22 i=i<<1; 23 return i;//左移一位 24 } 25 26 //返回下标为i的节点的右儿子的下标,入口参数i 27 int Right(int i) 28 { 29 i=i<<1;//左移一位 30 return (i+1); 31 } 32 33 //维护堆的性质,入口参数数组A,和下标cur 34 void MaxHeapify(int A[],int cur) 35 { 36 int left;//节点cur的左儿子 37 int right;//节点cur的右儿子 38 int largest;//关键字最大的节点下标 39 while (true) 40 { //寻找最大元素下标 41 int left=Left(cur);//节点cur的左儿子 42 int right=Right(cur);//节点cur的右儿子 43 if (left <= A[0] && A[left] > A[cur]) 44 { 45 largest=left; 46 } 47 else 48 { 49 largest = cur; 50 } 51 if (right <= A[0] && A[right] > A[largest]) 52 { 53 largest=right; 54 } 55 if (largest != cur) 56 { 57 swap(A[cur],A[largest]); 58 cur=largest;//为防止下标是largest的值是A[i],以该节点为根的子树可能违反最大堆性质。。。。。 59 } 60 else 61 { 62 break; 63 } 64 } 65 } 66 //**********优先队列的生成*************** 67 void PriorityQueue(int A[]) 68 { 69 int QueueSize=A[0]; 70 int j=QueueSize>>1; 71 72 for (; j>=1; j--) 73 { 74 MaxHeapify(A,j); 75 } 76 } 77 78 //**********优先队列的操作**************** 79 //如果队列为空,返回TRUE,否则返回false 80 bool EmptyQueue(int A[]) 81 { 82 if (A[0]==0) return true; 83 else return false; 84 } 85 //********* 86 //返回队列里元素个数 87 int SizeQueue(int A[]) 88 { 89 return A[0]; 90 } 91 //********* 92 //返回堆里关键字最大的元素 93 int TopQueue(int A[]) 94 { 95 return A[1]; 96 } 97 //******** 98 //从堆里去掉并返回关键字最大的元素 99 int PopQueue(int A[]) 100 { 101 if (EmptyQueue(A)) 102 { 103 printf("ERROR:heap underflow\n"); 104 exit(1); 105 } 106 int max = A[1]; 107 A[1]=A[A[0]];//类似堆排序的,算法导论P85-93 108 A[0]--; 109 MaxHeapify(A,1); 110 return max; 111 } 112 //********** 113 //把元素下标为i的关键字值增加到key,key大于原先i的关键字 114 void IncreaseQueueKey(int A[],int i,int key) 115 { 116 if (key < A[i]) 117 { 118 printf("Error:new key is smaller than current kry\n"); 119 return; 120 } 121 A[i] = key; 122 while (i >1 && A[Parent(i)] < A[i]) 123 { 124 swap(A[i],A[Parent(i)]); 125 i=Parent(i); 126 } 127 } 128 //********** 129 //把元素key加入队列里 130 void PushQueue(int A[],int key) 131 { 132 A[0]++; 133 A[A[0]]= -2147483647; 134 IncreaseQueueKey(A,A[0],key); 135 } 136 137 int main() 138 { 139 freopen("priority_queue.txt","r",stdin); 140 141 printf("********输入元素个数*******\n"); 142 scanf("%d",A);//数组首位是元素个数,在txt文件里默认14个,具体数据见算法导论P87练习6.2-1 143 for (int i=1;i<=A[0];i++) 144 { 145 scanf("%d",&A[i]); 146 } 147 148 printf("********根据数组生成优先队列********\n"); 149 PriorityQueue(A); 150 printf("\n********生成完毕********\n"); 151 printf("********优先队列的操作********\n"); 152 printf("\n********判断是否为空********\n"); 153 bool IsEmpty=EmptyQueue(A); 154 if (IsEmpty==true) printf("优先队列为空\n"); 155 else 156 { 157 printf("\n优先队列不空\n"); 158 printf("*******优先队列的大小********\n"); 159 int size=SizeQueue(A); 160 printf("\n优先队列的大小是:%4d\n",size); 161 printf("\n********优先队列优先级最高的元素的引用********\n"); 162 printf("\n优先级最高的元素是:%4d\n",TopQueue(A)); 163 printf("\n********优先队列添加键值为key的元素********\n"); 164 int key; 165 printf("\n请输入键值\n"); 166 scanf("%d",&key); 167 PushQueue(A,key); 168 printf("\n添加成功\n"); 169 printf("\n********优先队列删除优先级最高元素,并返回其键值********\n"); 170 int temp=PopQueue(A); 171 printf("\n删除的优先级最高元素键值是:%4d\n",temp); 172 printf("\n删除成功\n"); 173 printf("\n********优先队列元素键值改变(增加)********\n"); 174 printf("\n输入增加的元素下标和增加之后的键值\n"); 175 int i, key1; 176 scanf("%d %d",&i,&key1); 177 IncreaseQueueKey(A,i,key1); 178 printf("\n完成增值操作\n"); 179 } 180 181 fclose(stdin); 182 183 return 0; 184 }
四:稀疏矩阵及转置
#include <iostream> #include <cstdio> #include <cstdlib> #include <string.h> using namespace std; typedef struct { int i,j;//非零元的行和列下标 int key;//非零元的关键字 } Triple;//三元组结构体 typedef struct { Triple data[1000];//默认非零元最多999个 int mu,nu,tu;//矩阵的行数,列数,非零元个数 } Matrix; void Transpose(Matrix M,Matrix&T) { T.mu=M.nu;T.nu=M.mu;T.tu=M.tu;//转置矩阵T行数等于M的列数,列数等于M的行数,非零元个数不变 if (T.tu) {//如果非零元个数不是零个 int q=1; for (int col=1;col<=M.nu;col++) { for (int p=1;p<=M.tu;p++) { if (M.data[p].j==col) { T.data[q].i=M.data[p].j;T.data[q].j=M.data[p].i;T.data[q].key=M.data[p].key; q++; } } } } } void FastTranspose(Matrix M,Matrix &T) { T.mu=M.nu;T.nu=M.mu;T.tu=M.tu;//转置矩阵T行数等于M的列数,列数等于M的行数,非零元个数不变 if (T.tu) {//如果非零元个数不是零个 int num[100],cpot[100]; memset(num,0,sizeof(int)*(M.nu+1));//num数组清零 for (int i=1;i<=M.tu;i++) num[M.data[i].j]++;//num[col]最终是第col列非零元的元素个数 cpot[1]=1;//cpot[col]是第col列的第一个非零元在转置矩阵三元组即b.data[x]的下标x for (int col=2;col<=M.nu;col++) cpot[col]=cpot[col-1]+num[col-1];//递推公式 for (int p=1;p<=M.tu;p++) { int col = M.data[p].j;//在矩阵M三元组里第p个元素的列 int q=cpot[col];//在转置矩阵T三元组里对应的位置是q T.data[q].i=M.data[p].j;T.data[q].j=M.data[p].i;T.data[q].key=M.data[p].key; cpot[col]++;//在T的三元组里的位置前进一位 } } } int main() { freopen("triple_matrix.txt","r",stdin); Matrix M,T; printf("输入三元组矩阵的行数,列数,非零元个数:\n"); scanf("%d %d %d",&M.mu,&M.nu,&M.tu); printf("输入矩阵三元组:\n"); for (int k=1;k<=M.tu;k++) scanf("%d %d %d",&M.data[k].i,&M.data[k].j,&M.data[k].key); fclose(stdin); printf("普通转置法:\n"); Transpose(M,T); printf("转置矩阵的行数,列数,非零元个数:\n"); printf("%4d%4d%4d\n",T.mu,T.nu,T.tu); for (int k=1;k<=T.tu;k++) printf("%4d%4d%4d\n",T.data[k].i,T.data[k].j,T.data[k].key); printf("快速转置法:\n"); FastTranspose(M,T); printf("转置矩阵的行数,列数,非零元个数:\n"); printf("%4d%4d%4d\n",T.mu,T.nu,T.tu); for (int k=1;k<=T.tu;k++) printf("%4d%4d%4d\n",T.data[k].i,T.data[k].j,T.data[k].key); return 0; }

浙公网安备 33010602011771号