DS专题活动的总结

 
一、基本定义:  
  线性结构是由n(n>=0)个结点组成的有穷序列;除起始结点没有直接前驱外,其他结点有且仅有一个直接前驱;除终端结点没有直接后继外,其他结点有且仅有一个直接后继;  
   
   
  二、基本操作:  
  1、顺序表的基本操作:  
  顺序表是按顺序存储方式构造的线性表,它的一个重要特征是可以实现随即存取,所以在编程的过程中充分利用这个特征,可以大大提高程序的效率,举例如下  
   
  例1:编写一个函数从一给定的顺序表A中删除元素值在x到y(x<=y)之间的所有元素,要求以较高的效率来实现  
  解:从前向后扫描顺序表A,用k记录下元素值在x到y之间的元素的个数(初始k=0)。对于当前扫描的元素,若其值不在x到y之间,则前移k个位置;否则,执行k++   。这样每个不在x与y之间的元素仅移动一次,所以算法效率高,算法如下  
   
  void   delnode(SqList   &A,Elemtype   x,Elemtype   y)  
      {//从一给定的顺序表A中删除元素值在x到y(x<=y)之间的所有元素  
          int   i,k=0;  
          for(i=0;i<A.len;i++)  
            {if(A.data[i]>=x&&A.data[i]<=y)  
                k++;                                                         //k增1  
                else  
                A.data[i-k]=A.data[i];                     //当前元素前移k个位置  
              }  
              A.len-=k;                                                       //长度减小  
        }      
  2、链表的基本操作:  
  链表的指针操作最为关键,一定要做到熟练运用,特别是指针的初始化,指针的移动等等  
  ,当然熟能生巧,编程多了自然就会运用熟练  
   
  例二、实现带头结点的单链表就地逆置  
  void   reverse(LinkList   *k)    
      {//单链表就地逆置  
          LinkList   *p,*q;  
          p=h->next;  
          h->next=NULL;  
          while(p->next!=NULL)  
              {q=p;                                 //q指向当前结点  
                p=p->next;                     //p指向下一个结点  
                q->next=h->next;         //将*q插入到*h之后  
                h->next=q;  
              }  
        }  
   
  三、重点难点问题  
  1、线性表中比较难的问题(可能我不知道其他难的问题,见谅),是集合的交并差操作,这也是线性表的一个运用广泛的操作,很有熟练掌握的必要,现举一个综合程度很高的例子(由于集合的操作涉及到元素的移动,所以这种场合多使用链表存储结构,我也以链表举例)  
   
  例三:已知3个单链表A   B   C中的结点均依元素值自小到大非递减排列(可能存在两个以上值相同的结点),编写算法对A链表进行如下操作:使操作后的链表A中仅留下3个表中均包含的数据元素结点,且没有值相同的结点,并释放所有无用结点。限定算法的时间复杂度位O(m+n+p),其中m   n   p分别为3个表的长度。  
  解:先将A中重复的结点删除,然后对A的每个结点判断是否在B和C中。若不在则删除;否则保留,最后的A即为所求。算法如下:  
  LinkList   *(LinkList   *ha,LinkList   *hb,LinkList   *hc)  
      {//链表A中仅留下3个表中均包含的数据元素结点,且没有值相同的结点,并释放所有无  
        //用结点  
          LinkList   *pa,*pb,*pc,*q,*r;  
          q=(LinkList   *)malloc(sizeof(LinkList));   //对无头结点链表的处理方法  
          q->next=ha;  
          ha=q;  
          pa=ha->next;  
          while(pa!=NULL&&pa->next!=NULL)  
              {q=pa->next;  
                if(pa->data==q->data)  
                    {pa->next-q->next;  
                      free(q);  
                    }  
                pa=pa->next;  
              }  
          pa->next=NULL;  
          pa=ha->next;  
          r=ha;  
          r->next=NULL;                                                     //r始终指向新单链表最后一个结点  
          pb=hb;  
          pc=hc;  
          while(pa!=NULL)                                                 //查找均包含的结点  
              {while(pb!=NULL&&pa->data>pb->data)     //*pa是否与hb中结点值相等  
                    pb=pb->next;  
                while(pc!=NULL&&pa->data>pc->data)     //*pa是否与hc中结点值相等  
                    pc=pc->next;  
                if(pa->data==pb->data&&pa->data==pc->data)   //*pa是公共结点  
                    {r->next=pa;  
                      r=pa;  
                      pa=pa->next;  
                      r->next=NULL;  
                      }  
                  else                                         //*pa不是公共结点,则删除  
                      {q=pa;  
                        pa=pa->next;                   //pa移动到下一个结点  
                        free(q);  
                        }  
                  }  
                  q=ha;  
                  ha=ha->next;  
                  free(q);                             //删除临时头结点  
                  return   ha;  
            }  
   
  2、链表分为带头结点的链表和不带头结点的链表(通常情况下都带头结点),当我们遇到不带头结点的链表的时候就要特别注意,因为它在删除、插入的时候要对首元结点特殊考虑;如果你怕这个“特殊考虑”的麻烦,那你可以另外设一个头结点(如例三中那样)当作链表的临时头结点,最后释放它就可以了,这个就不举例了  
   
  3、关于静态链表  
  静态链表可能是一个容易被人忽视的线性结构,其实它是综合了顺序存储和链式存储的特点,在某些特殊的场合可能发挥很好的作用,举例如下:  
  例四:大家知道,效率比较高的排序方法有快速排序、堆排序等,这些排序方法都需要在顺序存储结构上实现(从这个方面说需要一种顺序结构);但是排序的过程中需要大量移动记录,当记录很大的时候时间耗费就会很多(从这个方面说需要一种链式结构),这个矛盾可以通过使用静态链表来解决,即除记录采用顺序存储结构外,另外设一个地址向量指向相应记录;例如设r[8]为待排序记录序列,另外设一个adr[8],在开始排序前令adr[i]=i;凡在排序过程中需进行r[i]=r[j]的操作时,均以adr[i]=adr[j]代替;则在排序结束之后,地址向量中的值指示排序后的记录的次序,r[adr[1]]为关键字最小的记录,r[adr[8]]为关键字最大的记录;最后在需要的时候再根据adr的值重排记录的物理位置,这样可以降低了时间的复杂度(至少转移了时间的复杂度,在实际中还是有很多应用的);为了便于理解,下面给出记录重排的算法  
   
  void   rearrange(SqList   L,int   adr[])  
      {//adr给出顺序表L的有序次序,即L.r[adr[i]]是第i小的记录  
        //本算法按adr重排L.r,使其有序  
        for(i=1;i<L.length;i++)  
            if(adr[i]!=i)  
                {   j=i;  
                    L.r[0]=L.r[i];           //暂存记录L.r[i]  
                    while(adr[j]!=i)     //调整L.r[adr[j]]的记录到位直到dar[j]=i为止  
                      {   k=adr[j];  
                          L.r[j]=L.r[k];  
                          adr[j]=j;  
                          j=k;  
                      }  
                    adr[j]=L.r[0];  
                    adr[j]=j;                 //交换记录到位  
                  }  
          }  
   
  ************************************************************************  
  以上算是对前面几期DS专题活动的总结吧,其中有些算法思想是参考其他资料上面的,有些是自己写的,不对的地方请大家批评指正  
  可能我的知识面不够广,有什么遗漏的请大家跟贴指出,我这个帖子也只想起一个抛砖引玉的作用,呵呵
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值