线性表的逻辑结构和存储结构

75 阅读5分钟

线性表的逻辑结构

线性表是具有相同特性数据元素的有限序列 相同特性:把同一类事物归类,方便批量处理

有限:表中元素个数为n,n有限大,n可以为0

序列:表中元素排成一列,体现了一对的逻辑特性(每个元素有则仅有一个前驱和一个后继)

typedef struct
{
    float height;  
    float weight; 
}Human;  //创建Human结构体 

Human humans[10000];

for(int i=0;i<10000;++i)
{
	处理1(humans[i].name);
	处理2(humans[i].IDNumber);
	处理3(humans[i].height);
} 

[!note]+ AI 解释一下线性表中的相同特性,并用C语言体现并说明

线性表是一种数据结构,具有以下相同特性:

  1. 存储方式连续:线性表中的元素在内存中是连续存储的,每个元素占用相同大小的存储空间。
  2. 元素有序:线性表中的元素是按照一定的顺序排列的,每个元素都有一个确定的位置。
  3. 只有一个开始和结束位置:线性表只有一个起始位置和一个结束位置,每个元素只能通过前驱或后继关系找到其它元素。

C语言中可以通过数组来实现线性表。数组是一种连续存储的数据结构,可以根据下标随机访问其中的元素。下面是一个简单的示例代码:

#include <stdio.h>
#define MAXSIZE 100

typedef struct {
    int data[MAXSIZE];
    int length;
} List;  //创建List 结构体,其中创建了一个一维数组的分量和length的分量

int main() {
    List list;
    list.length = 0;

    // 插入元素
    for (int i = 0; i < 10; i++) {
        list.data[i] = i + 1;
        list.length++;
    }

    // 遍历输出数组元素
    for (int i = 0; i < list.length; i++) {
        printf("%d ", list.data[i]);
    }

    return 0;
}

在上面的代码中,我们定义了一个结构体List来表示线性表。其中data数组用于存储元素,length表示当前线性表中元素数量。在主函数中,我们首先创建了一个空列表,并通过循环向其中插入了10个整数。最后再通过循环遍历输出列表中所有元素。

这段代码体现了线性表连续、有序、只有一个开始和结束位置等特点。


线性表的存储结构

比较

顺序表:随机访问(随机存取) 占用连续的存储空间 链表:结点的存储空间利用率较顺序表稍低一点,支持存储空间的动态分配

顺序表更适合存取 链表更适合插入,删除

顺序存储结构

假设线性表的每个元素需占用l个存储单元, 并以所占的第一个单元的存储地址作为数据元 素的存储起始位置。则线性表中第i+ 1个数据元素的存储位置 LOC(a;+1 )和第i个数据元素的存 储位置LOC(a;)之间满足下列关系: image.png|300 image.png|300 image.png

image.png maxsize 地皮的个数 length是房子的个数 image.png

链式存储结构(单链表)

散落在内存的存储单元,线性表通过指针连列 判空条件L->next==NULL; L表示头指针Head

typedef struct LNode
{
	int data;
	struct Lnode  *next ;
}

image.png

头节点:不含数据元素的节点 开始节点:第一个含有数据元素的节点 与头节点(头指针)不同,头节点指向开始结点 结束节点:最后一个含有数据元素的节点,尾指针指向结束节点


通过C语言表现单链表节点和不相邻的节点的关系

在C语言中,可以通过结构体来表示单链表节点,如下所示:

struct ListNode {
    int val;
    struct ListNode *next;
};

其中,val表示节点的值,next表示下一个节点的指针。

若要表示不相邻的节点之间的关系,则可以使用数组来存储节点,并通过一个数组来记录它们之间的关系。例如:

#define MAX_NODES 100

struct GraphNode {
    int val;
    int neighbors[MAX_NODES];
};

struct GraphNode nodes[MAX_NODES];

其中,GraphNode表示图中的一个节点,val表示节点的值,neighbors是一个数组,存储该节点与其他所有节点之间的关系。

当需要连接两个不相邻的节点时,可以通过修改它们之间的neighbors数组来实现。例如:

void connect(struct GraphNode *node1, struct GraphNode *node2) {
    node1->neighbors[node2->val] = 1;
    node2->neighbors[node1->val] = 1;
}

其中,node1和node2分别是需要连接的两个节点。假设它们分别为i和j,则将node1->neighbors[j]和node2->neighbors[i]都设置为1即可实现它们之间的连接。

以上就是通过C语言表现单链表节点和不相邻的节点之间关系的方法。


链式存储结构(双链表)

每两个节点之间有两个链,两个指针前一个的后继,和后一个前驱 ![[双链表.excalidraw]]

image.png

typedef struct DLNode
{
	int data;
	struct DLNode *next;
	struct DLNode *prior;
}DLNode;
DLNode *L'
L=(DLNode*)malloc(sizeof(DLNode));
A->next=B; B->next=C; C->next=D;  //后继
D->prior=C; C->prior=b; B->prior=A;  //前驱

判空条件:L->next == NULL && L->prior == NULL L表示双链表的头节指针,L->next表示头节点的后继节点指针,L->prio表示头节点的前驱节点指针


L->next == NULL && L->prior == NULL 中的L是什么意思

L是一个指向双向链表的头节点的指针,表示该双向链表为空表。其中L->next表示头节点的后继节点指针,L->prior表示头节点的前驱节点指针。因为该双向链表为空表,所以这两个指针都应该为NULL。



如何判断双链表为空

判断双链表是否为空需要判断其头指针和尾指针是否都为NULL。因为双链表中的头指针和尾指针都是在初始化时赋值为NULL,若两者都为NULL,则说明该双链表为空。

代码示例:

typedef struct DNode {
  int data;
  struct DNode *prior; // 前驱节点指针
  struct DNode *next; // 后继节点指针
} DNode, *DLinkList;

// 判断双链表是否为空
bool isEmpty(DLinkList L) {
  if (L->next == NULL && L->prior == NULL) { // 头尾指针均为NULL
    return true;
  }
  return false;
}

链式存储结构(循环链表)

单循环链表

Head->next == Head 为真时,单循环链表为空 image.png

双循环链表

Head->next==HeadHead->prior==Head时双循环链表为空 image.png 单循环链表和双循环链表不管什么情况下,都不会有空指针

不含头节点的单双循环链表

image.png

判空条件:Head==NULL为真 当head指针为空时,两种链表为空,也就时不含头节点的单双循环链表一个节点都没有的时候,两种链表为空


malloc 申请链表节点空间


22:01

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <math.h>
#define min 0.0001
#define maxSize 100 //定义两个宏常量
using namespace std;
int compare(float A[], int An, float B[], int Bn) //An代表A表的长度,Bn代表B表的长度
{
	int i = 0;
	while (i < An && i < Bn) //表示i未遍历到AB表不同的地方
	{
		if (fabs(A[i] - B[i]) < min) //这里判断条件时AB表无线接近,可以视为相等
			++i;
		else
			break;
	}
	if (i >= An && i >= Bn) //判断条件为AB是否相等
		return 0;
	else if ((i >=An && i < Bn) || A[i] < B[i])//a<b //
		return -1;
	else
		return 1;
}
int main()
{
	float A[maxSize] = { 1,2,3,4 };
	float B[maxSize] = { 1,2,3,4,3,6 };
	cout << compare(A, 4, B, 6);
	return 0;
}