线性表的学习笔记分享
目录
一、线性表的基本概念
线性表是一种常见的数据结构,它由一组有序的元素组成,每个元素最多只有一个前驱和一个后继。线性表可以分为两种类型:顺序表和链表。
二、顺序表的定义
顺序表是一种用数组实现的线性表,它的元素在内存中连续存储。顺序表的特点是:
- 可以通过下标直接访问元素;
- 随机访问速度快;
- 插入和删除操作需要移动元素,效率较低。
顺序表的实现代码如下:
#define maxsize 100 //线性表可能达到的最大长度
typedef struct {
ElemType elem[maxsize]; //线性表占用的数组空间
int last; //记录线性表中最后一个元素在数组elem[ ]中的位置(下标值),空表置为 -1
} SeqList;
三、顺序存储结构的基本运算
3.1 按内容查找运算
- 查找线性表 L 中第 i个数据元素
算法思路
- 初始化计数器i为0,用于记录当前扫描的位置。
- 进入while循环,条件是i小于等于L.last且L.elem[i]不等于e。
- 在循环中,逐个扫描顺序表中的元素,直到找到值为e的元素或者扫描到表尾。
- 如果找到值为e的元素,则返回其序号i + 1(序号比下标多1)。
- 如果未找到值为e的元素,则返回-1,表示未找到。
int Locate (SeqList L, ElemTypee) {
int i = 0 ; /*i不仅是下标,也作为计数器,初值为0*/
while ( (i <= L.last) && (L.elem[i] != e ) ) i++;
/*顺序扫描表,直到找到值为e的元素,或扫描到表尾也没找到*/
if (i <= L.last)
return (i + 1); /*找到值为 e 的元素,则返回其序号
(序号比下标多1)*/
else
return (-1); /*没找到,返回空序号*/
}
3.2 插入运算
- 在线性表中第i个位置插入元素e
算法思路
- 首先判断插入位置i是否合法,即i的取值范围应该是 1 到 L->last + 2 之间,如果不合法则输出错误信息并返回ERROR。
- 然后判断顺序表是否已满,如果已满则输出错误信息并返回错误代码。
- 如果插入位置合法且顺序表未满,则从后向前遍历顺序表,将位置大于等于i的元素依次向后移动一位,为插入元素腾出空间。
- 将元素e插入到位置i处(注意在C语言中数组的下标是从0开始的,所以实际上是在下标为i-1的位置插入元素)。
- 最后更新顺序表的长度last,插入成功返回OK。
int InsList (SeqList *L, int i, ElemType e) {
int k;
if ( (i < 1) || (i > L->last + 2) ) { /*首先判断插入位置是否合法*/
printf("插入位置i值不合法");
return (ERROR);
}
if (L->last >= maxsize - 1) {
printf("表已满无法插入");
return (ERROR);
}
for (k = L->last; k >= i - 1; k--) /*为插入元素而移动位置*/
L->elem[k + 1] = L->elem[k];
L->elem[i - 1] = e; /*在C语言中数组第i个元素的下标为i-1*/
L->last++;
return (OK);
}
3.3 删除运算
- 将线性表中第i个元素删除
算法思路
- 首先判断删除位置i是否合法,即i的取值范围应该是 1 到 L->last + 1 之间,如果不合法则输出错误信息并返回ERROR。
- 将被删除的元素保存到变量e所指向的内存空间中,以便调用函数后获取被删除的元素。
- 从位置i开始,将后面的元素依次向前移动一位,覆盖掉被删除的元素。
- 更新顺序表的长度last,使其减少1。
- 删除成功返回OK。
int DelList(SeqList *L, int i, ElemType *e) {
int k;
if ((i < 1) || (i > L->last + 1)) {
printf("删除位置不合法!");
return (ERROR);
}
*e = L->elem[i - 1]; /* 将删除的元素存放到e所指向的变量中*/
for (k = i; k <= L->last; k++)
L->elem[k - 1] = L->elem[k]; /*将后面的元素依次前移*/
L->last--;
return (OK);
}
四、链表的定义
链表是一种用链式结构实现的线性表,它的元素在内存中不连续存储。链表的特点是:
- 插入和删除操作不需要移动元素,效率较高;
- 不可以通过下标直接访问元素,需要通过指针访问。
链表的实现代码如下:
typedef struct Node // 结点类型定义
{
//ElemType 具体类型据实际设定, 如int,char等
ElemType data;
struct Node * next;
}Node,
*LinkList; //LinkList为结构指针类型
五、单链表上的基本运算
5.1 初始化单链表
- 方法一
void InitList(LinkList *L) { *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; } - 方法二
LinkList InitList( ) { LinkList L = (LinkList)malloc(sizeof(Node)); L->next = NULL; return L; }
5.2 建立单链表
- 头插法
算法思路
- 初始化指针变量s和整型变量flag,并将flag赋初值为1。
- 通过循环逐个接收输入字符,如果输入的字符不是'$',则创建一个新节点s,并将输入的字符赋给s的数据域。
- 将新节点s插入到链表L的头部,即使s的next指针指向链表原来的第一个节点,然后更新链表的头指针L指向s。
- 如果输入的字符是'$',则将flag置为0,表示建表结束。
- 完成字符输入和链表构建过程。
void CreateFromHead(LinkList L) {
Node *s;
int flag = 1; //标志--初值为1,当输入‘$’时,flag为0,建表结束
while (flag) {
char c = getchar(); //接收一字符
if (c != '$') { //如果字符不是‘$’,则执行头插
s = (Node *)malloc(sizeof(Node));
s->data = c;
s->next = L->next;
L->next = s;
} else {
flag = 0;
}
}
}
-
尾插法
算法思路
- 初始化指针变量r和s,以及整型变量flag并赋初值为1。
- 通过循环逐个接收输入字符,如果输入的字符不是'$',则创建一个新节点s,并将输入的字符赋给s的数据域。
- 将新节点s插入到链表L的尾部,并更新r指向新的尾节点s。
- 如果输入的字符是'$',则将flag置为0,表示建表结束,并将尾节点的next指针置为空。
- 完成字符输入和链表构建过程。
void CreateFromTail(LinkList L) {
Node *r, *s;
int flag = 1; //标志--初值为1,当输入“$”时,flag为0,建表结束
r = L;
while (flag) {
c = getchar(); //接收一字符
if (c != '$') //如果字符不是’$’,则执行尾插
s = (Node *)malloc(sizeof(Node));
s->data = c;
r->next = s;
r = s;
else {
flag = 0;
r->next = NULL;
}
}
}
5.3 单链表插入操作
- 在带头结点的单链表L中第i个位置前插入一个数据元素
算法思路
- 初始化指针变量pre和s,整型变量k。
- 判断插入位置i是否合理,如果不合理则返回错误。
- 通过循环找到第i-1个节点,即pre指向第i-1个节点。
- 判断是否找到了第i-1个节点,如果没有找到,则打印错误信息并返回错误。
- 为新节点s申请内存空间,并将值e赋给s的数据域。
- 将s插入到pre指向节点的后面,完成插入操作。
- 返回OK表示插入成功。
int InsList(LinkList L, int i, ElemType e) {
/*在带头结点的单链表L中第i个结点之前插入值为e的新结点。 */
Node *pre, *s;
int k;
if (i < 1) return Error;
pre = L;
k = 0;
while (pre != NULL && k < i - 1) {
pre = pre->next;
k = k + 1;
}
if (!pre) {
printf("插入位置不合理!");
return Error;
}
s = (Node*)malloc(sizeof(Node)); //为e申请一个新的结点
s->data = e; /*将待插入结点的值e赋给s的数据域*/
s->next = pre->next;
pre->next = s;
return OK;
}
5.4 单链表删除操作
- 删除单链表中第i个元素,并返回被删除的元素e
算法思路
- 初始化指针变量pre和r,整型变量k以及存储删除节点值的指针e。
- 通过循环找到第i-1个节点,即pre指向第i-1个节点。
- 判断是否找到了第i-1个节点,如果没有找到,则打印错误信息并返回ERROR。
- 找到第i-1个节点后,将r指向要删除的第i个节点,pre的next指针跳过r将其指向r的下一个节点,实现删除操作。
- 将r节点的数据存储到指针e指向的变量中。
- 释放r节点的内存空间。
- 返回OK表示删除成功。
int DelList(LinkList L, int i, ElemType *e) {
Node *pre,
*r;
int k;
pre = L;
k = 0;
while (pre->next != NULL && k < i - 1) {
pre = pre->next;
k = k + 1;
}
if ( !(pre->next) ) {
printf("删除结点的位置 i 不合理!");
return ERROR;
}
r = pre->next;
pre->next = pre->next->next /* 删除结点 r */
*e = r->data;
free(r);
return OK;
}
5.5 单链表查找
- 按序号查找:在带头结点的单链表L中查找第i个结点,若找到(1≤i≤n), 则返回该结点的存储位置; 否则返回NULL
算法思路
- 设带头结点的单链表的长度为 n,要查找表中第 i 个结点
- 从单链表的头指针 L 出发,从头结点 (L) 开始 顺着链域扫描,需要移动 i 次
- 用 j 做记数器,累计当前扫描过的结点数(初值 为0),当 j = i 时,指针 p 所指的结点就是要找 的第 i 个结点 。
Node * GetData(LinkList L, int i) {
int j;
Node *p;
if (i <= 0)
return NULL;
p = L;
j = 0;
/ * 从头结点开始扫描 * /
while ( (p->next != NULL) && (j < i) ) {
p = p->next;
j++;
}
if (i = = j)return p;
/* 找到了第i个结点 */
else return NULL;
/* 找不到,i > n */
}
- 按值查找 :在带头结点的单链表L中查找其结点值等于key的结点, 若找到则返回该结点的位置p,否则返回NULL
算法思路
- 在单链表中查找值等于 key 的结点
- 从单链表的头指针指向的头结点出发,顺链逐个 将结点值和给定值 key 作比较,返回查找结果
Node *Locate( LinkList L, ElemType key) {
Node *p;
p = L->next;
/ * 从表中第一个结点比较 * /
while (p != NULL)
if (p->data != key)
p = p->next;
else
break;
/ * 找到结点key,退出循环 * /
return p;
}
5.6 求单链表的长度
算法思路
- 遍历单链表,从头结点的下一个节点开始,依次遍历每个节点。
- 使用一个变量 j 来存放单链表的长度,初始值为 0。
- 当当前节点不为 NULL 时,将指针 p 指向下一个节点,然后 j 自增 1,表示单链表的长度加一。
- 直到遍历到最后一个节点,此时 p 为 NULL,退出循环。
- 返回变量 j 的值作为单链表的长度。
int ListLength(LinkList L) { /*L为带头结点的单链表*/
Node *p;
p = L->next;
j = 0; /*用来存放单链表的长度*/
while (p != NULL) {
p = p->next;
j ++;
}
return j;
}
5.7 两个有序单链表的合并
- 有两个单链表 LA 和 LB,其元素均为非递减有序排列,将它们合并成一个单链表 LC, 要求 LC 也是非递减有序排列
算法思路
- 定义指针pa和pb分别指向两个单链表LA和LB中的第一个节点,同时定义指针r指向LC的头结点。
- 初始化LC为空表,即LC的next指针为NULL。
- 循环比较pa和pb指向的节点的值,选择较小值的节点插入到LC中,并更新相应指针的位置。
- 当其中一个单链表的节点处理完之后,将另一个链表剩余的元素直接链接到LC的尾部。
- 释放LB的头结点,返回LC。
LinkList MergeLinkList(LinkList LA, LinkList LB)
/*将递增有序的单链表LA和LB合并成一个递增有序的单链表LC*/
{
Node *pa, *pb;
Node *r;
LinkList LC;
/*将LC初始置空表。pa和pb分别指向两个单链表LA和LB中的第一个结点,r初值为LC*/
pa = LA->next;
pb = LB->next;
LC = LA;
LC->next = NULL;
r = LC;
/*当两个表中均未处理完时,比较选择将较小值结点插入到新表LC中。*/
while (pa != NULL && pb != NULL) {
if (pa->data <= pb->data) {
r->next = pa;
r = pa;
pa = pa->next;
} else {
r->next = pb;
r = pb;
pb = pb->next;
}
}
if (pa) /*若表LA未完,将表LA中后续元素链到新表LC表尾*/
r->next = pa;
else /*否则将表LB中后续元素链到新表LC表尾*/
r->next = pb;
free(LB);
return (LC);
}
...全文


