详细讲解链表实例(包括一些随机数发生器原理)(C语言)

前言:

 相信大家在学习数据结构时,都会接触到线性表吧,在多数情况下,线性表可以简单的用数组实现,但事情也总不会一直那么简单,对于此,在这里我们就引进了链表,其在解决一些相对复杂问题就比线性表十分好用,本篇博客将介绍链表,并用它和数组进行比较。

1.1.初步分析:

1.1.1.题目:

图1
链表初始状态
一次操作后链表状态
再次执行指令后链表状态

1.1.2.分析:

  1. 可知,该题要根据指令来移动指定元素,在保证其它元素不动的情况下,我们只移动指定元素,最后组成一个新的数组。
  2. 这是一道可以用链表的方法去解决,结合数组知识,我们以指令A 1 4为例(在这里我们以1位首元素位置),将位置为1的元素先移动到2的右边,此时2元素的位置变为1,以此类推,移动到4的左边(相邻)。
  3. 当执行B 3 5 指令时,和上述执行A 1 4指令一样的方法进行移动,然而,这里有个不同点,就是要跨过元素位置为5的元素,我们可以将位置为3的元素代替元素位置为5的元素,其位置为5的元素其位置变为4.
  4. 当然,上述是在调用元素的位置,所以,我们还需要一个可以查找元素位置的函数,最后对上面进行整合即可完成。

1.1.3.代码:

#include<stdio.h>
const int N=1000;
int main(){
int C[N],m,n;
void shift_circular_left(int a,int b,int n,int C[]);              //该函数将位置为a的元素移动到b位置元素的左边 
void shift_circular_right(int a,int b,int n,int C[]);             //该函数将位置为a的元素移动到b位置元素的右边 
int find(int a,int n,int C[]);                                   //找到元素a的位置               
while(scanf("%d",&n)==1&&scanf("%d",&m)==1){                    //n为求得个数,m为指令个数 
 int i,c,d;  
    char k; 
 for(i=1;i<=n;i++){
  C[i]=i;
 }
 for(i=1;i<=m;i++){
  scanf("%s%d%d",&k,&c,&d);       //c,d表示元素 
  int c1=find(c,n,C);                          //找到元素的位置 
  int d1=find(d,n,C);  
  if(k=='A'){                 //下面代码大家可以在数轴上描写即可理解 
   if(c1<d1){
    shift_circular_left(c1,d1,n,C);               
   }
   else{
    shift_circular_right(d1,c1,n,C);
   }
  }
  else{
   if(c1<d1){
    shift_circular_left(c1,d1+1,n,C);
   } 
   else{
    shift_circular_right(d1+1,c1,n,C);
   }
  }
  printf("\n");  
 }
}
}
void shift_circular_left(int a,int b,int n,int C[]){        //将位置为a的元素移动到元素位置为b的左边 
int B[N],i,j=1,k,l;
for(i=1;i<a;i++){
 B[j++]=C[i];
}
l=j;
for(i=a+1;i<=b-1;i++){
 B[l++]=C[i];
}
k=l-1;
B[k+1]=C[a];
for(i=b;i<=n;i++){
 B[k+2]=C[i];
 k++;
}
for(i=1;i<=n;i++){
 printf("%d ",B[i]);
}
return ;
}
void shift_circular_right(int a,int b,int n,int C[]){    //将位置为b的元素移动到位置为a元素的右边 
int B[N],i,j=1,k,l,m;
for(i=1;i<a;i++){
 B[j++]=C[i];
}
k=j-1;
B[k+1]=C[b];
l=k+2;
for(i=a;i<b;i++){
 B[l++]=C[i];
}
m=l;
for(i=b+1;i<=n;i++){
 B[m++]=C[i];
}
for(i=1;i<=n;i++){
 printf("%d ",B[i]);
}
return ;
}
int find(int a,int n,int C[]){         //定义查找元素位置函数 
int i=1,k;
while(i<=n){
 if(C[i]!=a){
  i++;
 }
 else{
  k=i;
  break;
 }
}
return k;
}

 大家你知道吗当我在发布这段代码时,我就在想,当你们在看这段代码时会不会在心里狠狠的骂我😄😄😄。好了,但我还是要忍受你们的骂意,继续完成下面的内容😃.
 如果大家复制上面代码时去执行的话,你会发现其耗费的时间是有点长的,但是上面代码思路简单,易容易理解哈。还有你们要注意,上面scanf("%s%d%d",&k,&c,&d);为什么是用%s而不是%c?其实,当用scanf("%d")读取整数时,并没有读取后面的回车换行符,这是用%c会读到这个回车换行符,而只有%s才会跳过它,读取下一个非空白符组成的字符串。
 经过小编的思考及相关资料的启发,我用了另一方法去完成上述问题,如下:

1.1.3.深入分析:

  1. 我们可以将要移动的元素移出,然后再插入,即可。
  2. 比如一指令A 1 4为例,我们可以将位置为1的元素先移出原数组,然后在插入到位置为4的元素的左边,当然对于指令为B 3 5是同样的方法。

1.1.4.代码:

#include<stdio.h>
const int M=1000;
int main(){
 int C[M],n,m;
 void link_left_right1(int a,int b,int n,int C[]);    //定义一个移动数组元素的函数 
 void link_left_right2(int a,int b,int n,int C[]);
 int find(int a,int n,int C[]);        //一个查找数组的位置的函数 
 while(scanf("%d",&n)==1&&scanf("%d",&m)==1){            //自行分析下面代码的作用 
  for(int i=1;i<=n;i++){
   C[i]=i;
  }
  int x,y;
  char type;
  for(int j=1;j<=m;j++){
   scanf("%s%d%d",&type,&x,&y);
   int x1=find(x,n,C);
   int y1=find(y,n,C);
   if(type=='A'){
    if(x1<y1){
     link_left_right1(x1,y1,n,C);       //将y1位置的元素移到x1位置元素的左边 
    }
    else{
     link_left_right2(y1,x1,n,C);      //将x1位置的元素移动到y1位置元素的左边 
    }
   }
   else{
    if(x1<y1){
     link_left_right1(x1,y1+1,n,C);
    }
    else{
     link_left_right2(y1+1,x1,n,C);
    }
   }
   printf("\n");
  }
 }
}
void link_left_right1(int a,int b,int n,int C[]){
 int i,B[M],j=1,k,A[M];
 for(i=1;i<a;i++){
  B[j++]=C[i];
 }
 for(i=a+1;i<=n;i++){
  B[j++]=C[i];
 }
 for(j=1;j<b-1;j++){
  A[j]=B[j];
 }
 A[b-1]=C[a];
 for(j=b-1;j<=n-1;j++){
  A[j+1]=B[j];
 }
 for(i=1;i<=n;i++){
  printf("%d ",A[i]);
 }
 return ;
}
void link_left_right2(int a,int b,int n,int C[]){
 int i,B[M],j=1,k,A[M];
 for(i=1;i<b;i++){
  B[j++]=C[i];
 }
 for(i=b+1;i<=n;i++){
  B[j++]=C[i];
 }
 for(j=1;j<a;j++){
  A[j]=B[j];
 }
 A[a]=C[b];
 for(i=a;i<=n-1;i++){
  A[i+1]=B[i];
 }
 for(i=1;i<=n;i++){
  printf("%d",A[i]);
 }
 return ;
}
int find(int a,int n,int C[]){         //定义查找数组位置的函数 
 int i=1,k;
 while(i<=n){
  if(C[i]!=a){
   i++;
  }
  else{
   k=i;
   break;
  }
 }
 return k;
}

1.1.5.上述代码的对比:

从算法的复杂度来看我们很容易发现,第二段代码相对较好,其算法复杂度及时间复杂度都比第一段代码要优,换句话说就是效率更高。

2.1.对比测试:

 不知道读者有没有发现题目明确说过m和n的值最大会达到什么地步,而我给出的两段代码都不会达到那种地步,然而,在这里,我们可以建立一个数据生成器,来对大量数据进行测试。

2.1.1.代码:

//测试实例 
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int n=100,m=100000;
double random(){
 return (double)rand()/RAND_MAX;           //生成[0,1]的随机数,下面会详细解析RAND_MAX  
}
double random(int m){
 return (int)(random()*(m-1)+0.5);   //随机生成[0,m-1]的随机数 
}
int main(){
 srand(time(NULL));               //保证每次循环后生成的随机数不是相同的 
 printf("%d %d",n,m);
 for(int i=0;i<m;i++){
  if(rand()%2==0){              //间断的输出A和B 
   printf("A ");
  }
  else{
   printf("B ");
  }
  int X,Y;
  for(;;){
   X=random(n)+1;          //随机生成[0,n]的随机数 
   Y=random(n)+1;
   if(X!=Y){               //只有X,Y不相等才是合法的 
    break;
   }
     }
     printf("%d %d\n",X,Y);
 }
 return 0;
}

 对于RAND_MAX,其在C中的值至少为32767,而当rand()/RAND_MAX,表示,生成的随机数位于[0,1]内。

当然啦,头文件include<stdlib.h>是调用rand()需要的头文件,其include<time.h>是调用srand()需要的头文件,其中注意的是,当你在用srand()时,要声明srand(time(NULL));,否则,会出错。当你不调用srand(time(NULL));时,则每次生成的随机会和上一次生成的随机数是相同的,只有调用srand(time(NULL));时,每次生成的随机数会随时间的变化,而生成不同的随机数。

最后,希望大家多多支持哦!一起加油,一起进步!!!😄

3.1.参考文献:

《算法竞赛入门》 作者:刘汝佳
晚安

  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值