线性表的学习笔记分享

沐尤 2023-11-19 20:01:05

目录

一、线性表的基本概念

二、顺序表的定义

三、顺序存储结构的基本运算

四、链表的定义

五、单链表上的基本运算


一、线性表的基本概念

线性表是一种常见的数据结构,它由一组有序的元素组成,每个元素最多只有一个前驱和一个后继。线性表可以分为两种类型:顺序表和链表。


二、顺序表的定义

顺序表是一种用数组实现的线性表,它的元素在内存中连续存储。顺序表的特点是:

  1. 可以通过下标直接访问元素;
  2. 随机访问速度快;
  3. 插入和删除操作需要移动元素,效率较低。

顺序表的实现代码如下:

#define maxsize 100 //线性表可能达到的最大长度
typedef struct {
    ElemType elem[maxsize]; //线性表占用的数组空间
    int last; //记录线性表中最后一个元素在数组elem[ ]中的位置(下标值),空表置为 -1
} SeqList;

三、顺序存储结构的基本运算

3.1 按内容查找运算

  • 查找线性表 L 中第 i个数据元素

算法思路

  1. 初始化计数器i为0,用于记录当前扫描的位置。
  2. 进入while循环,条件是i小于等于L.last且L.elem[i]不等于e。
  3. 在循环中,逐个扫描顺序表中的元素,直到找到值为e的元素或者扫描到表尾。
  4. 如果找到值为e的元素,则返回其序号i + 1(序号比下标多1)。
  5. 如果未找到值为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

算法思路

  1. 首先判断插入位置i是否合法,即i的取值范围应该是 1 到 L->last + 2 之间,如果不合法则输出错误信息并返回ERROR。
  2. 然后判断顺序表是否已满,如果已满则输出错误信息并返回错误代码。
  3. 如果插入位置合法且顺序表未满,则从后向前遍历顺序表,将位置大于等于i的元素依次向后移动一位,为插入元素腾出空间。
  4. 将元素e插入到位置i处(注意在C语言中数组的下标是从0开始的,所以实际上是在下标为i-1的位置插入元素)。
  5. 最后更新顺序表的长度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个元素删除

算法思路

  1. 首先判断删除位置i是否合法,即i的取值范围应该是 1 到 L->last + 1 之间,如果不合法则输出错误信息并返回ERROR。
  2. 将被删除的元素保存到变量e所指向的内存空间中,以便调用函数后获取被删除的元素。
  3. 从位置i开始,将后面的元素依次向前移动一位,覆盖掉被删除的元素。
  4. 更新顺序表的长度last,使其减少1。
  5. 删除成功返回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);
}

四、链表的定义

链表是一种用链式结构实现的线性表,它的元素在内存中不连续存储。链表的特点是:

  1. 插入和删除操作不需要移动元素,效率较高;
  2. 不可以通过下标直接访问元素,需要通过指针访问。

链表的实现代码如下:

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 建立单链表 

  • 头插法

算法思路

  1. 初始化指针变量s和整型变量flag,并将flag赋初值为1。
  2. 通过循环逐个接收输入字符,如果输入的字符不是'$',则创建一个新节点s,并将输入的字符赋给s的数据域。
  3. 将新节点s插入到链表L的头部,即使s的next指针指向链表原来的第一个节点,然后更新链表的头指针L指向s。
  4. 如果输入的字符是'$',则将flag置为0,表示建表结束。
  5. 完成字符输入和链表构建过程。
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;
        }
    }
}

  •  尾插法

算法思路

  1. 初始化指针变量r和s,以及整型变量flag并赋初值为1。
  2. 通过循环逐个接收输入字符,如果输入的字符不是'$',则创建一个新节点s,并将输入的字符赋给s的数据域。
  3. 将新节点s插入到链表L的尾部,并更新r指向新的尾节点s。
  4. 如果输入的字符是'$',则将flag置为0,表示建表结束,并将尾节点的next指针置为空。
  5. 完成字符输入和链表构建过程。
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个位置前插入一个数据元素

算法思路

  1. 初始化指针变量pre和s,整型变量k。
  2. 判断插入位置i是否合理,如果不合理则返回错误。
  3. 通过循环找到第i-1个节点,即pre指向第i-1个节点。
  4. 判断是否找到了第i-1个节点,如果没有找到,则打印错误信息并返回错误。
  5. 为新节点s申请内存空间,并将值e赋给s的数据域。
  6. 将s插入到pre指向节点的后面,完成插入操作。
  7. 返回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

算法思路

  1. 初始化指针变量pre和r,整型变量k以及存储删除节点值的指针e。
  2. 通过循环找到第i-1个节点,即pre指向第i-1个节点。
  3. 判断是否找到了第i-1个节点,如果没有找到,则打印错误信息并返回ERROR。
  4. 找到第i-1个节点后,将r指向要删除的第i个节点,pre的next指针跳过r将其指向r的下一个节点,实现删除操作。
  5. 将r节点的数据存储到指针e指向的变量中。
  6. 释放r节点的内存空间。
  7. 返回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

 算法思路

  1. 设带头结点的单链表的长度为 n,要查找表中第 i 个结点
  2. 从单链表的头指针 L 出发,从头结点 (L) 开始 顺着链域扫描,需要移动 i 次
  3. 用 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

 算法思路

  1. 在单链表中查找值等于 key 的结点
  2. 从单链表的头指针指向的头结点出发,顺链逐个 将结点值和给定值 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 求单链表的长度 

算法思路

  1. 遍历单链表,从头结点的下一个节点开始,依次遍历每个节点。
  2. 使用一个变量 j 来存放单链表的长度,初始值为 0。
  3. 当当前节点不为 NULL 时,将指针 p 指向下一个节点,然后 j 自增 1,表示单链表的长度加一。
  4. 直到遍历到最后一个节点,此时 p 为 NULL,退出循环。
  5. 返回变量 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 也是非递减有序排列

算法思路

  1. 定义指针pa和pb分别指向两个单链表LA和LB中的第一个节点,同时定义指针r指向LC的头结点。
  2. 初始化LC为空表,即LC的next指针为NULL。
  3. 循环比较pa和pb指向的节点的值,选择较小值的节点插入到LC中,并更新相应指针的位置。
  4. 当其中一个单链表的节点处理完之后,将另一个链表剩余的元素直接链接到LC的尾部。
  5. 释放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);
}
...全文
75 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

480

社区成员

发帖
与我相关
我的任务
社区描述
闽江学院IT领域专业的学生社区
社区管理员
  • c_university_1157
  • 枫_0329
  • 傅宣
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧