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

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

✨链式存储

🎆一定别忘记生成新结点

🍔存储结构

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!

相关文章
|
2月前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
26 3
|
2月前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
33 6
|
2月前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
72 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
29天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
2月前
|
存储
ES6中的Set数据结构的常用方法和使用场景
ES6中的Set数据结构的常用方法和使用场景
|
2月前
|
存储 算法 索引
HashMap底层数据结构及其增put删remove查get方法的代码实现原理
HashMap 是基于数组 + 链表 + 红黑树实现的高效键值对存储结构。默认初始容量为16,负载因子为0.75。当存储元素超过容量 * 负载因子时,会进行扩容。HashMap 使用哈希算法计算键的索引位置,通过链表或红黑树解决哈希冲突,确保高效存取。插入、获取和删除操作的时间复杂度接近 O(1)。
29 0
|
2月前
01(数据结构考研)线性表相关操作代码
01(数据结构考研)线性表相关操作代码
74 0
|
27天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
123 9
|
18天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
22 1
|
5天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
26 5