数据结构(C语言)—— 线性表小结

线性表

1、线性表的定义
线性表简称为表,是由n(n>=0)个数据元素(也叫节点或表元素)组成的有限序列。n=0时,该线性表成为空表。
表中各数据元素: 1)存在线性关系 2)结构类型完全一致

2、线性表的特点

  1. 唯一首元素
  2. 唯一尾元素
  3. 除首元素外,任何一个元素都有一个前驱
  4. 除尾元素外,任何一个元素都有一个后继
  5. 每个元素有一个位序(下标)

线性表的顺序存储

1.线性表的顺序存储原理
用一组地址连续的存储单位按线性表元素之间的逻辑顺序,一次存储线性表的数据元素。(属于静态存储,不能自由扩充)

数据元素的逻辑顺序和物理上的存储顺序完全一致,因此不需要另外建立空间来记录各个元素间
的关系

2、优、缺点
优点:随机存储。可以随机存取表中的任意一个数据元素(查快)
缺点:存储密度大。在插入、删除某元素时,需要移动大量元素(改慢);浪费储存空间

代码实现

#include<stdio.h>
#include<stdlib.h>

#define INIT_SIZE 10  //初始值
#define INCERMENT 5   //增加值
typedef int ElemType;   //自定义类型 (别名)
typedef struct {
	ElemType* elem;   //元素首地址
	int length;   //长度(元素个数)
	int listsize;  //容量(内存大小),用以判断顺序表是否为空
}SqList;   //顺序表名
//初始化
int Init_Sqlist(SqList* L) {
	L->elem = (ElemType*)malloc(INIT_SIZE * sizeof(ElemType));   //分配长度为INIT_SIZE字节的内存块
	if (!L->elem)  //if(L->elem == NULL)
		return -1;
	L->length = 0;
	L->listsize = INIT_SIZE;
	return 0;
}
//插入 , 在第i(下标)个位置插入元素e
int Insert_SqList(SqList* L, int i, ElemType e) {
	ElemType* newBase;  //保存新分配的内存首地址
	int j;
	if (i<0 || i>L->length)  //判断i的值是否有效
		return -1;
	if (L->length >= L->listsize) {   //判断内存是否满
		newBase = (ElemType*)realloc(L->elem , (L->listsize + INCERMENT) * sizeof(ElemType));   //将malloc()函数分配的地址扩大
		if (newBase == NULL)
			return -1;
		L->elem = newBase;
		L->listsize += INCERMENT;
	}
	for (j = L->length - 1; j >= i; j--){    //i下标后的值后移
		L->elem[j + 1] = L->elem[j];
	}
	L->elem[i] = e;
	L->length++;
	return 0;
}
//查找 , 找到值为e的位置
int Find_SqList(SqList L, ElemType e) {
	for (int i = 0; i < L.length ; i++) {
		if (L.elem[i] == e)
			return i + 1;
	}
	return 0;
}
//删除 , 第i(下标)位置的值
int Delect_SqList(SqList* L, int i) {
	if (i<0 || i>L->length)
		return -1;
	for (int j = i; j < L->length; j++)   //将i位置后的值前移,覆盖i位置的值
		L->elem[j] = L->elem[j + 1];
	L->length--;
	return 0;
}
//输出
void show(SqList L) {
	for (int i = 0; i < L.length; i++) {
		printf("%d\t", L.elem[i]);
	}
	printf("\n");
}
void main() {
	SqList L;
	Init_Sqlist(&L);
	Insert_SqList(&L, 0, 1);
	Insert_SqList(&L, 1, 2);
	Insert_SqList(&L, 2, 3);
	show(L);
	int a = Find_SqList(L, 3);
	printf("%d", a);
	printf("\n");
	Delect_SqList(&L, 1);
	show(L);
	system("pause");
}

在这里插入图片描述

线性表的链式存储

1.线性表的链式存储原理
用一组任意的存储单位来存放线性表的数据元素(存储单位可以是连续的,也可以是不连续的),数据元素之间的逻辑关系通过指针来指示。其中包括单链表、循环链表、双向链表。
2、优、缺点
优点:插入、删除通过修改链表指针,不需要移动数据元素(改快)
缺点:不可随机存取元素(查慢)

1、单链表

1)单链表存储原理
对于每一个数据元素来说,链表除了存放数据元素本身的值以外,还应一起存放数据元素的直接后继点所在存储单元的内存起始地址。
在这里插入图片描述

每个节点只有一个指向后继节点的指针。
每个节点包括数据域和指针域才可将数据元素之间的逻辑关系完全体现出来,(节点间的逻辑
结构比每个节点的实际内存地址显得更重要)

2) 头节点
定义:在链表头部加入一个特殊的节点,类型与数据节点相同,但其数据域不存放有效值,标识链表的头指针变量指向该节点。
作用:链表为空时头节点的头指针就指向头节点(头节点指针域为NULL);在插入和删除操作中无需进行特殊处理。
在这里插入图片描述

代码实现
#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;
typedef struct LNode {      //结点
	ElemType data;         //数据域
	struct LNode *next;    //指针域
}LNode, *LinkList;         //LNode* 等价于LinkList
//根据元素的值生成一个结点
LNode *CreateNode(ElemType e) {
	LNode *p = (LNode*)malloc(sizeof(LNode));
	p->data = e;
	p->next = NULL;
	return p;
}
//初始化
LinkList Init_LinkList() {
	return CreateNode(0);
}
//插入 , 在某结点curNode后插入新结点newNode
//当curNode为NULL时,表示在尾部插入
void Insert_LinkList(LinkList L, LNode* curNode, LNode* newNode) {
	LNode* p = curNode;
	if (!newNode) {
		return;
	}
	if (!curNode) {          //在尾部插入
		p = L;               //p为头结点
		while (p->next != NULL) {
			p = p->next;      //下一个
		}
	}
	newNode->next = p->next;    //插入新结点
	p->next = newNode;
	L->data++;
}
//查找 , 元素所在的结点位序(下标)
LNode* Find_LinkList(LinkList L, ElemType e) {
	LNode* p = L->next;   //首元
	int i = 0;
	while (p != NULL) {
		if (p->data == e)
			return i;
		p = p->next;
		i++;
	}
	return NULL;
}
//删除 , 按元素删除
void Delete_LinkList(LinkList L, ElemType e) {
	LNode* p, * c;   //p为前一个结点,c为当前结点
	p = L;
	while (p->next != NULL) {
		if (p->next->data == e) {
			c = p->next;
			p->next = p->next->next;
			L->data--;
			free(c);
			break;
		}
		p = p->next;
	}
}
//遍历
void show(LinkList L) {
	LNode* p;  //链表首元
	for (p = L->next; p != NULL; p = p->next) {
		printf("%d\t", p->data);
	}
	printf("\n");
}
void main() {
	LinkList L = NULL;
	LNode* node1, * node2, * node3, * node4, * node5;
	L = Init_LinkList();
	if (L == NULL)
		printf("链表初始化失败");
	//生成结点
	node1 = CreateNode(1);
	node2 = CreateNode(2);
	node3 = CreateNode(3);
	node4 = CreateNode(4);
	node5 = CreateNode(5);
	//尾部插入
	Insert_LinkList(L, NULL, node1);
	Insert_LinkList(L, NULL, node2);
	Insert_LinkList(L, node1, node3);
	//最前面插入
	Insert_LinkList(L, L, node4);
	Insert_LinkList(L, node2, node5);
	show(L);
	int i = Find_LinkList(L, 3);
	printf("%d", i);
	printf("\n");
	Delete_LinkList(L, 5);
	show(L);
}

在这里插入图片描述

2、循环链表

将单链表的尾结点的指针域指向头节点。
特性:从任何一个节点开始,都可以遍历整个链表。(单链表:整个链表遍历需从头节点开始)

代码实现
//初始化
LinkList Init_LinkList() {
	LinkList head = CreateNode(0);
	head->next = head;
	return head;
}
//插入 , 在某结点xNode后插入新结点newNode
//当xNode为NULL时,表示在尾部插入
void Insert_LinkList(LinkList head, LNode* xNode, LNode* newNode) {
	LNode* tail;
	LNode* p = head;
	while(p->next != head) {
		p = p->next;
	}
	tail = p;
	if (xNode == NULL || xNode == tail) {          //在尾部插入
		tail->next = newNode;          
		newNode->next = head;
	}else {
		newNode->next = xNode->next;
		xNode->next = newNode;
	}
	head->data++; 
}

3、双向链表

在链表节点中增加一个指向前驱节点的指针。
特性:可以快速找到节点前驱和后继
双向链表的储存结构描述

typedef struct DuLNode{
	ElemType data;
	struct DuLNode *prior;  //指向前驱
	struct DuLNode *next;   //指向后继
}DuLNode,* DuLinkList;
双向循环链表

双向链表中每个节点都有指向前驱和后继的指针,所以在插入、删除时对指针的操作必须在两个方向同时进行,否则可能会使其中一条链被截断(若截断可以利用另一条链来修补)。
因此,为了算法统一将双向链表的前向单链表和后向单链表共用一个头结点首尾相连,构成一个双向循环链表。

双向循环链表的某节点后插入
在这里插入图片描述

void inselem(DuLinkList L,ElemType e,DuLNode *p){
	DuLNode *s,*r;
	s = (DULNode*)malloc(sizeof(DuLNode));
	s->data = e;       //建立一个新节点s
	r = p->next;       
	s->next = r;       //s的后继为r
	r->prior = s;      //插入节点p后
	p->next = s;       
	s->prior = p;
}
在某节点前插入类似

双向循环链表的删除某节点
在这里插入图片描述

删除某节点,即是修改某节点前节点的next指针、后节点的prior指针,可以创建一个指针返回
删除的节点,最后释放删除节点空间(free())
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是 C 语言线性表基本操作代码: #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 // 线性表的最大长度 typedef int ElemType; // 定义线性表元素类型为整型 typedef struct { ElemType data[MAXSIZE]; // 存储线性表元素的数组 int length; // 线性表的当前长度 } SqList; // 顺序存储结构的线性表 // 初始化线性表 void InitList(SqList *L) { L->length = 0; // 线性表长度为0 } // 判断线性表是否为空 int ListEmpty(SqList L) { return L.length == 0; } // 获取线性表长度 int ListLength(SqList L) { return L.length; } // 获取线性表中指定位置的元素 int GetElem(SqList L, int i, ElemType *e) { if (i < 1 || i > L.length) { return 0; // i值不合法 } *e = L.data[i - 1]; // 将第i个元素的值赋给e return 1; } // 在线性表中查找指定元素的位置 int LocateElem(SqList L, ElemType e) { for (int i = 0; i < L.length; i++) { if (L.data[i] == e) { return i + 1; // 返回元素在线性表中的位置 } } return 0; // 没有找到元素 } // 在线性表中插入元素 int ListInsert(SqList *L, int i, ElemType e) { if (i < 1 || i > L->length + 1) { return 0; // i值不合法 } if (L->length >= MAXSIZE) { return 0; // 线性表已满 } for (int j = L->length; j >= i; j--) { L->data[j] = L->data[j - 1]; // 将第i个元素及之后的元素后移 } L->data[i - 1] = e; // 将新元素插入到第i个位置 L->length++; // 线性表长度加1 return 1; } // 在线性表中删除元素 int ListDelete(SqList *L, int i, ElemType *e) { if (i < 1 || i > L->length) { return 0; // i值不合法 } *e = L->data[i - 1]; // 将第i个元素的值赋给e for (int j = i; j < L->length; j++) { L->data[j - 1] = L->data[j]; // 将第i个元素之后的元素前移 } L->length--; // 线性表长度减1 return 1; } // 打印线性表中的所有元素 void PrintList(SqList L) { for (int i = 0; i < L.length; i++) { printf("%d ", L.data[i]); } printf("\n"); } // 测试线性表基本操作 int main() { SqList L; InitList(&L); ListInsert(&L, 1, 1); ListInsert(&L, 2, 2); ListInsert(&L, 3, 3); ListInsert(&L, 4, 4); ListInsert(&L, 5, 5); printf("线性表中的元素为:"); PrintList(L); int e; GetElem(L, 3, &e); printf("线性表中第3个元素为:%d\n", e); printf("元素2在线性表中的位置为:%d\n", LocateElem(L, 2)); ListDelete(&L, 4, &e); printf("删除线性表中第4个元素后,线性表中的元素为:"); PrintList(L); printf("线性表的长度为:%d\n", ListLength(L)); return 0; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值