算法学习-近期两周的任务

目录:

一 跳表

二 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   
View Code

 

 

 

 

二 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;
  }
View Code

 

posted @ 2016-03-01 16:52  banana16314  阅读(144)  评论(0)    收藏  举报