数据结构之线性表
概念定义
线性表是具有相同特性元素的一个有限序列,序列中元素的个数叫做线性表的长度,用n表示,n>=0,等于0时代表线性表是一个空表
线性数据结构的4个基本特征
1、集合中有且仅有一个“第一个元素”
2、集合中有且仅有一个“最后一个元素”
3、除最后一个元素外,其他元素均有唯一的后继元素
4、除第一个元素外,其他元素均有唯一的一个前驱元素
线性表的两种存储结构
1、第一种:顺序表
表中所有元素都按照其逻辑顺序,依次存储到一块连续的存储空间中
顺序表支持“随机访问特性”,因为存储是开辟的一块存储空间时连续的,我们很容易通过类似下标的概念,访问这块存储空间的任何一块的内容
2、第二种:链表
链表结构中,每个结点,不仅仅会存储这个结点的元素值信息,还会存储与其他结点之间的逻辑关系信息,比如前驱结点指向元素的指针地址信息,后继元素指向的指针地址信息
链表不支持“随机访问特性”,如果想访问这个链表种的最后一个结点,必须从第一个结点开始访问,才能根据第一个结点的“后继”元素的地址指针,访问到第二个元素,之后重复这个过程,依次才能访问到最后一个结点元素
因为链表的这个结 构特性,数据可以分散的存储在内存中的任意的位置,只需要通后结点的“前驱”地址指针和“后继”地址指针指向这块内存地址就可以访问到;并且链表不需要一次性的分配全部将要使用的地址空间(有时你也不知道你这一次到底要使用多少的空间),那么链表结构支持存储空间的动态分配,可以做到随用随分配
链表的5种形式
单链表、双链表、循环单链表、循环双链表、静态链表
请看下面的灵魂草图8⃣️
线性表的结构体定义(基于C语言)
顺序表的结构体定义
#define maxSize 100
typedef struct
{
//顺序表数组元素
int data[maxSize];
//顺序表长度
int length;
}Sqlist
单链表的结构体定义
typedef struct LNode
{
//结点中的数据区
int data;
//后继结点的指针
struct LNode *node;
}
双链表的结构体定义
typedef struct DLNode
{
//结点中的数据区
int data;
//前驱结点指针
struct DLNode *prior;
//后继结点指针
struct DLNode *next;
}
顺序表的操作
例题:
已知一个顺序表,其中的元素递增有序排列,插入一个元素x后,使该顺序表仍然有序递增
int findElem(Sqlist L, int x)
{
for(int i=0; i<L.length; i++)
{
if(x<L.data[i])
{
return i;
}
}
//顺序表中不存在比x大的元素,则应插入到顺序表表尾
return i;
}
void insertELem(Sqlist &L, int x)
{
int p = findELem(L, x);
for(int i=L.length-1; i>=p; --i)
{
L.data[i+1] = L.data[i];
L.data[p] = x;
++(L.length);
}
}
单链表的操作
例题:
A和B是两个带有头结点的单链表,A和B中的元素都是递增有序的。设计一个算法,将A和B合并成一个按元素值非递减有序的链表C
以下代码使用的是尾插法进行插入链表的操作,类似的插入操作还有头插法
void merge(LNode *A, LNode *B, LNode *&C)
{
LNode *p = A -> next;
LNode *q = B -> next;
LNode *r;
C = A;
C -> next = NULL;
free(B);
r = C;
while(p!=NULL && q!=NULL)
{
if(p -> data <= q -> data)
{
r -> next = p;
p = p -> next;
r = r ->next;
}
else
{
r -> next = q;
q = q-> next;
r = r ->next;
}
}
r -> next = NULL;
if(p != NULL)
{
r -> next = p;
}
if(q != NULL)
{
r -> next = q;
}
}
双链表的操作
使用尾插法建立双链表
void createDlistR(DLNode *&L, int a[], int n)
{
DLNode *s, *r;
int i;
L = (DLNode*)malloc(sizeof(DLNode));
L -> prior = NULL;
L -> next = NULL;
r = L;
for(i = 0; i < n; ++i)
{
s = (DLNode*)malloc(sizeof(DLNode));
s -> data = a[i];
r -> next = s;
s -> prior = r;
r = s;
}
r -> next = NULL;
}
双链表查找结点的方法
DLNode* findNode(DLNode *C, int x)
{
DLNode *p = C -> next;
while(p != NULL)
{
if(p -> data == x)
break;
p = p -> next;
}
return p;
}
向双链表中插入结点的方法
s -> next = p -> next;
s -> prior = p;
p -> next = s;
s -> next -> prior = s;
删除双链表中结点的方法
q = p -> next;
p -> next = q -> next;
q -> next -> prior = p;
free(q);
逆置问题🌟
线性表元素逆置操作
void reverse(int a[], int firstIndex, int lastIndex, int k)
{
int temp;
for(int i = firstIndex,j = lastIndex; i < firstIndex + k && i < j; ++i,--j)
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
将数组中的元素,循环左移p个位置(0<p<n)
//整体思路:先将0~p-1位置元素逆置,再将p~n-1位置元素逆置,最后再将整个数组逆置
void(int a[], int n, int p)
{
reverse(a, 0, p-1, p);
reverse(a, p, n-1, n-p);
reverse(a, 0, n-1, n);
}