数据结构系列--线性表总结+栈的前戏

数据结构系列--线性表总结+栈的前戏

原创 慧子和她的3D 慧子和她的3D 昨天

    栈的实现依赖线性表,回顾线性表(顺序表,单链表,双链表,静态链表,循环链表)。审查前文代码,作如下改进来达到复用效果,单链表静态链表依旧原版。

顺序表源代码

#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 ;
  if(capacity >= 0 )
  {
    ret = (TSeqList*)malloc(sizeof(TSeqList) + sizeof(TSeqListNode)* capacity);
  }
  
  if(ret != NULL)//指针不为0时可以继续赋值操作
  {
    ret->capacity = capacity;//data作为静态链表表头表示长度
    ret->length = 0;
    ret->node = (TSeqListNode*)(ret + 1);// 初始化的时候头结点的前向指针为空,即便不用也要初始化
  }
  
  return ret;
}

void SeqList_Destory(SeqList* list)
{
  free(list);
}

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_Insert(SeqList* list,SeqListNode* node,int pos)
{
  TSeqList* sList = (TSeqList*)list;//用到了数据封装,所以强制类型转换 
  int ret =(sList !=NULL);//单链表方法完成判断
  int i=0;
  
  ret = ret && (sList->length+ 1 <= sList-> capacity);
  ret = ret && (0 <= pos);
  
  if(ret)//在数组中找空闲位置index
  {
    if(pos >= sList->length)
      {
        pos = sList->length;
      }
    for(i=sList->length;i>pos;i--)
      {
        sList->node[i] = sList->node[i-1]
      }
       sList->node[i] = (TSeqListNode)node;
       sList->length++ ;
     }
  // 插入位置在头结点的位置比较特殊。pre需指向空
  return ret;
}

//由表头开始通过next域移动pos次后,当前元素的next域即要获取元素在数组中的下标
SeqListNode* SeqList_Get(SeqList* list,int pos)
{
    SeqList* sList = (TSeqList*)list;//用到了数据封装,所以强制类型转换 
    SeqListNode* ret = NULL;//定义一个返回值
    
    if( (sList != NULL ) && (0 <= pos) && (pos <= sList->length) )//链表不为空是合法的,长度正常
    // 测试的时候多了一个等号,应该改为pos < sList->length
        {
          ret = (SeqListNode*)(sList->node[pos]);
        }
    return ret;
}

//获取第pos个元素,将第pos个元素从链表里删除
SeqListNode* SeqList_Delete(SeqList* list,int pos)
{
  TSeqList* sList = (TSeqList*)list;//用到了数据封装,所以强制类型转换 
  SeqListNode* ret = SeqList_Get(list,pos);//定义一个返回值
  int i = 0;
  if(ret != NULL)
    {
      for(i = pos+1;i<sList->length;i++)
        {
        sList->node[i-1] = sList->node[i];
        }
      sList->length--;
    }
   return ret;

}

顺序表测试程序

#include <stdio.h>
#include <stdlib.h>
#include "SeqList.h"
 int main(int argc, char *argv[])
{
   SeqList*list = SeqList_Create(10);// 修正版让顺序表可以容纳10个元素,添加6个元素进顺序表
   int i = 0;
   int j = 1;
   int k = 2;
   int x = 3;
   int y = 4;
   int z = 5;
   
   SeqList_Insert(list, &i,0 );
   SeqList_Insert(list, &j,0 );
   SeqList_Insert(list, &k,0 );
   SeqList_Insert(list, &x,0 );
   SeqList_Insert(list, &y,0 );
   SeqList_Insert(list, &z,0 );
   
   SeqList_Delete(list, 5 );
   printf("%x\n",SeqList_Get(list, 5));// 最后一个元素已经删掉但依旧返回地址值
   SeqList_Destory(list);
   return 0; 
 }

循环链表改进部分

源文件修改两部分,其余和前文一致

修正Insert函数

// 插入时,如果表头是空的指向NULL,元素是空的,进行单链表元素插入时,现将插入元素
// 尾结点与NULL相连,再把插入元素数据与前结点相连,再把该节点next与自己相连,去除原来NULL,构成循环链表
int CircleList_Insert(CircleList* list,CircleListNode* node,int pos)//o(n)n是插入元素的位置·
{
    TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换 
    int ret =(sList !=NULL)&& (pos >=0) && (node != NULL);//单链表方法完成判断
    int i=0;
    
   if(ret)//在数组中找空闲位置index
      {
      CircleListNode* current = (CircleListNode*)sList;
      
      for(i = 0;(i<pos) && (current->next != NULL); i++)
        {
          current = current->next;
        }
      
      node->next = current->next;
      current->next = node;
      
      if(sList->length == 0)// 插入的元素是第一个,length的值为0
        {
          node->next =  node;// 新元素node的next指针指向自己
          // node->next = node;// 插入式循环链表第一个元素,将该元素的next指针指向自己
          // 此时存在的问题是只修正了一次,应该在每次插入到第一个的时候都修正一次
        }
     if(current == (CircleListNode*)sList ) // 判断插入的位置是不是第零个位置,是则for不会执行,current指向表头
    {
       (CircleListNode* last = CircleList_Get(sList,sList->length - 1);// 修正的时候把最后一个元素找到} 
        last->next = current->next;
      }
  
  return ret;
}

  删除函数的时候,每次都要遍历链表,效率很低,用指针指向链表的逻辑最后一个元素

//获取第pos个元素,将第pos个元素从链表里删除
//特殊的删除第一个元素,除了将表头next移到第二个元素之外,还要将最后一个next移到第二个next
// 只有删除的元素是第一个元素才会用到last指针,修改,使得在需要的时候再遍历
CircleListNode* CircleList_Delete(CircleList* list,int pos)//o(n)
{
  TCircleList* sList = (TCircleList*)list;//用到了数据封装,所以强制类型转换 
  CircleListNode* ret = NULL;//定义一个返回值
  int i = 0;
  if( (sList !=NULL) && (0 <= pos)&& (sList->length>0) )//链表不为空是合法的,长度正常
  // 加一个条件增强健壮性检测,再只有表头时,环都没有形成,不必再做后面操作
  {
    CircleListNode* current = (CircleListNode*)sList;
    CircleListNode* first = sList->header.next;// 标记第一个元素
    CircleListNode* last = NULL ;
    // 由get函数得到最后一个元素
    for(i=0;i<pos;i++)
      {
        current = current->next;//第一个元素所在下标
      }
      if(current ==(CircleListNode*)sList)// 删除第一个元素,再遍历
      {
      last = (CircleListNode*)CircleList_Get(sList,sList->length - 1);
      }
    ret = current->next;
    current->next = ret->next;
    
    sList->length--;
    
    if( last !== NULL )// 判断删除元素是否是原来表头,first指针与原来ret指针是否是同一个
      {
        sList->header.next = ret->next;// 将表头指向ret
        last->next = ret->next;// 指针移动到原来的第二个元素
      }
      if(sList->slider == ret)
      {
      sList->slider = ret->next;
      }
      if(sList->length == 0)// 如果链表空了则前面操作没有意义
      {
        sList->header.next = NULL;// 复原
         sList->slider = NULL;// 复原
      }
  }
  return ret;

}

测试文件

int main(int argc,char *argv[])
{
  CircleList*  list = CircleList_Create();
  
  struct Value v1;
  struct Value v2;
  struct Value v3;
  struct Value v4;
  struct Value v5;
  struct Value v6;
  struct Value v7;
  struct Value v8;
  
  int i = 0;
  
  v1.v = 1 ;
  v2.v = 2 ;
  v3.v = 3 ;
  v4.v = 4 ;
  v5.v = 5 ;
  v6.v = 6 ;
  v7.v = 7 ;
  v8.v = 8 ;
  // 原来用的尾插法,现改为头插法,暴露源码bug
  CircleList_Insert(list( CircleListNode*)&V1, 0);           
  CircleList_Insert(list( CircleListNode*)&V2, 0); 
  CircleList_Insert(list( CircleListNode*)&V3, 0); 
  CircleList_Insert(list( CircleListNode*)&V4, 0); 
  
  // CircleList_Insert(list,( CircleListNode*)&V5,5);
 //  CircleList_Delete(list,0);
  for(i = 0;i<2*CircleList_Length(list);i++)
  {
  struct Value* pv = (struct Value*)CircleList_Get(list,i);
  printf("%d\n",pv->v);
  }
  while(CircleList_Length(list)>0)// 结束条件为长度为0
  {
  CircleList_Delete(list,0);// 不断删除第一个元素
  }
  printf("\n");
  
 
   CircleList_Destory(list); 
    return 0;
}

双向链表修正

修改测试程序暴露bug

int main(int argc,char *argv[])
{

DlinkList* list =  DlinkList_Creat();
// 定义
struct Value v1;
struct Value v2;
struct Value v3;
struct Value v4;
struct Value v5;
// 赋值
v1.v = 1;
v2.v = 2;
v3.v = 3;
v4.v = 4;
v5.v = 5;
// 头插法插入五个元素
DlinkList_Insert(list,(DLinkListNode*)&v1,0);
DlinkList_Insert(list,(DLinkListNode*)&v2,0);
DlinkList_Insert(list,(DLinkListNode*)&v3,0);
DlinkList_Insert(list,(DLinkListNode*)&v4,0);
DlinkList_Insert(list,(DLinkListNode*)&v5,0);

DlinkList_Reset(list);// 使得游标指向第一个元素
// DlinkList_Pre(list);// 移动游标,从第一个元素向前移动一个,理论应该变为空
printf("%x/n",DlinkList_Current(list));
DlinkList_Destroy(list);

return 0;
}

问题在于插入元素是链表的第一个元素时修正指针指向空

int DlinkList_Insert(DlinkList* list,DlinkListNode* node,int pos)
{
  TDlinkList* sList = (TDlinkList*)list;//用到了数据封装,所以强制类型转换 
  int ret =(sList !=NULL) && (pos >=0) && (node != NULL);//单链表方法完成判断
  int i=0;
  
  if(ret)//在数组中找空闲位置index
  {
    DlinkListNode* current = (DlinkListNode*)sList;
    DlinkListNode* next = NULL ;// 定义指针
    // 移动到插入位置
    for(i=0;(i<pos) && (current->next != NULL);i++)//后面是个保护动作,对于位置的保护,最多放在最后
      {
        current = current->next;//当前移动到下一个位置
      }
    next = current->next;
    
    current->next = node;
    node->next = next;
      // 新插入位置是双向链表第一个
    if(next != NULL)// 双向链表第一个元素的next不能为空,不然会与76行报错
        {
          next->pre = node;
        }
    node->pre = current;
    
    if (sLsit->length == 0)// 如果是个空链表插入第一个位置,那头结点要指向空
        {
         sList->slider = node; 
        }
    if(current == (DlinkListNode*)sList)// 判断插入的是不是第一个元素
        {// 修正插入指针
           node->pre = NULL;
        }
    sList->length++ ;
  
     }
  // 插入位置在头结点的位置比较特殊。pre需指向空
  return ret;
}

栈是一种特殊的线性表

仅能在线性表的一段进行操作即栈顶,后进先出

 


实现顺序栈

类似于在数组最开头进行操作

顺序栈头文件

#ifndf _SEQSTACK_H_
#define _SEQSTACK_H_

typedef void SeqStack;//用类来数据封装
// 并没有创建新的类型,只是把void取个别名,SeqStack和SeqList类型兼容,可直接返回
SeqStack* SeqStack_Create(int capacity);
void SeqStack_Destory(SeqStack* stack);
void SeqStack_Clear(SeqStack* stack);
int SeqStack_Push(SeqStack* stack,void* item);// 进哪个栈,保存地址
void* SeqStack_Pop(SeqStack* stack);// 出栈弹出哪个栈
void* SeqStack_Top(SeqStack* stack);// 栈顶元素
int SeqStack_Size(SeqStack* stack);
int SeqStack_Capacity(SeqStack* stack);
#endif

栈源文件(切记要把SeqList文件放到同一个工程下)

#include "SeqStack.h"
#include "SeqList.h"// 用顺序表来实现顺序栈
SeqStack* SeqStack_Create(int capacity)
{
  return SeqList_Create(capacity);
}
void SeqStack_Destory(SeqStack* stack)
{
  SeqList_Destory(stack);
}
void SeqStack_Clear(SeqStack* stack)
{
  SeqList_Clear(stack);
}
int SeqStack_Push(SeqStack* stack,void* item)// 新元素压到栈顶,选顺序表队尾当栈顶
{
  return SeqList_Insert(stack,item,SeqList_Length(stack));
}
void* SeqStack_Pop(SeqStack* stack)
{
  return SeqList_Delete(stack,SeqList_Length(stack) - 1);// 队尾元素下标
}
void* SeqStack_Top(SeqStack* stack)
{
  return SeqList_Get(stack,SeqList_Length(stack) - 1);
}
int SeqStack_Size(SeqStack* stack)
{
  return SeqList_Length(stack);
}
int SeqStack_Capacity(SeqStack* stack)
{
  return SeqList_Capacity(stack);
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "SeqStack.h"

int main(int argc,char *argv[])
{
  SeqStack* stack = SeqStack_Create(20);
  int a[10];
  int i=0;
  for(i = 0;i < 10;i++)
  {
    a[i] = i;// 边赋值边压栈
    SeqStack_Push(stack,a + i);
  }
  printf("Top : %d\n",*(int*)SeqStack_Top(stack));
  printf("Capacity : %d\n",SeqStack_Capacity(stack));
  printf("Length : %d\n",SeqStack_Size(stack));
 // 弹出测试
  while(SeqStack_Size(stack) > 0 )
  {
    printf("Pop : %d\n",*(int*)SeqStack_Pop(stack));
  }
  SeqStack_Destory(stack);
  return 0;
}

栈的链式储存结构(类似于单链表)

链式存储

#ifndf _LINKSTACK_H_
#definf _LINKSTACK_H_

typedef void LinkStack;
LinkStack* LinkStack_Create();
void LinkStack_Destory(LinkStack* stack);
void LinkStack_Clear(LinkStack* stack);
int LinkStack_Push(LinkStack* stack,void* item);
void* LinkStack_Pop(LinkStack* stack);
void* LinkStack_Top(LinkStack* stack);
int LinkStack_Size(LinkStack* stack);
#endif

源文件

#include <malloc.h>
#include "LinkStack.h"
#include "LinkList.h"
// 链表与顺序表的区别,定义结构体
typedef struct _tag_LinkStackNode
{
  LinkListNode header;// 第一个成员
  void* item;// 保存地址
}TLinkStackNode;

LinkStack* LinkStack_Create()
{
  return LinkStack_Create(capacity);
}

void LinkStack_Destory(LinkStack* stack)
{
  LinkStack_Clear(stack);// 防止内存泄漏先清除
  LinkList_Destory(stack);
}

void LinkStack_Clear(LinkStack* stack)// 不能直接调用线性表的clear,都是malloc出来的元素,直接调用会会导致内存泄露
{
  while(LinkStack_Size(stack)>0)// 只要链式栈有元素就逐渐弹出
  {
    LinkStack_Pop(stack);
  }
}

int LinkStack_Push(LinkStack* stack,void* item)// 新元素压到栈顶
{
  TLinkStackNode* node = (TLinkStackNode*)malloc(sizeof(TLinkStackNode));
  int ret = (node !=NULL) &&  (item != NULL);// 申请是否成功
  if (ret)
  {
    node->item = item;// item保存传进来的item
    ret = LinkStack_Insert(stack,(LinkListNode*)node,0);// node存到链表里,队头作为栈顶
  }
  if ( !ret )
  {
   free(node);// 没有插成功释放node
  }
  return ret;
}

void* LinkStack_Pop(LinkStack* stack)
{ 
  TLinkStackNode* node = (TLinkStackNode*)LinkList->Delete(stack,0);
  void* ret = NULL;
  if (node != NULL)
  {
    ret = node->item ;
   free(node);
  } 
  return ret;
}

void* LinkStack_Top(LinkStack* stack)
{
  TLinkStackNode* node = (TLinkStackNode*)LinkList->Get(stack,0);// 返回栈顶元素
  void ret = NULL;
  if (node != NULL)
  {
    ret = node->item ;
  } 
  return ret;
}
int LinkStack_Size(SeqStack* stack)
{
  return LinkStack_Length(stack);
}
int LinkStack_Capacity(LinkStack* stack)
{
  return LinkStack_Capacity(stack);
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include “LinkStack.h”
int main(int argc,char *argv[])
{
  LinkStack* stack = LinkStack_Create();
  int a[10];
  int i = 0;
  for(i = 0;i < 10;i++)
  {
    a[i] = i;
    LinkStack_Push(stack,a + i);
  }
  printf("Top : %d\n",*(int*)LinkStack_Top(stack));
  printf("Length : %d\n",LinkStack_Size(stack));
  // LinkStack_Clear(stack);
  while(LinkStack_Size(stack) > 0 )
  {
    printf("Pop : %d\n",*(int*)LinkStack_Pop(stack));
  }
  LinkStack_Destory(stack);

  return 0;
}

小结:

栈是一种特殊的线性表

栈只允许在线性表的一端进行操作

栈通常有两种实现方式:顺序结构实现

                                  链式结构实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个完整版的线性表代码,包括顺序表和链表两种实现方式: 1. 顺序表实现: ```c #define MaxSize 100 // 定义线性表的最大长度 typedef struct { int data[MaxSize]; // 存储线性表元素的数组 int length; // 线性表的当前长度 } SqList; // 顺序表类型定义 // 初始化线性表 void InitList(SqList &L) { for (int i = 0; i < MaxSize; i++) { L.data[i] = 0; // 将所有元素初始化为0 } L.length = 0; // 线性表长度初始化为0 } // 插入元素 bool ListInsert(SqList &L, int i, int e) { if (i < 1 || i > L.length + 1) { // 判断插入位置是否合法 return false; } if (L.length >= MaxSize) { // 判断线性表是否已满 return false; } for (int j = L.length; j >= i; j--) { // 将插入位置后的元素依次后移 L.data[j] = L.data[j - 1]; } L.data[i - 1] = e; // 将新元素插入到指定位置 L.length++; // 线性表长度加1 return true; } // 删除元素 bool ListDelete(SqList &L, int i, int &e) { if (i < 1 || i > L.length) { // 判断删除位置是否合法 return false; } e = L.data[i - 1]; // 将被删除的元素赋值给e for (int j = i; j < L.length; j++) { // 将删除位置后的元素依次前移 L.data[j - 1] = L.data[j]; } L.length--; // 线性表长度减1 return true; } // 查找元素 int LocateElem(SqList L, int e) { for (int i = 0; i < L.length; i++) { if (L.data[i] == e) { return i + 1; // 返回元素在线性表中的位置 } } return 0; // 没有找到返回0 } ``` 2. 链表实现: ```c typedef struct Node { int data; // 存储数据元素 struct Node *next; // 存储下一个节点的指针 } LNode, *LinkList; // 链表节点类型定义和链表类型定义 // 初始化链表 void InitList(LinkList &L) { L = (LinkList)malloc(sizeof(LNode)); // 创建头节点 L->next = NULL; // 头节点的指针域初始化为NULL } // 插入元素 bool ListInsert(LinkList &L, int i, int e) { LinkList p = L, s; // p指向头节点,s为新节点 int j = 0; while (p && j < i - 1) { // 寻找第i-1个节点 p = p->next; j++; } if (!p || j > i - 1) { // 判断插入位置是否合法 return false; } s = (LinkList)malloc(sizeof(LNode)); // 创建新节点 s->data = e; // 将新元素赋值给新节点 s->next = p->next; // 将新节点插入到第i个节点之后 p->next = s; return true; } // 删除元素 bool ListDelete(LinkList &L, int i, int &e) { LinkList p = L, q; // p指向头节点,q为待删除节点 int j = 0; while (p->next && j < i - 1) { // 寻找第i-1个节点 p = p->next; j++; } if (!p->next || j > i - 1) { // 判断删除位置是否合法 return false; } q = p->next; // 将待删除节点赋值给q e = q->data; // 将待删除节点的元素赋值给e p->next = q->next; // 将待删除节点从链表中删除 free(q); // 释放待删除节点的内存空间 return true; } // 查找元素 int LocateElem(LinkList L, int e) { LinkList p = L->next; // p指向第一个节点 int i = 1; while (p) { // 遍历链表 if (p->data == e) { return i; // 返回元素在链表中的位置 } p = p->next; i++; } return 0; // 没有找到返回0 } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值