线性表
文章目录
1 简介
1.1 定义
将具有“一对一”关系的数据“线性”地存储到物理空间中,这种存储结构就称为线性存储结构(简称线性表)。线性表要求数据类型必须一致。
1.2 存储结构
顺序存储结构:将数据依次存储在连续的整块物理空间中,简称顺序表。
链式存储结构:数据分散的存储在物理空间中,通过一根线保存着它们之间的逻辑关系,简称链表。
1.3 前驱和后继
数据结构中,一组数据中的每个个体被称为数据元素(简称元素)。某一元素的左侧相邻元素称为直接前驱,位于此元素左侧的所有元素都统称为前驱元素;某一元素的右侧相邻元素称为直接后继,位于此元素右侧的所有元素都统称为后继元素。
2 顺序表
顺序表是线性表的一种,即将具有“一对一’”逻辑关系的数据按照次序连续存储到一整块物理空间上。
2.1 初始化
顺序表存储数据之前,除了要申请足够大小的物理空间之外,还要记录顺序表申请的存储容量以及顺序表的长度,也就是表中存储数据元素的个数。
2.2 插入元素
根据插入位置的不同,可分为3种情况:插入到顺序表的表头;在表的中间位置插入元素;尾随顺序表中已有元素,作为顺序表中的最后一个元素。这3种情况可用一种方法解决:将要插入位置元素及其后继元素向后移动一个位置,再将插入元素放到插入位置。
2.3 删除元素
只需找到目标元素,并将其后续所有元素整体前移 1 个位置,即可间接实现删除元素的目的。
/* 定义顺序表 */
#define SIZE 5
typedef struct Table{
int * head; //声明了一个名为head的长度不确定的数组,也叫动态数组
int length; //记录当前顺序表的长度
int size; //记录顺序表分配的存储容量
}table;
/* 初始化顺序表 */
table initTable() {
table t;
t.head = (int *)malloc(SIZE * sizeof(int));
if(!t.head) exit(0);
t.length = 0;
t.size = SIZE;
return t;
}
/* 插入元素,data为插入的元素,index为插入的位置 */
table addData(table t, int data, int index) {
if(index < 1 || index > t.length+1) return t;
if(t.length == t.size) {
t.head = (int *)realloc(t.head, (t.size+1) * sizeof(int));
if(!head) return t;
t.size += 1;
}
//将从插入位置开始的后续元素逐个后移
for(int i = t.length-1; i >= index-1; i--) {
t.head[i+1] = t.head[i];
}
t.head[index-1] = data;
t.length++;
return t;
}
/* 删除元素 */
table delData(table t, int index) {
if(index > t.length || index < 1) return t;
for(int i = index; i < t.length; i++)
t.head[i-1] = t.head[i];
t.length--;
return t;
}
/* 查找元素 */
int findData(table t, int data) {
for(int i = 0; i < t.length; i++) {
if(t.head[i] == data) return i+1;
}
//查找失败返回-1
return -1;
}
3 链表
链表是一种数据元素随机存储,并通过指针表示数据之间逻辑关系的存储结构,链表存储的数据元素,其物理存储位置是随机的。
3.1 节点
链表中每个数据的存储都由以下两部分组成:
数据域:数据元素本身。
指针域:指向直接后继元素的指针。
链表中的节点又细分为头节点、首元节点和其他节点:
头节点:其实就是一个不存任何数据的空节点,通常作为链表的第一个节点。
首元节点:链表中称第一个存有数据的节点为首元节点。
3.2 初始化
3.3 插入元素
向链表中增添元素,根据添加位置不同,可分为以下 3 种情况:
- 插入到链表的头部(头节点之后),作为首元节点。
- 插入到链表中间的某个位置。
- 插入到链表的最末端,作为链表中最后一个数据元素。
链表插入元素的思想是固定的,只需做以下两步操作:
-
将新结点的 next 指针指向插入位置后的结点。
-
将插入位置前结点的 next 指针指向插入结点。
typedef struct Node {
int data;
struct Node *next;
}node, *pNode;
node *initList()
{
pNode list = (pNode)malloc(sizeof(node));
if(!list)
{
printf("malloc error!\n");
return NULL;
}
list->next = NULL;
return list;
}
int IsExist(pNode list)
{
return list != NULL;
}
int IsEmpty(pNode list)
{
return list->next == NULL;
}
//根据数据查找结点
Node *findByData(int data, pNode list)
{
pNode cur;
cur = list->next;
while(cur != NULL && cur->data != data)
cur = cur->next;
return cur;
}
//根据索引查找节点
Node *findByIndex(pNode list, int index)
{
int i = 1;
pNode cur = list->next;
if(index <= 0 || index > getNodeNum(list)) return NULL;
while(cur)
{
if(i == index) return cur;
i++;
cur = cur->next;
}
return NULL;
}
int InsertHead(pNode list, int data)
{
if(!list)
{
printf("list not exist\n");
return -1;
}
pNode tmp = (pNode)malloc(sizeof(node));
if(!tmp)
{
printf("malloc error\n");
return -1;
}
tmp->data = data;
tmp->next = list->next;
list->next = tmp;
return 0;
}
int InsertTail(pNode list, int data)
{
if(!list) return -1;
pNode tmp = (pNode)malloc(sizeof(node));
if(!tmp) return -1;
tmp->data = data;
tmp->next = NULL;
pNode tail = list;
while(tail->next != NULL)
tail = tail->next;
tail->next = tmp;
return 0;
}
int showList(pNode list)
{
if(!head) return -1;
if(!head-next) return -1;
pNode cur = head->next;
while(cur)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
return 0;
}
int getNodeNum(pNode list)
{
...
for(cur = head->next; !cur; cur = cur->next, count++)
;
return count;
}
4 静态链表
静态链表,也是线性存储结构的一种,它兼顾了顺序表和链表的优点于一身,既能快速访问元素,又能快速增加或删除数据元素。
使用静态链表存储数据,数据全部存储在数组中(和顺序表一样),但存储位置是随机的,数据之间"一对一"的逻辑关系通过一个整形变量(称为"游标",和指针功能类似)维持(和链表类似)。
4.1 节点
静态链表存储数据元素至少需要包含以下2部分信息:
- 数据域:用于存储数据元素的值。
- 游标:其实就是数组下标,表示直接后继元素所在数组中的位置。
4.2 备用链表
备用链表:一条连接各个空闲位置的链表,作用是回收数组中未使用或之前使用过(目前未使用)的存储空间,留待后期使用。通常,备用链表的表头位于数组下标为 0(a[0]) 的位置,而数据链表的表头位于数组下标为 1(a[1])的位置。
备用链表摘除节点最简单的方法是摘除 a[0] 的直接后继节点,向备用链表中添加空闲节点也是添加作为 a[0] 新的直接后继节点。无需遍历备用链表,耗费的时间复杂度为 O(1)。
#define MAXSIZE 6
typedef struct {
int data;
int cur;
}component;
/* 创建备用链表 */
void reserveArr(component *array){
for(int i = 0; i < MAXSIZE; i++)
array[i].cur = i + 1;
array[MAXSIZE-1].cur = 0;
}
/* 提取分配空间 */
int mallocArr(component *array){
int i = array[0].cur;
if(array[0].cur)
array[0].cur = array[i].cur;
return i;
}
/* 初始化静态链表 */
int initArr(component *array){
reserveArr(array);
int body = mallocArr(array);
}
5 循环链表
循环链表:把链表的两头连接,使其成为了一个环状链表,只需要将表中最后一个结点的指针指向头结点。
6 双向链表
6.1 定义
双向,指的是各节点之间的逻辑关系是双向的,但通常头指针只设置一个。双向链表中各节点包含以下 3 部分信息:
-
指针域(prior):用于指向当前节点的直接前驱节点。
-
数据域(data):用于存储数据元素。
-
指针域(next):用于指向当前节点的直接后继节点。
6.2 添加节点
添加至表头:
假设新元素节点为 temp,表头节点为 head,则需要做以下 2 步操作即可:
- temp->next=head; head->prior=temp。
- 将 head 移至 temp,重新指向新的表头。
添加至表中:
-
新节点先与其直接后继节点建立双层逻辑关系。
-
新节点的直接前驱节点与之建立双层逻辑关系。
添加至表尾:
-
找到双链表中最后一个节点;
-
让新节点与最后一个节点进行双层逻辑关系;
6.3 删除节点
6.4 查找节点
typedef struct node{
struct node *prior;
int data;
struct node *next;
}node;
/* 初始化双向链表 */
node *initList(node *head){
head = (node *)malloc(sizeof(node));
head->prior = NULL;
head->next = NULL;
head->data = 1;
}
/* 添加节点 */
node *insertNode(node *head, int data, int index){
node *tmp = (node *)malloc(sizeof(node));
tmp->data = data;
tmp->prior = NULL;
tmp->next = NULL;
//插入位置为链表头
if(index = 1){
tmp->next = head;
head->prior = tmp;
head = tmp;
}else{
node *body = head;
for(int i = 1; i < index - 1; i++)
body = body->next;
//条件为真说明插入位置为链表尾
if(!body->next){
body->next = tmp;
tmp->prior = body;
}else{
body->next->prior = tmp;
tmp->next = body->next;
body->next = tmp;
tmp->prior = body;
}
}
return head;
}
/* 删除节点 */
node *delNode(node *head, int data){
node *tmp = head;
while(tmp){
if(tmp->data == data){
tmp->prior->next = tmp->next;
tmp->next->prior = tmp->prior;
free(temp);
return head;
}
tmp = tmp->next;
}
printf("链表中不存在该元素\n");
return head;
}
/* 查找节点对应的索引 */
int findNode(node *head, int data){
int index = 1;
node *cur = head;
while(cur){
if(cur->data == data)
return index;
index++;
cur = cur->next;
}
return -1;
}
free(temp);
return head;
}
tmp = tmp->next;
}
printf("链表中不存在该元素\n");
return head;
}
/* 查找节点对应的索引 */
int findNode(node *head, int data){
int index = 1;
node *cur = head;
while(cur){
if(cur->data == data)
return index;
index++;
cur = cur->next;
}
return -1;
}