数据结构笔记——2. 线性表

(1). 线性表的本质

  线性表可用于描述“队列类型”关系的问题

  1. 线性表的定义

    1. 线性表(List)是零个或多个数据元素的集合

    2. 线性表中的数据元素之间是有顺序的

    3. 线性表中的数据元素个数是有限的

    4. 线性表中的数据元素的类型必须相同

  2. 线性表的性质

    1. a0为线性表的第一个元素,只有一个后继

    2. an为线性表的最后一个元素,只有一个前驱

    3. 除a0和an外的其他元素ai,既有前驱,又有后继

    4. 线性表能够逐项访问和顺序存取

(2). 线性表的顺序存储结构

  1. 顺序存储定义

      线性表的顺序存储结构,指的是用一段地址连续的存储单元依次存储线性表的数据元素。

  2. 部分功能实现

    1. 存储实现

      typedef struct _tag_SeqList
      {
          int capacity;       // 线性表存储容量
          int length;         // 线性表当前长度
          TSeqListNode* node; // 存储空间起始地址
      } TSeqList;
      
    2. 创建线性表操作
      在这里插入图片描述

      SeqList* SeqList_Create(int capacity) 
      {
          TSeqList* ret = NULL;
          
          // 1. 创建存储空间
          if (capacity >= 0)
          {
              ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode) * capacity);
          }
          
          // 2. 初始化线性表
          if (ret != NULL)
          {
              ret->capacity = capacity;
              ret->length = 0;
              // 3. 指针操作
              ret->node = (TSeqListNode*)(ret + 1);
          }
          
          return ret;
      }
      
    3. 插入元素操作
      Alt

      int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
      {
          // 1. 判断线性表是否合法
          TSeqList* sList = (TSeqList*)list;
          int ret = (sList != NULL);
          int i = 0;
          
          // 2. 判断插入位置是否合法
          ret = ret && (sList->length + 1 <= sList->capacity);
          ret = ret && (0 <= pos);
          
          if (ret)
          {
              // 3. 若插入位置大于当前长度,则将元素放在最后
              if (pos >= sList->length)
              {
                  pos = sList->length;
              }
              
              // 4. 从最后一个元素开始到第pos个元素,分别将他们都向后移动一个位置
              for (i = sList->length; i > pos; i--)
              {
                  sList->node[i] = sList->node[i-1];
              }
              
              // 5. 将新元素插入
              sList->node[i] = (TSeqListNode)node;
              
              // 6. 长度加1
              sList->length++;
          }
          
          return ret;
      }
      
    4. 删除元素操作
      Alt

      SeqListNode* SeqList_Delete(SeqList* list, int pos)
      {
          // 1. 进行位置合法性检测与删除元素获取
          TSeqList* sList = (TSeqList*)list;
          SeqListNode* ret = SeqList_Get(list, pos);
          int i = 0;
          
          // 2. 判断线性表是否合法
          if (ret != NULL)
          {
               // 3. 把删除位置pos后的元素分别向前移动一个位置
              for (i = pos + 1; i < sList->length; i++)
              {
                  sList->node[i - 1] = sList->node[i];
              }
              
              // 4. 长度减1
              sList->length--;
          }
          
          return ret;
      }
      
  3. 优缺点:

    1. 优点:

      • 无需为线性表中的逻辑关系增加额外的空间

      • 可以快速的获取表中合法位置的元素

    2. 缺点:

      • 插入和删除操作需要移动大量元素

      • 当线性表长度变化较大时难以确定存储空间的容量

  4. 工程实例

    1. 头文件

      #ifndef _SEQLIST_H_
      #define _SEQLIST_H_
      
      typedef void SeqList;
      typedef void SeqListNode;
      
      /*
          该方法用于创建并且返回一个空的线性表
      */
      SeqList* SeqList_Create(int capacity);
      
      /*
          该方法用于销毁一个线性表list
      */
      void SeqList_Destroy(SeqList* list);
      
      /*
          该方法用于将一个线性表list中的所有元素清空
          使得线性表回到创建时的初始状态
      */
      void SeqList_Clear(SeqList* list);
      
      /*
          该方法用于返回一个线性表list中的所有元素个数
      */
      int SeqList_Length(SeqList* list);
      
      /*
          该方法用于返回一个线性表list中最大容量
      */
      int SeqList_Capacity(SeqList* list);
      
      /*
          该方法用于向一个线性表list的pos位置处插入新元素node
          返回值为1表示插入成功,0表示插入失败
      */
      int SeqList_Insert(SeqList* list, SeqListNode* node, int pos);
      
      /*
          该方法用于获取一个线性表list的pos位置处的元素
          返回值为pos位置处的元素,NULL表示获取失败
      */
      SeqListNode* SeqList_Get(SeqList* list, int pos);
      
      /*
          该方法用于删除一个线性表list的pos位置处的元素
          返回值为被删除的元素,NULL表示删除失败
      */
      SeqListNode* SeqList_Delete(SeqList* list, int pos);
      
      #endif
      
    2. 实现文件

      #include <stdio.h>
      #include <malloc.h>
      #include "SeqList.h"
      
      typedef unsigned int TSeqListNode;
      
      typedef struct _tag_SeqList
      {
          int capacity;
          int length;
          TSeqListNode* node;
      } TSeqList;
      
      SeqList* SeqList_Create(int capacity) 
      {
          TSeqList* ret = NULL;
          
          // 1. 创建存储空间
          if (capacity >= 0)
          {
              ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode) * capacity);
          }
          
          // 2. 初始化线性表
          if (ret != NULL)
          {
              ret->capacity = capacity;
              ret->length = 0;
              // 3. 指针操作
              ret->node = (TSeqListNode*)(ret + 1);
          }
          
          return ret;
      }
      
      void SeqList_Destroy(SeqList* list) 
      {
          free(list);
      	list = NULL;
      }
      
      void SeqList_Clear(SeqList* list)
      {
          TSeqList* sList = (TSeqList*)list;
          
          if( sList != NULL )
          {
              sList->length = 0;
          }
      }
      
      int SeqList_Length(SeqList* list)
      {
          TSeqList* sList = (TSeqList*)list;
          int ret = -1;
          
          if( sList != NULL )
          {
              ret = sList->length;
          }
          
          return ret;
      }
      
      int SeqList_Capacity(SeqList* list)
      {
          TSeqList* sList = (TSeqList*)list;
          int ret = -1;
          
          if( sList != NULL )
          {
              ret = sList->capacity;
          }
          
          return ret;
      }
      
      int SeqList_Insert(SeqList* list, SeqListNode* node, int pos)
      {
          // 1. 判断线性表是否合法
          TSeqList* sList = (TSeqList*)list;
          int ret = (sList != NULL);
          int i = 0;
          
          // 2. 判断插入位置是否合法
          ret = ret && (sList->length + 1 <= sList->capacity);
          ret = ret && (0 <= pos);
          
          if (ret)
          {
              // 3. 若插入位置大于当前长度,则将元素放在最后
              if (pos >= sList->length)
              {
                  pos = sList->length;
              }
              
              // 4. 从最后一个元素开始到第pos个元素,分别将他们都向后移动一个位置
              for (i = sList->length; i > pos; i--)
              {
                  sList->node[i] = sList->node[i-1];
              }
              
              // 5. 将新元素插入
              sList->node[i] = (TSeqListNode)node;
              
              // 6. 长度加1
              sList->length++;
          }
          
          return ret;
      }
      
      SeqListNode* SeqList_Get(SeqList* list, int pos)
      {
          TSeqList* sList = (TSeqList*)list;
          SeqListNode* ret = NULL;
          
          if ((sList != NULL) && (0 <= pos) && (pos < sList->length))
          {
      		ret = (SeqListNode*)(sList->node[pos]);
          }
          
          return ret;
      }
      
      SeqListNode* SeqList_Delete(SeqList* list, int pos)
      {
          // 1. 进行位置合法性检测与删除元素获取
          TSeqList* sList = (TSeqList*)list;
          SeqListNode* ret = SeqList_Get(list, pos);
          int i = 0;
          
          // 2. 判断线性表是否合法
          if (ret != NULL)
          {
               // 3. 把删除位置pos后的元素分别向前移动一个位置
              for (i = pos + 1; i < sList->length; i++)
              {
                  sList->node[i - 1] = sList->node[i];
              }
              
              // 4. 长度减1
              sList->length--;
          }
          
          return ret;
      }
      
    3. 测试文件

      #include <stdio.h>
      #include <stdlib.h>
      #include "SeqList.h"
      
      int main(int argc, char *argv[]) 
      {
          SeqList* list = SeqList_Create(3);
          
          int i = 0;
          int j = 1;
          int k = 2;
          int x = 3;
          int index = 0;
          
          SeqList_Insert(list, &i, 0);
          SeqList_Insert(list, &j, 0);
          SeqList_Insert(list, &k, 0);
          SeqList_Insert(list, &x, 0);
          for(index=0; index<SeqList_Length(list); index++)
          {
              int* p = (int*)SeqList_Get(list, index);
              
              printf("%d\n", *p);
          }
          
          printf("\n");
          
          while( SeqList_Length(list) > 0 )
          {
              int* p = (int*)SeqList_Delete(list, 0);
              
              printf("%d\n", *p);
          }
          
          SeqList_Destroy(list);
          
          return 0;
      }
      

(3). 线性表的链式存储结构

  1. 链式存储定义

      为了表示每一个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
    Alt

  2. 部分功能实现

    1. 插入元素操作
      Alt
          int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
          { 
              // 1. 线性表与插入位置合法性检测
              TLinkList* sList = (TLinkList*)list;
              int ret = (sList != NULL) && (pos >= 0) && (node != NULL);
              int i = 0;
              
              if( ret )
              {
                  LinkListNode* current = (LinkListNode*)sList;
                  
                  // 2. 由表头开始通过next指针移动pos次后,当前元素的next指针即指向要插入的位置
                  for(i=0; (i<pos) && (current->next != NULL); i++)
                  {
                      current = current->next;
                  }
                  
                  // 3. 将新元素插入
                  node->next = current->next;
                  current->next = node;
                  
                  // 4. 线性表长度加1
                  sList->length++;
              }
              
              return ret;
          }
      
    2. 删除元素操作
      Alt
          LinkListNode* LinkList_Delete(LinkList* list, int pos)
          {
              TLinkList* sList = (TLinkList*)list;
              LinkListNode* ret = NULL;
              int i = 0;
              
          	// 1. 线性表与删除位置合法性检测
              if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
              {
                  LinkListNode* current = (LinkListNode*)sList;
          		
                  // 2. 由表头开始通过next指针移动pos次后,当前元素的next指针即指向要删除元素的位置
                  for(i=0; i<pos; i++)
                  {
                      current = current->next;
                  }
                  
          		// 3. 获取将删除的元素
                  ret = current->next;
          		
          		// 4. 将元素舍弃
                  current->next = ret->next;
                  
          		// 5. 线性表长度减1
                  sList->length--;
              }
              
              return ret;
          }
      
  3. 优缺点:

    1. 优点:

      • 无需一次性定制链表的容量

      • 插入和删除操作无需移动数据元素

    2. 缺点:

      • 数据元素必须保存后继元素的位置信息

      • 获取指定数据的元素操作需要顺序访问之前的元素

  4. 工程实例

    1. 头文件

      #ifndef _LINKLIST_H_
      #define _LINKLIST_H_
      
      typedef void LinkList;
      typedef struct _tag_LinkListNode LinkListNode;
      struct _tag_LinkListNode
      {
          LinkListNode* next;
      };
      
      /*
      	该方法用于创建并且返回一个空的线性表
      */
      LinkList* LinkList_Create();
      
      /*
      	该方法用于销毁一个线性表list
      */
      void LinkList_Destroy(LinkList* list);
      
      /*
      	该方法用于将一个线性表list中的所有元素清空
      	使得线性表回到创建时的初始状态
      */
      void LinkList_Clear(LinkList* list);
      
      /*
      	该方法用于返回一个线性表list中的所有元素个数
      */
      int LinkList_Length(LinkList* list);
      
      /*
      	该方法用于向一个线性表list的pos位置处插入新元素node
      	返回值为1表示插入成功,0表示插入失败
      */
      int LinkList_Insert(LinkList* list, LinkListNode* node, int pos);
      
      /*
      	该方法用于获取一个线性表list的pos位置处的元素
      	返回值为pos位置处的元素,NULL表示获取失败
      */
      LinkListNode* LinkList_Get(LinkList* list, int pos);
      
      /*
      	该方法用于删除一个线性表list的pos位置处的元素
      	返回值为被删除的元素,NULL表示删除失败
      */
      LinkListNode* LinkList_Delete(LinkList* list, int pos);
      
      #endif
      
    2. 实现文件

      #include <stdio.h>
      #include <malloc.h>
      #include "LinkList.h"
      
      typedef struct _tag_LinkList
      {
          LinkListNode header;
          int length;
      } TLinkList;
      
      LinkList* LinkList_Create() 
      {
          TLinkList* ret = (TLinkList*)malloc(sizeof(TLinkList));
          
          if( ret != NULL )
          {
              ret->length = 0;
              ret->header.next = NULL;
          }
          
          return ret;
      }
      
      void LinkList_Destroy(LinkList* list) 
      {
          free(list);
      	
      	list = NULL;
      }
      
      void LinkList_Clear(LinkList* list) 
      {
          TLinkList* sList = (TLinkList*)list;
          
          if( sList != NULL )
          {
              sList->length = 0;
              sList->header.next = NULL;
          }
      }
      
      int LinkList_Length(LinkList* list) 
      {
          TLinkList* sList = (TLinkList*)list;
          int ret = -1;
          
          if( sList != NULL )
          {
              ret = sList->length;
          }
          
          return ret;
      }
      
      int LinkList_Insert(LinkList* list, LinkListNode* node, int pos)
      { 
          TLinkList* sList = (TLinkList*)list;
          int ret = (sList != NULL) && (pos >= 0) && (node != NULL);
          int i = 0;
          
          if( ret )
          {
              LinkListNode* current = (LinkListNode*)sList;
              
              for(i=0; (i<pos) && (current->next != NULL); i++)
              {
                  current = current->next;
              }
              
              node->next = current->next;
              current->next = node;
              
              sList->length++;
          }
          
          return ret;
      }
      
      LinkListNode* LinkList_Get(LinkList* list, int pos) 
      {
          TLinkList* sList = (TLinkList*)list;
          LinkListNode* ret = NULL;
          int i = 0;
          
          if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
          {
              LinkListNode* current = (LinkListNode*)sList;
              
              for(i=0; i<pos; i++)
              {
                  current = current->next;
              }
              
              ret = current->next;
          }
          
          return ret;
      }
      
      LinkListNode* LinkList_Delete(LinkList* list, int pos)
      {
          TLinkList* sList = (TLinkList*)list;
          LinkListNode* ret = NULL;
          int i = 0;
      
          if( (sList != NULL) && (0 <= pos) && (pos < sList->length) )
          {
              LinkListNode* current = (LinkListNode*)sList;
      
              for(i=0; i<pos; i++)
              {
                  current = current->next;
              }
      
              ret = current->next;
      
              current->next = ret->next;
              
              sList->length--;
          }
          
          return ret;
      }
      
      
    3. 测试文件

      #include <stdio.h>
      #include <stdlib.h>
      #include "LinkList.h"
      
      struct Value
      {
          LinkListNode header;
          int v;
      };
      
      int main(int argc, char *argv[]) 
      {
          int i = 0;
          LinkList* list = LinkList_Create();
          
          struct Value v1;
          struct Value v2;
          
          v1.v = 1;
          v2.v = 2;
      
          LinkList_Insert(list, (LinkListNode*)&v1, LinkList_Length(list));
          LinkList_Insert(list, (LinkListNode*)&v2, LinkList_Length(list));
      
          
          for(i=0; i<LinkList_Length(list); i++)
          {
              struct Value* pv = (struct Value*)LinkList_Get(list, i);
              
              printf("%d\n", pv->v);
          }
          
          while( LinkList_Length(list) > 0 )
          {
              struct Value* pv = (struct Value*)LinkList_Delete(list, 0);
              
              printf("%d\n", pv->v);
          }
          
          LinkList_Destroy(list);
          
          return 0;
      }
      

(4). 小结

  1. 代码难点主要在于插入与删除的实现,后续代码实现会在此代码基础上进行修改或调用。

  2. 本文及以后代码实现,目的在于实现一种可复用的代码程序,故只保存具体数据元素的地址。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值