【数据结构】线性表(顺序存储和链式存储)两种方法,细节满满,保你学会(二)

简介: 【数据结构】线性表(顺序存储和链式存储)两种方法,细节满满,保你学会

✨链式存储

🎆一定别忘记生成新结点

🍔存储结构

typedef struct LNode{
  int data;     //数据域
  struct LNode *next; //指针域
}LNode,*LinkList;   

LinkList为指向结构体LNode的指针类型

🚥🚥🚥🚥🚥🚥

⭐习惯上用LinkList定义单链表,强调的是某个单链表的头指针,用LNode*定义指向单链表中任意结点的指针变量

例如:

定义LinkList L,那么L为单链表的头指针

定义LNode *p,那么p为指向单链表某个结点的指针,用*p代表该节点

⭐注意区分指针变量和结点变量两个不同的概念

例如

若定义LinkList p或LNode *p

p表示指向某个结点的指针变量,表示该结点的地址

*p表示对应的结点变量,表示该结点的名称

这两种定义方式是等价的

🚥🚥🚥🚥🚥🚥

⭐易错:首元结点VS头节点VS头指针


头节点是首元结点之前附设的结点

头指针是指向链表第一个结点的指针

(如果有头节点,那么头指针指向的结点为头节点)

(如果没有头节点,那么头指针指向的结点为首元结点)

image.png🚥🚥🚥🚥🚥🚥

⭐链表增加头结点的好处


🎈便于首元结点的处理

增加头结点后,首元结点的地址

🎈便于空表和非空表的统一处理

没有头结点,设L为单链表的头指针,L应该指向首元结点,那么当单链表为长度为0的空表时 ,L指针为空(L==NULL)

有头结点,无论链表是否为空,头指针都是指向头节点的非空指针,那么当头结点的指针域为空,(L->next==NULL)如下图所示  

image.png

🚥🚥🚥🚥🚥🚥

在顺序表中,由于相邻的两个元素在物理位置上相邻,那么每个元素的存储位置都可以从线性表的起始位置计算得到

在单链表里面,各个元素的存储位置都是任意的,都是每个元素的存储位置都包含着其直接前驱结点,因为p->next=ai , p->next->next=ai+1 , 那么想要取得第i个数据元素必须从头指针出发顺链寻找

🚥🚥🚥🚥🚥🚥

🎈初始化


生成新结点作为头结点,用头指针 L 指向头结点

头结点的指针域置空

int InitList(LinkList &L)
{
  L=new LNode;
  L->next=NULL;
  return 1;
}

int InitList(LinkList *L)
{
  *L=new LNode;
  (*L)->next=NULL;//必须加上括号
  return 1;
}

为什么必须加上():优先级:->大于*

🎈尾插法建立单链表


1.创建一个只有头结点的空链表

2.尾指针 r 初始化,指向头结点

3.根据创建链表包括的元素个数n,循环n次执行以下操作

~生成一个新结点*p

~输入元素并把值赋给新结点*p的数据域

~将新结点*p插入尾结点*r后面

~尾指针r指向新的尾结点*p

(在前面说过,*X表示结点的名称)

image.png

int Create_back(LinkList &L,int num)
{
  L=new LNode;
  LNode *p,*r;    //先建立一个带有头节点的空链表 
  L->next=NULL;   //尾指针r指向头结点
  r=L;
  for(int i=0;i<num;i++)
  {
    p=new LNode;  //生成新结点
    cin>>p->data;
    p->next=NULL;
    r->next=p;    //新结点*p插入尾结点*r后(*X表示结点的名称)
    r=p;      //r指向新的尾结点*p
  } 
  return 1;
}

代码里面的 LNode *p,*r;还可以写为LinkList p,r;具体原因在上面说过了(就是在上面的⭐注意区分指针变量和结点变量两个不同的概念

int Create_back(LinkList *L,int num)
{
  *L=new LNode;
  LNode *p,*r;    //先建立一个带有头节点的空链表 
  (*L)->next=NULL;    //尾指针r指向头结点
  r=*L;
  for(int i=0;i<num;i++)
  {
    p=new LNode;  //生成新结点
    cin>>p->data;
    p->next=NULL;
    r->next=p;    //新结点*p插入尾结点*r后(*X表示结点的名称)
    r=p;      //r指向新的尾结点*p
  } 
  return 1;
}

🎈头插法建立单链表

1.创建一个只有头结点的空链表

2.根据创建链表包括的元素个数n,循环n次执行以下操作

~生成一个新结点*p

~输入元素并把值赋给新结点*p的数据域

~将新结点*p插入到头结点后面

image.png

int Create_front(LinkList &L,int num)
{
  L=new LNode;
  LNode *p;
  L->next=NULL;
  for(int i=0;i<num;i++)
  {
    p=new LNode;    //生成新结点*p
    cin>>p->data;
    p->next=L->next;
    L->next=p;      //将新结点*p插入到头结点后面
  }
  return OK;
}
int Create_front(LinkList *L,int num)
{
  *L=new LNode;
  LNode *p;
  (*L)->next=NULL;
  for(int i=0;i<num;i++)
  {
    p=new LNode;    //生成新结点*p
    cin>>p->data;
    p->next=(*L)->next;
    (*L)->next=p;     //将新结点*p插入到头结点后面
  }
  return OK;
}

🎈插入

注意:插入和前面的前插法尾插法不同,前插法尾插法是建立单链表,而插入是在已经建立好的单链表里面再加入额外的结点

具体步骤如下图所示

image.png

注意:插入操作必须要找到该位置的前驱结点

这句话看上去理所应当,但是在某些时候是真的特别有用)

int ListInsert(LinkList &L,int num,int place)
{
  LNode *p,*s;
  p=L;
  int j=0;
  while(p&&j<place-1)        //找到插入位置
  {
    p=p->next;
    j++;
  }
  if(!p||j>place-1) return ERROR;
  s=new LNode;         //生成新结点
  s->data=num;         //数据域赋值
  s->next=p->next;       //结点*s的指针域指向a2
  p->next=s;           //结点*p的指针域指向*s
  return OK;
}
int ListInsert(LinkList *L,int num,int place)
{
  LNode *p,*s;
  p=*L;
  int j=0;
  while(p&&j<place-1)        //找到插入位置
  {
    p=p->next;
    j++;
  }
  if(!p||j>place-1) return ERROR;
  s=new LNode;         //生成新结点
  s->data=num;         //数据域赋值
  s->next=p->next;       //结点*s的指针域指向a2
  p->next=s;           //结点*p的指针域指向*s
  return OK;
}

🎈删除

1.先找到待删除位置a的前驱结点

2.创建临时结点q,保存待删除结点的地址

3.将结点*p的指针域指向a的直接后继结点

4.释放结点a的空间

image.png

int LinkDelete(LinkList &L,int place)
{
  LNode *p,*q;
  p=L;
  int j=0;
  while((p->next)&&(j<place-1))
  {
    p=p->next;
    ++j;
  } 
  if(!(p->next)||(j>place-1) )return ERROR;
  q=p->next;        //创建临时结点q
  p->next=q->next;    //改变待删除结点的前驱结点的指针域
  delete q;       //释放被删除结点的空间
  return OK;
}
int LinkDelete(LinkList *L,int place)
{
  LNode *p,*q;
  p=*L;
  int j=0;
  while((p->next)&&(j<place-1))
  {
    p=p->next;
    ++j;
  } 
  if(!(p->next)||(j>place-1) )return ERROR;
  q=p->next;        //创建临时结点q
  p->next=q->next;    //改变待删除结点的前驱结点的指针域
  delete q;       //释放被删除结点的空间
  return OK;
}

🎈取出元素

找到要取出的元素的位置,然后返回数据域即可

int ListPop(LinkList &L,int num)
{
  LNode *p;
  p=L;
  int j=0;
  while(p&&j<num)
  {
    p=p->next;
    j++;
  }
  if(p)
  return p->data;
  else
  return -1;
}
int ListPop(LinkList *L,int num)
{
  LNode *p;
  p=*L;
  int j=0;
  while(p&&j<num)
  {
    p=p->next;
    j++;
  }
  if(p)
  return p->data;
  else
  return -1;
}

🎈输出链表

void OutputList(LinkList &L)
{
  LNode *p;
  p=L->next;
  while(p)
  {
    cout<<p->data<<' ';
    p=p->next;
  }
  cout<<endl;
}
void OutputList(LinkList *L)
{
  LNode *p;
  p=(*L)->next;
  while(p)
  {
    cout<<p->data<<' ';
    p=p->next;
  }
  cout<<endl;
}

😎完整代码1

#include<iostream>
using namespace std;
#define OK 1
#define ERROR -1
typedef struct LNode
{
  int data;
  struct LNode *next;
}LNode,*LinkList;
//1
int InitList(LinkList *L)
{
  *L=new LNode;
  (*L)->next=NULL;
  return OK;
}
int Create_front(LinkList *L,int num)
{
  *L=new LNode;
  LNode *p;
  (*L)->next=NULL;
  for(int i=0;i<num;i++)
  {
    p=new LNode;    //生成新结点*p
    cin>>p->data;
    p->next=(*L)->next;
    (*L)->next=p;     //将新结点*p插入到头结点后面
  }
  return OK;
}
//3
int ListInsert(LinkList *L,int num,int place)
{
  LNode *p,*s;
  p=*L;
  int j=0;
  while(p&&j<place-1)        //找到插入位置
  {
    p=p->next;
    j++;
  }
  if(!p||j>place-1) return ERROR;
  s=new LNode;         //生成新结点
  s->data=num;         //数据域赋值
  s->next=p->next;       //结点*s的指针域指向a2
  p->next=s;           //结点*p的指针域指向*s
  return OK;
}
//4
int LinkDelete(LinkList *L,int place)
{
  LNode *p,*q;
  p=*L;
  int j=0;
  while((p->next)&&(j<place-1))
  {
    p=p->next;
    ++j;
  } 
  if(!(p->next)||(j>place-1) )return ERROR;
  q=p->next;        //创建临时结点q
  p->next=q->next;    //改变待删除结点的前驱结点的指针域
  delete q;       //释放被删除结点的空间
  return OK;
}
//5
int ListPop(LinkList *L,int num)
{
  LNode *p;
  p=*L;
  int j=0;
  while(p&&j<num)
  {
    p=p->next;
    j++;
  }
  if(p)
  return p->data;
  else
  return -1;
}
void OutputList(LinkList *L)
{
  LNode *p;
  p=(*L)->next;
  while(p)
  {
    cout<<p->data<<' ';
    p=p->next;
  }
  cout<<endl;
}
int main()
{
  LinkList L;
  int n,e;
  cout<<"请输入你的操作"<<endl;
  printf("0:结束程序\n1:建立空表\n2:尾插法建立空表\n3:插入\n4:删除\n5:取出元素\n");
  for(;;)
  {
    cin>>n;
    switch(n)
    {
      case 0:
        return 0;
        break;
      case 1:
        cout<<"建立空表"<<endl;
        if(InitList(&L))
        {
          cout<<"建立成功"<<endl;
        }
        else
        {
          cout<<"建立失败"<<endl;
        }
        break;
      case 2:
        cout<<"输入21 18 30 75 42 56,使用尾插法建立链表"<<endl;
        cout<<"请输入要加入的数字的数量"<<endl;
        int num;
        cin>>num;
        if(Create_front(&L,num)!=0)
        {
           cout<<"请进行下一步操作"<<endl;
           OutputList(&L); 
        }
        else cout<<"失败了"<<endl;
        break;
      case 3:
        cout<<"在第3个位置插入67,在第9个位置插入10"<<endl;
        cout<<"请输入需要插入的数字的个数"<<endl;
        int qqq;
        cin>>qqq;
        for(int i=0;i<qqq;i++)
        {
          cout<<"需要插入的数据和位置"<<endl;
          int nn,mm;
          cin>>nn>>mm;
          if(ListInsert(&L,nn,mm))
          {
            cout<<"插入成功"<<endl;
            OutputList(&L);
          }
          else
          {
            cout<<"插入失败"<<endl;
          }
        }
        break;
      case 4:
        cout<<"删除第6个元素和第8个元素"<<endl;
        cout<<"输入需要删除的元素是哪一个"<<endl;
        int qq;
        cin>>qq;
        if(LinkDelete(&L,qq))
        {
          cout<<"删除成功"<<endl;
          OutputList(&L);
        } 
        else
        {
          cout<<"删除失败"<<endl;
        }
        break;
      case 5:
        cout<<"取出第5个元素和第7个元素"<<endl;
        cout<<"需要取出多少个数"<<endl;
        int cnt;
        cin>>cnt;
        for(int i=0;i<cnt;i++)
        {
          cout<<"需要取出哪个位置的元素"<<endl;
          int _;
          cin>>_;
          if(ListPop(&L,_)==-1) cout<<"取出元素失败"<<endl;
          else  cout<<ListPop(&L,_)<<endl;
        }
        break;
    }
  }
}

😎完整代码2

#include<iostream>
using namespace std;
#define OK 1
#define ERROR -1
typedef struct LNode
{
  int data;
  struct LNode *next;
}LNode,*LinkList;
//1
int InitList(LinkList &L)
{
  L=new LNode;
  L->next=NULL;
  return OK;
}
//2
int Create_back(LinkList &L,int num)
{
  L=new LNode;//头节点 
  LNode *p,*r;
  L->next=NULL;
  r=L;
  for(int i=0;i<num;i++)
  {
    p=new LNode;
    cin>>p->data;
    p->next=NULL;
    r->next=p;
    r=p;  
  } 
  return OK;
}
//3
int ListInsert(LinkList &L,int num,int place)
{
  LNode *p,*s;
  p=L;
  int j=0;
  while(p&&j<place-1)
  {
    p=p->next;
    j++;
  }
  if(!p||j>place-1) return ERROR;
  s=new LNode;
  s->data=num;
  s->next=p->next;
  p->next=s;
  return OK;
}
//4
int LinkDelete(LinkList &L,int place)
{
  LNode *p,*q;
  p=L;
  int j=0;
  while((p->next)&&(j<place-1))
  {
    p=p->next;
    ++j;
  } 
  if(!(p->next)||(j>place-1) )return ERROR;
  q=p->next;
  p->next=q->next;
  //e=p->data;
  delete q;
  return OK;
}
//5
int ListPop(LinkList &L,int num)
{
  LNode *p;
  p=L;
  int j=0;
  while(p&&j<num)
  {
    p=p->next;
    j++;
  }
  if(p)
  return p->data;
  else
  return -1;
}
void OutputList(LinkList &L)
{
  LNode *p;
  p=L->next;
  while(p)
  {
    cout<<p->data<<' ';
    p=p->next;
  }
  cout<<endl;
}
int main()
{
  LinkList L;
  int n,e;
  cout<<"请输入你的操作"<<endl;
  printf("0:结束程序\n1:建立空表\n2:尾插法建立空表\n3:插入\n4:删除\n5:取出元素\n");
  for(;;)
  {
    cin>>n;
    switch(n)
    {
      case 0:
        return 0;
        break;
      case 1:
        cout<<"建立空表"<<endl;
        if(InitList(L))
        {
          cout<<"建立成功"<<endl;
        }
        else
        {
          cout<<"建立失败"<<endl;
        }
        break;
      case 2:
        cout<<"输入21 18 30 75 42 56,使用尾插法建立链表"<<endl;
        cout<<"请输入要加入的数字的数量"<<endl;
        int num;
        cin>>num;
        if(Create_back(L,num)!=0)
        {
           cout<<"请进行下一步操作"<<endl;
           OutputList(L); 
        }
        else cout<<"失败了"<<endl;
        break;
      case 3:
        cout<<"在第3个位置插入67,在第9个位置插入10"<<endl;
        cout<<"请输入需要插入的数字的个数"<<endl;
        int qqq;
        cin>>qqq;
        for(int i=0;i<qqq;i++)
        {
          cout<<"需要插入的数据和位置"<<endl;
          int nn,mm;
          cin>>nn>>mm;
          if(ListInsert(L,nn,mm))
          {
            cout<<"插入成功"<<endl;
            OutputList(L);
          }
          else
          {
            cout<<"插入失败"<<endl;
          }
        }
        break;
      case 4:
        cout<<"删除第6个元素和第8个元素"<<endl;
        cout<<"输入需要删除的元素是哪一个"<<endl;
        int qq;
        cin>>qq;
        if(LinkDelete(L,qq))
        {
          cout<<"删除成功"<<endl;
          OutputList(L);
        } 
        else
        {
          cout<<"删除失败"<<endl;
        }
        break;
      case 5:
        cout<<"取出第5个元素和第7个元素"<<endl;
        cout<<"需要取出多少个数"<<endl;
        int cnt;
        cin>>cnt;
        for(int i=0;i<cnt;i++)
        {
          cout<<"需要取出哪个位置的元素"<<endl;
          int _;
          cin>>_;
          if(ListPop(L,_)==-1) cout<<"取出元素失败"<<endl;
          else  cout<<ListPop(L,_)<<endl;
        }
        break;
    }
  }
} 

🥰如果大家有不明白的地方,或者文章有问题,欢迎大家在评论区讨论,指正🥰

Code over!

相关文章
|
13天前
|
存储
数据结构第二课 -----线性表之单向链表
数据结构第二课 -----线性表之单向链表
|
13天前
|
存储
数据结构第五课 -----线性表之树
数据结构第五课 -----线性表之树
|
13天前
|
机器学习/深度学习 分布式数据库
数据结构第六课 -----链式二叉树的实现
数据结构第六课 -----链式二叉树的实现
|
13天前
数据结构第四课 -----线性表之队列
数据结构第四课 -----线性表之队列
|
13天前
数据结构第四课 -----线性表之栈
数据结构第四课 -----线性表之栈
|
6天前
|
机器学习/深度学习 算法 测试技术
【单调栈】3113. 边界元素是最大值的子数组数目
【单调栈】3113. 边界元素是最大值的子数组数目
|
4天前
栈的基本应用
栈的基本应用
11 3
|
4天前
栈与队列理解
栈与队列理解
10 1
|
4天前
|
存储 算法
数据结构与算法 栈与队列
数据结构与算法 栈与队列
10 0
数据结构与算法 栈与队列
|
4天前
|
C++
数据结构(共享栈
数据结构(共享栈
6 0