目录
一、顺序表
// 基于静态存储方式的线性表
#include <stdio.h>
#define MAX_SIZE 10
typedef int ElemType;
typedef struct {
ElemType data[MAX_SIZE];
int length;
} SqList;
/**
静态存储方式
初始化顺序表
*/
void ListInit(SqList *L) {
L->length = 0;
for (int i = 0; i < L->length; i++) {
L->data[i] = 0;
}
}
/**
向指定位置添加元素
添加成功,返回true;否则返回false
*/
bool ListInsert(SqList *L, int i, ElemType e) {
if (i < 1 || i > L->length + 1) {
return false;
}
if (L->length >= MAX_SIZE) {
return false;
}
for (int j = L->length - 1; j >= i - 1; j--) {
L->data[j + 1] = L->data[j];
}
L->data[i - 1] = e;
L->length++;
return true;
}
/**
删除i处元素,并返回被删除的元素
*/
ElemType ListDelete(SqList *L, int i) {
if (i < 1 || i > L->length) {
printf("the index is out of length! \n");
return 0;
}
int temp = L->data[i - 1];
for (int j = i; j < L->length; j++) {
L->data[j - 1] = L->data[j];
}
L->length--;
return temp;
}
/**
返回i位置的元素
*/
ElemType LocateElem(SqList L, int i) {
if (i < 1 || i > L.length) {
return -1;
}
return L.data[i - 1];
}
/**
查找顺序表中首次匹配的元素所处的位序
*/
int LocateIndexByElem(SqList L, ElemType e) {
for (int i = 0; i < L.length; i++) {
if (L.data[i] == e) {
return i + 1;
}
}
return -1;
}
/**
返回顺序表的当前长度
*/
int GetLength(SqList L) {
return L.length;
}
/**
判断顺序表是否为空
*/
bool Empty(SqList L) {
if (GetLength(L) == 0) {
return true;
}
return false;
}
/**
销毁线性表
*/
void DestoryList(SqList *L) {
for (int i = L->length - 1; i >= 0; i--) {
L->data[i] = 0;
}
L->length = 0;
}
/**
遍历顺序表
*/
void ListTraverse(SqList L) {
for (int i = 0; i < L.length; i++) {
printf("%d ", L.data[i]);
}
printf("\n");
}
int main() {
SqList list;
ListInit(&list);
ListInsert(&list, 1, 2);
ListInsert(&list, 2, 3);
ListInsert(&list, 3, 5);
ListInsert(&list, 4, 7);
ListTraverse(list);
ListDelete(&list, 5);
printf("the first element is %d \n", LocateElem(list, 1));
printf("the last element is %d \n", LocateElem(list, list.length));
ListTraverse(list);
DestoryList(&list);
if (Empty(list) == true) {
printf("list clear out \n");
}
ListInsert(&list, 1, 6);
ListTraverse(list);
int ele = LocateElem(list, 3);
printf("%d", ele);
return 0;
}
初始化线性表后,线性表的最大长度确定,如果添加数据的长度大于线性表允许的最大长度,会引发越界异常,显然这种方式是有很大的局限。为了解决这个问题,引出动态存储方式的线性表。
// 基于动态存储方式的线性表
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
double score;
} Student;
typedef Student ElemType;
typedef struct {
ElemType *data;
int MAX_SIZE, length;
} SqList;
void ListInit(SqList *L) {
L->MAX_SIZE = 3;
L->length = 0;
L->data = (ElemType *)malloc(L->MAX_SIZE * sizeof(ElemType));
}
/**
扩容操作
*/
void expandList(SqList *L, int newLength) {
if (newLength <= L->MAX_SIZE) {
return;
}
ElemType *newData = (ElemType *)malloc(newLength * sizeof(ElemType));
if (newData == NULL) {
return;
}
for (int i = 0; i < L->length; i++) {
newData[i] = L->data[i];
}
free(L->data);
L->data = newData;
L->MAX_SIZE = newLength;
}
bool ListInsert(SqList *L, ElemType e, int i) {
if (i < 1 || i > L->length + 1) {
return false;
}
if (L->length >= L->MAX_SIZE) {
expandList(L, L->MAX_SIZE * 2); // 当前容量不足时,扩容为原来的两倍
}
for (int j = L->length; j >= i; j--) {
L->data[j] = L->data[j - 1];
}
L->data[i - 1] = e;
L->length++;
return true;
}
bool ListDelete(SqList *L, int i) {
if (i < 1 || i > L->length) {
return false;
}
ElemType e = L->data[i - 1];
for (int j = i; j < L->length; j++) {
L->data[j - 1] = L->data[j];
}
L->length--;
return true;
}
void ListDestory(SqList *L) {
if (L->data != NULL) {
free(L->data);
L->data = NULL;
}
L->MAX_SIZE = 0;
L->length = 0;
}
void ListTraverse(SqList L) {
for (int i = 0; i < L.length; i++) {
Student stu = L.data[i];
printf("id: %d,score: %.1f\n", stu.id, stu.score);
}
}
int main() {
SqList list;
ListInit(&list);
Student stu1 = {1, 90.5};
ListInsert(&list, stu1, 1);
stu1.id = 2;
stu1.score = 95;
ListInsert(&list, stu1, 1);
stu1.id = 3;
ListInsert(&list, stu1, 2);
stu1.id = 4;
stu1.score = 100;
ListInsert(&list, stu1, 4);
printf("%d\n", list.MAX_SIZE);
ListTraverse(list);
ListDelete(&list, 3);
ListTraverse(list);
ListDestory(&list);
return 0;
}
基于动态存储的线性表的扩容方式,听起来很高级。实际上原理很简单。我们设定在调用添加函数时,如果当前长度超过最大长度,那么会将线性表的存储空间翻倍。首先明确线性表的定义,它存储数组的空间是一段连续空间,因而在扩容操作时,从理论上来讲,如果当前数组的末尾地址如果存在足够大的空间,是可以继续接着分配。然而这种直接在原先的地址下继续分配的概率不大,系统一般会寻找一个新的空间来存储扩容数组。那么,我们是如何实现扩容操作的呢?基于数组的特点,我们在开辟一段新的空间后,首先将原先数组复制到新数组,然后紧接着数组序号,将新添加的元素放入此数组中。接下来分析这种方法的时间复杂度和空间复杂度,扩容函数执行最频繁的操作是为新数组的每个位置的元素赋值,因而时间复杂度T(n) = O(n)。假设新扩充的长度为n,那么新开辟的空间应为(ElemType)*(m+n),空间复杂度S(n) = O(n) 。从时间和空间复杂度来看,这种扩容方式的效率是不高的。
二、单链表
单链表是通过一组任意的存储单元来存储线性表中的数据元素,为了建立数据元素之间的线性关系,每个链表结点,除了存放元素自身的信息外,还需要存放一个指向其后继的指针。单链表的物理存储方式是一组任意的存储单元,而顺序表则是分配一段连续空间。
// 单链表
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct LNode {
ElemType data;
LNode *next;
} LNode, *LinkList;
// 头插法
LinkList HeadInsert(LinkList *L) {
// LinkList *p = &L; 形参 指向LinkList结构体变量的指针指向L的地址
// 创建头结点
int x;
// 声明新结点
LNode *s;
*L = (LinkList)malloc(sizeof(LNode));
(*L)->next = NULL;
(*L)->data = 0;
// 创建新结点
printf("please input node:\n");
scanf("%d", &x);
while (x != 1000) {
s = (LNode *) malloc(sizeof(LNode));
s->next = (*L)->next;
s->data = x;
(*L)->next = s;
printf("please input next node:\n");
scanf("%d", &x);
}
return *L;
}
// 尾插法
LinkList RailInsert(LinkList *L) {
// 关键在于记录末尾结点
int x;
*L = (LinkList)malloc(sizeof(LNode));
// 声明尾指针,当前尾指针指向链表的头结点
LNode *r = *L;
LNode *s;
printf("please input node:\n");
scanf("%d", &x);
while (x != 1000) {
s = (LNode *)malloc(sizeof(LNode));
// -> 运算符是对指针进行解引用并访问成员的操作
s->data = x;
r->next = s;
r = s;
printf("please input next node:\n");
scanf("%d", &x);
}
r->next = NULL;
return *L;
}
// 按位序查找结点
LNode *GetElem(LinkList L, int i) {
if (i < 1)
return NULL;
LNode *p = L->next;
int j = 1;
while (p != NULL && j < i) {
p = p->next;
j++;
}
return p;
}
// 按值查找结点
LNode *LocateElem(LinkList L, ElemType e) {
LNode *p = L->next;
while (p != NULL) {
if (p->data == e) {
return p;
}
p = p->next;
}
return NULL;
}
// 前插法插入结点
void InsertNode(LinkList L, int i, ElemType e) {
if (i < 1 || L->next == NULL) {
return;
}
LNode *s = (LNode *)malloc(sizeof(LNode));
s->data = e;
if (i == 1) {
s->next = L->next;
L->next = s;
return;
}
LNode *p = GetElem(L, i - 1);
s->next = p->next;
p->next = s;
}
/**
删除位序为i的元素
*/
bool DeleteNode(LinkList L, int i) {
if (i < 1) {
return false;
}
LNode *p = GetElem(L, i - 1);
if (p != NULL) {
LNode *s = p->next;
p->next = s->next;
free(s);
return true;
}
return false;
}
void TraverseList(LinkList L) {
while (L->next != NULL) {
L = L->next;
printf("%d\n", L->data);
}
}
int GetLength(LinkList L) {
LNode *p = L->next;
int length = 0;
while (L->next != NULL) {
L = L->next;
length++;
}
return length;
}
void DestoryList(LinkList *L) {
LNode *p = (*L)->next;
LNode *s;
while (p != NULL) {
s = p;
p = p->next;
free(s);
}
free(*L);
// 避免悬空指针
*L = NULL;
}
bool IsEmpty(LinkList L) {
return (L == NULL) ? true : false;
}
int main() {
LinkList L = NULL;
HeadInsert(&L);
// RailInsert(&L);
LNode *s = GetElem(L, 4);
if (s == NULL) {
printf("找不到对应序号的元素!\n");
} else {
printf("%d\n", s->data);
}
printf("------------\n");
LNode *p1 = LocateElem(L, 100);
if (p1 == NULL) {
printf("表中没有此元素!\n");
} else {
printf("查找到的元素为%d", p1->data);
}
printf("-------------\n");
InsertNode(L, 2, 1000);
TraverseList(L);
DeleteNode(L, 2);
TraverseList(L);
int length = GetLength(L);
printf("length is %d\n", length);
DestoryList(&L);
if (IsEmpty(L)) {
printf("单链表已清空!");
} else {
printf("未清空!");
}
return 0;
};
上述内容如果有错误的地方,希望大佬们可以指正。我一直在学习的路上,您的帮助使我收获更大!觉得对您有帮助的话,还请点赞支持!我也会不断更新文章!