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

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

✨链式存储

🎆一定别忘记生成新结点

🍔存储结构

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!

相关文章
|
24天前
|
Java C++ 索引
让星星⭐月亮告诉你,LinkedList和ArrayList底层数据结构及方法源码说明
`LinkedList` 和 `ArrayList` 是 Java 中两种常见的列表实现。`LinkedList` 基于双向链表,适合频繁的插入和删除操作,但按索引访问元素效率较低。`ArrayList` 基于动态数组,支持快速随机访问,但在中间位置插入或删除元素时性能较差。两者均实现了 `List` 接口,`LinkedList` 还额外实现了 `Deque` 接口,提供了更多队列操作。
21 3
|
29天前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
29 6
|
27天前
|
存储 算法 Java
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
前缀(波兰)表达式、中缀表达式和后缀(逆波兰)表达式的基本概念、计算机求值方法,以及如何将中缀表达式转换为后缀表达式,并提供了相应的Java代码实现和测试结果。
29 0
数据结构与算法学习八:前缀(波兰)表达式、中缀表达式、后缀(逆波兰)表达式的学习,中缀转后缀的两个方法,逆波兰计算器的实现
|
8天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
21天前
|
存储
ES6中的Set数据结构的常用方法和使用场景
ES6中的Set数据结构的常用方法和使用场景
|
24天前
|
存储 算法 索引
HashMap底层数据结构及其增put删remove查get方法的代码实现原理
HashMap 是基于数组 + 链表 + 红黑树实现的高效键值对存储结构。默认初始容量为16,负载因子为0.75。当存储元素超过容量 * 负载因子时,会进行扩容。HashMap 使用哈希算法计算键的索引位置,通过链表或红黑树解决哈希冲突,确保高效存取。插入、获取和删除操作的时间复杂度接近 O(1)。
26 0
|
29天前
01(数据结构考研)线性表相关操作代码
01(数据结构考研)线性表相关操作代码
52 0
|
5天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
64 9
|
2天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
4天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
26 4