顺序表整理和单项链表01 day20
二:各个主要函数
一:CreatSeqList
SeqList *CreateSeqList(int len);
-------------------------------------------------------------/*** @brief Create a Seq List object 创建一个顺序表** @param n 是顺序表的大小* @return SeqList* 指向顺序表的指针 (重要) ,错误的话返回NULL*/
SeqList *CreateSeqList(int n)
{SeqList *sl = malloc(sizeof(SeqList));if (sl == NULL){fprintf(stderr,"CreateSeqList malloc error\n"); // strerr 专门输出错误信息的return NULL;}sl->head = malloc(sizeof(DATATYPE) * n);if (NULL == sl->head){fprintf(stderr,"CreateSeqList malloc2 error\n"); // strerr 专门输出错误信息的return NULL;}sl->tlen = n;sl->clen = 0;return sl;
}
作用:创建一个顺序表,最多可存储 len
个元素。
返回值:创建好的顺序表指针。
二:DestroySeqList
`int DestroySeqList(SeqList *list);`
作用:销毁顺序表,释放所有内存
三:ShowSeqList
int ShowSeqList(SeqList *list);
作用:遍历顺序表,将所有元素打印出来
四:InsertTailSeqList
int InsertTailSeqList(SeqList *list, DATATYPE data);
作用:将一个元素插入到顺序表末尾。
五:IsFullSeqList
int IsFullSeqList(SeqList *list);
作用:判断顺序表是否已满
返回值:
1
表示已满。0
表示未满。
六:IsEmptySeqList
int IsEmptySeqList(SeqList *list);
--------------------------------------------------------
int IsEmptySeqList(SeqList* sl)
{return 0 == sl->clen;
}
作用:判断顺序表是否为空。
返回值:
1
表示为空。0
表示非空。
七:InsertPosSeqList
int InsertPosSeqList(SeqList *list, DATATYPE data, int pos);
作用:将一个元素插入到指定位置 pos
。
关键点:
- 判断是否满。
- 判断位置是否有效。
- 从尾部开始向后移动,腾出空间。
- 插入元素到
pos
。 len++
。
八:FindSeqList
int FindSeqList(SeqList *list, char *name);
---------------------------------------------
作用:在顺序表中查找名字为 name
的元素
返回值:
- 找到则返回该元素的索引(下标)。
- 未找到返回
-1
。
九:ModifySeqList
int ModifySeqList(SeqList *list, char *old, DATATYPE new);
作用:将名字为 old
的元素替换为 new
。
流程:
- 调用
FindSeqList
查找旧元素。 - 替换内容。
十:DeleteSeqList
int DeleteSeqList(SeqList *list, char *name);
作用:删除名字为 name
的元素。
流程:
- 找到该元素的下标。
- 用后面的元素依次向前覆盖。
len--
。
十一:ClearSeqList
int ClearSeqList(SeqList *list);
作用:清空顺序表中所有元素,但不释放内存。
等效于:list->len = 0;
三:线性表(升级版的数组)
一:基础定义
1.线性表:零个或多个数据元素的有限序列
线性表元素的个数n(n≥0)定义为线性表的长度,当n=0时,称为空表
线性表的数据对象集合为{a1,a2.....an)
,每个元素的类型均为DataType
。其中,除第一个元素a外,每一个元素有且只有一个直接前驱元素,除了最后一个元素a,外,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系
元素之间是有顺序了。如果存在多个元素,第一个元素无前驱,最有一个没有后继,其他的元素只有一个前驱和一个后继。
当线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,为空表。在非空的表中每个元素都有一个确定的位置,如果a1是第一个元素,那么an就是第n个元素。
线性表顺序存储的优点,缺点
优点
1,无需为表中的逻辑关系增加额外的存储空间
2,可以快速随机访问元素O(1)
缺点
1,插入,删除元素需要移动元素o(n)
2,无法动态存储。
二:线性表的顺序存储结构
1.为了建立一个线性表,要在内存中找一块地,于是这块地的第一个位置就非常关键,它是存储空间的起始位置。
2.顺序存储结构需要三个属性:存储空间的起始位置:数组data
,它的存储位置就是存储空间的存储位置。
线性表的最大存储容量:数组长度MaxSize
。
线性表的当前长度:length。
三:memcpy()
void *memcpy(void *str1, const void *str2, size_t n)
参数:
- str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- str2 – 指向要复制的数据源,类型强制转换为 void* 指针。
- n – 要被复制的字节数。
返回值:该函数返回一个指向目标存储区 str1 的指针。
memcpy(&sl->head[sl->clen], data, sizeof(DATATYPE));
//练习InsertPosSeqList
//由于顺序表的原因,当旧数据和clen一样大时,无法插入新数据
//当旧数据比clen小至少2个数据时,新数据不能直接插入最后一位的位置
//当新数据插入旧数据时,且当旧数据比clen小至少1个数据时,
//新数据前的旧数据不变位置,新数据后的旧数据由于新数据的插入,向后顺延一个数据的位置
四::顺序表(Seqlist
)
1.组成
Head
组头:Head[0]—Head[1]--------------Head[n]·
Tlen
:总长度
Clen
:当前长度
DATATYPE * Head
理解:就是head,tlen,clne
三个组成结构体,其中clen
是我所设的数组a的长度大小(a[clen] = {...,.,.}
),其中tlen
就是所设数组a[clen] = {...,.,.}
中存放的有效元素个数,所以head
这个一位数组(指针变量)可以指向数组a[clen] = {...,.,.}
中存放的有效元素。clen
限制所设数组长度,tlen
存放有效元素个数,head
访问有效元素,head
的长度应该<=clen
。
SeqList *CreateSeqList(int len);
int DestroySeqList(SeqList *list);//销毁
int ShowSeqList(SeqList *list);//展示
int InsertTailSeqList(SeqList *list, DATATYPE data);//尾插
int IsFullSeqList(SeqList *list);//判断是否满
int IsEmptySeqList(SeqList *list);//判断是否空
int InsertPosSeqList(SeqList *list, DATATYPE data, int pos);//按位置插
int FindSeqList(SeqList *list, char *name);//查找
int ModifySeqList(SeqList *list, char *old, DATATYPE new);//修改
int DeleteSeqList(SeqList *list, char *name);//删除
int ClearSeqList(SeqList *list);//清空表
//.h文件
#ifndef _SEQLIST_H_
#define _SEQLIST_H_typedef struct
{char name[32];char sex;int age;int score;}PER;typedef PER DATATYPE;
typedef struct
{DATATYPE * head; // 数组的数组名 存储datatype 类型的数据int tlen; // 数组的容量int clen; // 数组中有效元素的个数
}SeqList;// 顺序表 表头结构SeqList* CreateSeqList(int n);
int InsertTailSeqList(SeqList* sl,DATATYPE*data);
int ShowSeqList(SeqList* sl);
int IsFullSeqList(SeqList* sl);
int GetSizeSeqList(SeqList* sl);
int InsertPosSeqList(SeqList *sl, DATATYPE *data, int pos);#endif
----------------------------------------------------------------------------------------------------------//.c文件
SeqList *CreateSeqList(int n)
{SeqList *sl = malloc(sizeof(SeqList));//malloc 是动态内存分配函数,用于在堆(Heap)上申请一块连续的内存空间sizeof(SeqList) 计算 SeqList 结构体的大小(字节数),确保分配的内存足够存储整个结构体。//SeqList *sl:定义一个指向 SeqList 结构体的指针 sl。将 malloc 返回的内存地址赋值给 sl,此时 sl 指向新分配的内存空间。//直接定义 SeqList sl 会创建在栈(Stack)上,但顺序表通常需要动态调整大小(如扩容),因此需通过堆内存动态分配。if (NULL == sl){fprintf(stderr,"CreateSeqList malloc error\n"); // strerr 专门输出错误信息的return NULL;}sl->head = malloc(sizeof(DATATYPE) * n);if (NULL == sl->head){fprintf(stderr,"CreateSeqList malloc2 error\n"); // strerr 专门输出错误信息的return NULL;}sl->tlen = n;sl->clen = 0;return sl;
}
/*** @brief 顺序表的尾插** @param sl 被插入的 顺序表* @param data 被存储的数据的指针* @return int 0表示成功 1 表示失败*/
int InsertTailSeqList(SeqList *sl, DATATYPE *data)
{if (IsFullSeqList(sl)){return 1;}// strcpymemcpy(&sl->head[sl->clen], data, sizeof(DATATYPE));sl->clen++;return 0;
}int InsertPosSeqList(SeqList *sl, DATATYPE *data, int pos)
{if (sl == NULL || data == NULL){fprintf(stderr, "Error: NULL pointer!\n");return -1; // 返回错误码}if (sl->clen >= sl->tlen){fprintf(stderr, "Error: SeqList is full!\n");return -1;}if (pos > sl->clen || pos < 0){fprintf(stderr,"CreateSeqList malloc2 error\n"); // strerr 专门输出错误信息的}if (pos < sl->clen){for (int i = sl->clen; i > pos; i--){memcpy(&sl->head[i], &sl->head[i - 1], sizeof(DATATYPE));}}memcpy(&sl->head[pos], data, sizeof(DATATYPE));sl->clen++;return 0;
}
/*** @brief 遍历整个顺序表** @param sl 被遍历的顺序表的指针* @return int 0表示成功 1 表示失败*/
int ShowSeqList(SeqList *sl)
{int size = GetSizeSeqList(sl);int i = 0;for (i = 0; i < size; i++){printf("name:%s sex:%c age:%d score:%d\n", sl->head[i].name,sl->head[i].sex, sl->head[i].age, sl->head[i].score);}return 0;
}int IsFullSeqList(SeqList *sl) { return sl->clen == sl->tlen; }
int GetSizeSeqList(SeqList *sl) { return sl->clen; }
4-1:完整seqlist
练习
//.h文件#ifndef _SEQLIST_H_
#define _SEQLIST_H_typedef struct
{char name[32];char sex;int age;int score;}PER;typedef PER DATATYPE;
typedef struct
{DATATYPE * head; // 数组的数组名 存储datatype 类型的数据int tlen; // 数组的容量int clen; // 数组中有效元素的个数
}SeqList;// 顺序表 表头结构SeqList* CreateSeqList(int n);
int InsertTailSeqList(SeqList* sl,DATATYPE*data);
int ShowSeqList(SeqList* sl);
int IsFullSeqList(SeqList* sl);
int GetSizeSeqList(SeqList* sl);
int InsertPosSeqList(SeqList* sl,DATATYPE*data,int pos);
int IsEmptySeqList(SeqList* sl);
int FindSeqListByName(SeqList *ls, char *name);
DATATYPE* GetItemSeqList(SeqList* sl,int pos);
int ClearSeqList(SeqList *sl);
int ModifySeqList(SeqList *sl, char *old, DATATYPE*data);
int DeleteSeqList(SeqList *sl, char *name);
int DestroySeqList(SeqList *sl);
#endif
-----------------------------------------------------------------
//.c文件
#include "seqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*** @brief Create a Seq List object 创建一个顺序表** @param n 是顺序表的大小* @return SeqList* 指向顺序表的指针 (重要) ,错误的话返回NULL*/
SeqList *CreateSeqList(int n)
{SeqList *sl = malloc(sizeof(SeqList));if (NULL == sl){fprintf(stderr,"CreateSeqList malloc error\n"); // strerr 专门输出错误信息的return NULL;}sl->head = malloc(sizeof(DATATYPE) * n);if (NULL == sl->head){fprintf(stderr,"CreateSeqList malloc2 error\n"); // strerr 专门输出错误信息的return NULL;}sl->tlen = n;sl->clen = 0;return sl;
}
/*** @brief 顺序表的尾插** @param sl 被插入的 顺序表* @param data 被存储的数据的指针* @return int 0表示成功 1 表示失败*/
int InsertTailSeqList(SeqList *sl, DATATYPE *data)
{if (IsFullSeqList(sl)){return 1;}// strcpymemcpy(&sl->head[sl->clen], data, sizeof(DATATYPE));sl->clen++;return 0;
}
/*** @brief 遍历整个顺序表** @param sl 被遍历的顺序表的指针* @return int 0表示成功 1 表示失败*/
int ShowSeqList(SeqList *sl)
{int size = GetSizeSeqList(sl);int i = 0;for (i = 0; i < size; i++){printf("name:%s sex:%c age:%d score:%d\n", sl->head[i].name,sl->head[i].sex, sl->head[i].age, sl->head[i].score);}return 0;
}int IsFullSeqList(SeqList *sl) { return sl->clen == sl->tlen; }
int GetSizeSeqList(SeqList *sl) { return sl->clen; }int InsertPosSeqList(SeqList *sl, DATATYPE *data, int pos)
{int size = GetSizeSeqList(sl);if (pos < 0 || pos > size){fprintf(stderr,"CreateSeqList malloc2 error\n"); // strerr 专门输出错误信息的return 1;}if (IsFullSeqList(sl)){printf("seqlist full\n");return 1;}int i = 0;for (i = sl->clen; i > pos; i--){memcpy(&sl->head[i], &sl->head[i-1], sizeof(DATATYPE));}memcpy(&sl->head[pos], data, sizeof(DATATYPE));sl->clen++;return 0;
}
int IsEmptySeqList(SeqList* sl)
{return 0 == sl->clen;//逻辑表达式,如果clen=0,返回1
}
/*** @brief 在顺序表中按照名字查找信息* * @param ls * @param name * @return int -1没找到*/
int FindSeqListByName(SeqList *sl, char *name)
{if(IsEmptySeqList(sl))//1执行{return -1;}int size = GetSizeSeqList(sl);int i = 0;for(i=0;i<size;i++){if(0 == strcmp(sl->head[i].name, name)){return i;}}return -1;
}
DATATYPE* GetItemSeqList(SeqList* sl,int pos)
{int size = GetSizeSeqList(sl);if(pos<0 || pos > size-1){return NULL;}return &sl->head[pos];
}
int ClearSeqList(SeqList *sl)
{sl->clen = 0; //本质上就是要新的数据直接覆盖到旧数据的位置上,clen=0意味着旧数据无法写入return 0;
}
int ModifySeqList(SeqList *sl, char *old, DATATYPE*data)
{int ret = FindSeqListByName(sl, old);if(-1 == ret){return 1;}memcpy(&sl->head[ret], data, sizeof(DATATYPE));return 0;
}
int DeleteSeqList(SeqList *sl, char *name)
{int ret = FindSeqListByName(sl, name);if(-1 == ret){return -1;}int i;int size = GetSizeSeqList(sl);for(i=ret;i<size;i++)//循环前移{memcpy(&sl->head[i], &sl->head[i+1], sizeof(DATATYPE));}sl->clen--;return 0;
}
int DestroySeqList(SeqList *sl)
{free(sl->head);free(sl);return 0;
}
五:makefile
1.规范名字 makefile/Makefile
//一般用第二个
a.out:main.c seqlist.c//目标:依赖gcc main.c seqlist.c -o a.out//规则clean:rm a.out--------------------------------------------------------
编写完make再make clean
//形式1
#目标:依赖
#\t规则a.out:main.c seqlist.cgcc main.c seqlist.c -o a.out clean:rm a.out
//形式2
a.out:main.c seqlist.cgcc $^ -o $@//$@ 目标:上一条语句// $^ 依赖:只在这条规则生效
//形式3
SRC = main.c
SRC += seqlist.c//在上一套SRC追加'+='
DST = all
$(DST) : $(SRC)gcc $^ -o $@
clean:rm $(DST)
//形式4 适用于大型文件
SRC = main.o
SRC += seqlist.o
DST = all%.o:%.c //%代表文件名()通配符,是一个变量,对应的.c文件 生成对应的.o文件gcc -c $^ -o $@
$(DST) : $(SRC)gcc $^ -o $@
clean:rm $(DST) $(SRC)//也再删.o文件
六:线性表的链式存储
- 线性表链式存储结构的特点是一组任意的存储单位存储线性表的数据元素,存储单元可以是连续的,也可以不连续。可以被存储在任意内存未被占用的位置上。
所以前面的顺序表只需要存储数据元素信息就可以了。在链式结构中还需要一个元素存储下一个元素的地址。
为了表示每个数据元素,ai
与其直接后继数据元素ai+1
之间的逻辑关系,对ai
来说,除了存储其本身的信息外,还需要存一个指示器直接后续的信息。把存储元素信息的域叫数据域,把存储直接后继位置的域叫指针域。这两部分信息组成数据元素ai
的存储映像,叫结点(Node
);
typedef struct
{char name[32];char sex;int age;int score;
}DATATYPE;
------------------------------------------------------------------------------------typedef struct node//必须有名字,不然无法表示引用自己
{DATATYPE data;struct node *next;
}LinkNode;typedef struct
{LinkNode *head;int clen;
}LinkList;
int InsertHeadLinkList(LinkList *list, DATATYPE *data)
{//新结点的初始化LinkNode *newnode = malloc(sizeof(LinkNode));if (NULL == newnode){fprintf(stderr, "InsertHeadLinkList malloc\n");return 1;}memcpy(&newnode->data, data, sizeof(DATATYPE));newnode->next = NULL;// 把新节点连接到链表中if (IsEmptyLinkList(list)) // empty{list->head = newnode;}else{newnode->next = list->head;list->head = newnode;}list->clen++;return 0;
}