Visual C++环境下调试数据结构——线性表(链式存储结构)

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Visual C++环境下调试数据结构——线性表之链式存储结构。

我为什么要写这篇贴子呢?类似的贴子已经很多了,为什么还要再写这个话题。

不知道大家有没有发现一个问题,就是很多介绍链式存储结构的贴子里的代码一写入到VC里根本不能运行的,不是这个符号错,就是那个指针错。

所以我一定要避免这个问题,写完本文后我检查了很多遍,力保所有的C语言代码的每一个字母、每一个指针、每一个符号都不能出错。以保证大家得到真正的可运行代码。

好了,让我们开始吧。

线性表的链式存储是用通过指针链接起来的结点来存储数据元素,基本的结点结构如下所示:

其中,数据域用于存储数据元素的值,指针域则存储当前元素的直接前驱或直接后继的位置信息,指针域中的信息称为指针。

存储各数据元素的结点的地址并不要求是连续的,因此存储数据元素的同时必须存储元素之间的逻辑关系。另外,结点空间只有在需要的时候才申请,无须事先分配。结点之间通过指针域构成一个链表,若结点中只有一个指针域,则称为线性链表或单链表,如图所示:

设线性表中的元素是整型,则单链表结点类型的定义为:

typedef struct node{
  int data;                /*结点的数据域,此处以整型为例*/
  struct node *next;  /*结点的指针域*/
}NODE,*LinkList;

在链式存储结构中,只需要一个指针(称为头指针,如上图中的head)指向第一个结点,就可以顺序地访问到表中的任意一个元素。

在链式存储结构下进行插入和删除,其实质都是对相关指针的修改。在单链表中,若在p所指结点后插入新元素结点(s所指结点,已经生成),如下图所示,其基本步骤如下。

(1)  s->next=p->next;
(2)  p->next=s;

即先将p所指结点的后继结点指针赋给s所指结点的指针域,然后将p所指结点的指针域修改为指向s所指结点。

同理,在单链表中删除p所指结点的后继结点时(如上图所示),步骤如下。

(1)  q=p->next;
(2)  p->next=p->next->next;
(3)  free(q);

即先令临时指针q指向待删除的结点,然后修改p所指结点的指针域为指向p所指结点的后继的后继结点,从而将元素b所在的结点从链表中删除,最后释放q所指结点的空间。

在实际应用中,为了简化对链表状态的判定和处理,特别引入一个不存储数据元素的结点,称为头结点,将其作为链表的第一个结点并令头指针指向该结点。

下面给出单链表上查找、插入和删除运算的实现过程。

【函数】单链表的查找运算。


LinkList Find_List(LinkList L, int k)  /*L为带头结点单链表的头指针*/

/*在表中查找第k个元素,若找到,返回该元素结点的指针;否则,返回空指针NULL*/

{      LinkList p;    int i;
  i=1;    p=L->next;  /*初始时,令p指向第一个元素结点,i为计数器*/
  while(p && i<k){  /*顺指针链向后查找直到p指向第k个元素结点或p为空指针*/

              p=p->next;   i++;
  }
  if(p && i==k)  return p;        /*存在第k个元素且指针p指向该元素结点*/

       return NULL;  /*第k个元素不存在,返回空指针*/
  }/*Find_List*/

【函数】单链表的插入运算。


int Insert_List(LinkList L, int k, int newElem)  /*L为带头结点单链表的头指针*/

/*将元素newElem插入表中的第k个元素之前,若成功则返回0,否则返回-1*/

/*该插入操作等同于将元素newElem插入在第k-1个元素之后*/

{       LinkList p,s;  /*p、s为临时指针*/
  if(k==1) p=L;  /*元素newElem要插入到第1个元素之前*/
  else p=Find_List(L, k-1);  /*查找表中的第k-1个元素并令p指向该元素结点*/
  if(!p) return -1;  /*表中不存在第k-1个元素,不满足运算要求*/
  s=(NODE*)malloc(sizeof(NODE));         /*创建新元素的结点空间*/

    if (!s) return -1;
  s->data=newElem;
  s->next=p->next;   p->next=s;  /*将元素newElem插入第k-1个元素之后*/
  retun 0;
  )/*Insert List*/


【函数】单链表的刑除运算。


int Delete_List(LinkList L, int k)   /*L为带头结点单链表的头指针*/

/*删除表中的第k个元素结点,若成功则返回0,否则返回-1*/

/*删除第k个元素相当于令第k-1个元素结点的指针域指向第k+1个元素所在结点*/

{      LinkList p,q;  /*p、g为临时指针*/
  if (k==1)  p=L;  /*删除的是第一个元素结点*/
  else p=Find_List(L, k-1);  /*查找表中的第k-1个元素并令p指向该元素结点*/

       if (!p ll !p->next)  return -1;  /*表中不存在第k个元素*/
  q=p->next;  /*令q指向第k个元素结点*/
  p->next=q->next;   free(q);  /*删除结点*/
  return 0;
}/*Delete_List */

当线性表采用链表作为存储结构时,不能对数据元素进行随机访问,但是具有插入和删除操作不需要移动元素的优点。

根据结点中指针域的设置方式,还有其他几种链表结构。
双向链表。每个结点包含两个指针,分别指出当前元素的直接前驱和直接后继。其特点是可以从表中任意的结点出发,从两个方向上遍历链表。
循环链表。在单向链表(或双向链表)的基础上令表尾结点的指针指向链表的第一个结点,构成循环链表。其特点是可以从表中任意结点开始遍历整个链表。
静态链表。借助数组来描述线性表的链式存储结构,用数组元素的下标表示元素所在结点的指针。

若双向链表中结点的front和next指针域分别指示当前结点的直接前驱和直接后继,则在双向链表中插入结点*s时指针的变化情况如图所示,其操作过程可表示为:


(1)  s->front =p->front; 

(2)  p->front->next=s;     //或者表示为s->front->next=s;
(3)  s->next=p;
(4)  p->front=s;

在双向链表中删除结点时指针的变化情况如图所示,其操作过程可表示为:


(1)  p->front->next=p->next;
(2)  p->next->front =p->front;   

(3)  free(p);

大家可以把以上代码代入程序进行调试。相信过程会非常顺利。每一个字母、每一个指针、每一个符号我都检查过了。请大家放心使用。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值