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

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

⭐定义:

线性表(List):零个或多个数据元素的有限序列


⭐ 理解:

线性表,顾名思义,就是具有像线一样性质的表,元素之间是有顺序的,若元素存在多个,那么第一个元素没有前驱元素,最后一个元素没有后继元素,其他元素既有前驱元素又有后继元素


⭐存储方式 :

线性存储


链式存储


⭐顺序存储的优缺点:

优点:

1.表中数据元素可以根据序号 随机存取


2. 存储密度大,存储密度为1(存储密度是指一个结点中数据元素所占的存储单元和整个结点所占的存储单元之比)


缺点:

1.做插入、删除操作时,要移动大量元素,因此对很长的线性表操作效率低,插入和删除操作不方便;


2.要预先分配存储空间,预先估计过大,会导致存储空间浪费,估计过小,会造成数据溢出。


⭐链式存储的优缺点:

优点:

1.做插入、删除操作时,不用移动大量元素,相比线性存储方式方便;


2.不用预先分配存储空间。


缺点:

1.表中数据元素不可随机存取


2.存储密度小,存储密度小于1


⭐基本操作

✨顺序存储

🍔存储结构

#define maxsize 100
typedef struct{
  ElemType *elem;//由于这里不知道线性表的具体长度,所以这里就用了指针
                   //可以是elem[maxsize]
  int length;
}SqList;

其中ElemType可以改为其他的数据类型 ,比如

typedef int ElemType

length表示当前线性表中数据元素的个数,注意数组里面的下标是从0开始的,那么


a1->elem[0]; //注意,用数组表示


a2->elem[1];


a3->elem[2] ;


an->elem[length-1];


🎈初始化


🚕分配空间,建立空表,使空表长度为0


使用引用初始化

int InitList(SqList &L)
{
  L.elem=new ElemType[maxsize];
  if(!L.elem) return -1;      //内存分配失败
  L.length=0;           //空表长度为0
  return 1; 
}

使用指针初始化

int InitList(SqList* L)
{
  L->elem = new int[100];
  if (!L->elem) exit(overflow);
  L->length = 0;
  return OK;
}

🎈添加数字建立表

🚕向空表里面添加数字,从而建立线性表

使用引用

void InputList(SqList& L, int num)
{
  L.elem[k++] = num;
  L.length++;
}

使用指针

void InputList(SqList* L, int num)
{
  L->elem[k++] = num;
  L->length++;
}

🎈输出线性表里面的数

🚕输出线性表里面的数

使用引用

void OutputList(SqList& L)
{
  for (int i = 1; i <= L->length; i++)
  {
    cout << L.elem[i] << " ";
  }
}

使用指针

void OutputList(SqList *L)
{
  for (int i = 1; i <= L.length; i++)
  {
    cout << L->elem[i] << " ";
  }
}

🎈插入数字

🚕顺序存储插入数字,就是找到要插入的数字的位置,从这个位置开始的后面的所有的数字全都后移一位,这样子前面就回空出一个位置,就是要插入的数字的位置


使用引用

void ListInsert(SqList& L, int place, int num)
{
  L.length++;
  for (int i = L.length; i >= place; i--)
  {
    L.elem[i + 1] = L.elem[i];
  }
  L.elem[place] = num;
}

注意:这里是  i = L.length , 不是 i = L.length-1

你想一下,数组下标是从0开始的,所以整个表最后一个位置(L.elem[length])没有存数

这样把数组后移,到最后刚好可以空出一个位置来存需要插入的数

使用指针

void ListInsert(SqList *L, int place, int num)
{
  L->length++;
  for (int i = L->length; i >= place; i--)
  {
    L->elem[i + 1] = L->elem[i];
  }
  L->elem[place] = num;
}

🎈删除数字

🚕与插入数字类似,删除数字采用的是向前覆盖数字

使用引用

void ListDelete(SqList& L, int place)
{
  for (int i = place; i <= L.length; i++)
  {
    L.elem[i - 1] = L.elem[i];
  }
  L.length--;
}

使用指针

void ListDelete(SqList *L, int place)
{
  for (int i = place; i <= L->length; i++)
  {
    L->elem[i - 1] = L->elem[i];
  }
  L->length--;
}

🎈取出数字

🚕就是给出这个数字的位置,就是想当于给出了这个数字在数组里面的位置,然后直接根据这个位置取值

int GetElem(SqList L, int i, int& e)//int *e
{
  if ((i < 1) || (i > L.length)) return -1;
  e = L.elem[i];                  //*e=L.elem[i];
  return 1;
}
int GetElem(SqList *L, int i, int& e)
{
  if ((i < 1) || (i > L->length)) return -1;
  e = L->elem[i];
  return 1;
}

🎈置空

🚕直接L.length=0就可以了

(如果要返回线性表的长度,直接L.length即可)

😎完整代码1

#include<iostream>
using namespace std;
#define OK 1
#define overflow -1
int k=1;
typedef struct{
  int *elem;
  int length;
}SqList;
int InitList(SqList &L)
{
  L.elem=new int[100];
  if(!L.elem) exit(overflow);
  L.length=0;
  return OK;
}
//3
void InputList(SqList &L,int n)
{
  L.elem[k++]=n;
  L.length++;
}
//4
void OutputList(SqList &L)
{
  for(int i=1;i<=L.length;i++)
  {
    cout<<L.elem[i]<<" ";
  }
} 
//5
void ListInsert(SqList &L,int a,int b)
{
  L.length++;
  for(int i=L.length;i>=a;i--)
  {
    L.elem[i+1]=L.elem[i];
  }
  L.elem[a]=b;
}
//6   
 void ListDelete(SqList &L,int x)
 {
  for(int i=x;i<=L.length;i++)
  {
    L.elem[i-1]=L.elem[i];
   }  
  L.length--;
 }
 //7
 int GetElem(SqList L,int i,int &e)
 { 
  if((i<1)||(i>L.length)) return -1;
   e=L.elem[i];
   return OK;
 }
 //8
int ClearList(SqList L)
{
    L.length=0;
    return 1;
}
int main()
{
  SqList L;
  int s,e;
  InitList(L);
  if(InitList) cout<<"成功"<<endl;
  else cout<<"失败"<<endl;
  cout<<"0:退出程序"<<endl;
  cout<<"3:加入数"<<endl;
  cout<<"4:输出线性表里面的数"<<endl;
  cout<<"5:插入数"<<endl;
  cout<<"6:删除数"<<endl;
  cout<<"7:取出某个位置的数"<<endl;
  cout<<"8:置空"<<endl;
  cout<<endl;
  for(;;)
  {
    cout<<"请输入3~8之间的数,代表3~8题,0表示程序结束"<<endl;
    cin>>s;
    if(s==0) return 0;
    switch(s)
    {
    case 3:
      cout<<"请输入需要加入的数的个数:"<<endl;
      int n;
      cin>>n;
      cout<<"请输入需要加入的数:"<<endl;
      for(int i=1;i<=n;i++)
      {
        int t;
        cin>>t;
        InputList(L,t); 
      }
      break;
    case 4:
      OutputList(L);
      cout<<endl;
      break;
    case 5:
      for(int i=1;i<=2;i++)
      {
        cout<<"请输入插入的位置和插入的数是什么:"<<endl;
        int a,b;
        cin>>a>>b;
        ListInsert(L,a,b);
      }
      break;
    case 6:
      cout<<"请输入需要删除哪个位置的数:"<<endl;
      for(int i=1;i<=2;i++)
      {
        int aa;
        cin>>aa;
        ListDelete(L,aa);
      }
      break;  
    case 7:
      cout<<"请输入需要取出哪个位置的数:"<<endl;
      for(int i=1;i<=2;i++)
      {
        int a;
        cin>>a;
        GetElem(L,a,e);
        cout<<"第"<<a<<"个位置的数的值为:"<<e<<endl;
      }
      break;
    case 8:
      if (ClearList(L)) cout << "置空成功" << endl;
      else cout << "置空失败" << endl;
      break;
    }
  }
 } 

🚌小细节:什么时候使用引用,什么时候不使用引用

观察代码我们会发现,


有的是SqList L(没有使用引用)


有的是SqList &L(使用了引用)


这是为什么呢


原因:我们发现,使用引用的代码段里面都会有分配空间的操作,而没有使用引用的代码段里面就没有分配空间的操作


分配一段空间,使整个程序改变了,所以要使用引用,而如果不分配空间,那么就直接使用就行了,不需要引用


😎完整代码2  

#include<iostream>
using namespace std;
#define OK 1
#define overflow -1
int k = 1;
typedef struct {
  int* elem;
  int length;
}SqList;
int InitList(SqList* L)
{
  L->elem = new int[100];
  if (!L->elem) exit(overflow);
  L->length = 0;
  return OK;
}
//3
void InputList(SqList* L, int n)
{
  L->elem[k++] = n;
  L->length++;
}
//4
void OutputList(SqList* L)
{
  for (int i = 1; i <= L->length; i++)
  {
    cout << L->elem[i] << " ";
  }
}
//5
void ListInsert(SqList *L, int place, int num)
{
  L->length++;
  for (int i = L->length; i >= place; i--)
  {
    L->elem[i + 1] = L->elem[i];
  }
  L->elem[place] = num;
}
//6   
void ListDelete(SqList *L, int place)
{
  for (int i = place; i <= L->length; i++)
  {
    L->elem[i - 1] = L->elem[i];
  }
  L->length--;
}
//7
int GetElem(SqList L, int i, int& e)
{
  if ((i < 1) || (i > L.length)) return -1;
  e = L.elem[i];
  return OK;
}
//8
int ClearList(SqList *L)
{
  L->length=0;
    return 1;
}
int main()
{
  SqList L;
  int s, e;
  InitList(&L);
  if (InitList) cout << "成功" << endl;
  else cout << "失败" << endl;
  cout << "0:退出程序" << endl;
  cout << "3:加入数" << endl;
  cout << "4:输出线性表里面的数" << endl;
  cout << "5:插入数" << endl;
  cout << "6:删除数" << endl;
  cout << "7:取出某个位置的数" << endl;
  cout << "8:置空" << endl;
  cout << endl;
  for (;;)
  {
    cout << "请输入3~8之间的数,代表3~8题,0表示程序结束" << endl;
    cin >> s;
    if (s == 0) return 0;
    switch (s)
    {
    case 3:
      cout << "请输入需要加入的数的个数:" << endl;
      int n;
      cin >> n;
      cout << "请输入需要加入的数:" << endl;
      for (int i = 1; i <= n; i++)
      {
        int t;
        cin >> t;
        InputList(&L, t);
      }
      break;
    case 4:
      OutputList(&L);
      cout << endl;
      break;
    case 5:
      for (int i = 1; i <= 2; i++)
      {
        cout << "请输入插入的位置和插入的数是什么:" << endl;
        int a, b;
        cin >> a >> b;
        ListInsert(&L, a, b);
      }
      break;
    case 6:
      cout << "请输入需要删除哪个位置的数:" << endl;
      for (int i = 1; i <= 2; i++)
      {
        int aa;
        cin >> aa;
        ListDelete(&L, aa);
      }
      break;
    case 7:
      cout << "请输入需要取出哪个位置的数:" << endl;
      for (int i = 1; i <= 2; i++)
      {
        int a;
        cin >> a;
        GetElem(L, a, e);
        cout << "第" << a << "个位置的数的值为:" << e << endl;
      }
      break;
    case 8:
      if (ClearList(&L)) cout << "置空成功" << endl;
      else cout << "置空失败" << endl;
      break;
    }
  }
}
相关文章
|
22天前
|
存储 安全 数据库
除了 HashMap,还有哪些数据结构可以实现键值对存储?
【10月更文挑战第11天】 除了`HashMap`,其他常见支持键值对存储的数据结构包括:`TreeMap`(基于红黑树,键有序)、`LinkedHashMap`(保留插入顺序)、`HashTable`(线程安全)、`B-Tree`和`B+Tree`(高效存储大量数据)、`SkipList`(通过跳跃指针提高查找效率)及`UnorderedMap`(类似`HashMap`)。选择合适的数据结构需根据排序、并发、存储和查找性能等需求。
|
29天前
|
存储 Java
数据结构第二篇【关于java线性表(顺序表)的基本操作】
数据结构第二篇【关于java线性表(顺序表)的基本操作】
29 6
|
8天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
29天前
01(数据结构考研)线性表相关操作代码
01(数据结构考研)线性表相关操作代码
52 0
|
5天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
64 9
|
2天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
4天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
26 4
|
27天前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
26 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
8天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
27天前
初步认识栈和队列
初步认识栈和队列
57 10