线性表链式表示-单链表

上一节我们所讲的顺序存储其实是有缺陷的,最大的缺点就是数据的增删的时候可能会移动大量的数据,然后链式存储则可以呵呵好解决这个问题

当我们用链式来存储线性表的时候,不需要地址连续的存储单元,即 不要求逻辑相邻的元素在物理位置上也相邻,它通过 建立起数据元素之间的逻辑关系,因此插入或者删除时候不需要移动元素只需要修该指针,但是也会因此失去 随机存取的特点只能进行 顺序存取

首先要说明的是单链表的实现

线性表中的单链表最为重要的特点我认为还是 如果要寻找目标元素只能找他的前一个结点元素,为了方便单链表的创建增删改等存在一般会初始化一个头结点,真能给单链表带来两个好处:

1.由于第一个数据结点得到位置被存放在头结点的指针域中,所以就算是第一个位置也和其他的操作的位置无差

2.无论链表是否为空,其头指针指向头结点的非空指针(空表中头结点的指针域为空),因此空表和非空表的处理也得到了统一

单链表的ADT定义

typedef int Elemtype;
//定义链表ADT(单链表)
typedef struct LNode {
	struct Node* next;//记录下一个节点的地址
	Elemtype data;
}LNode, * LinkList;//定义一个指向结构体的指针LinkList

每个节点都有一个数据域和指针域,数据域保存着你需要的数据类型,指针域会指向下一个节点地址

定义一个结构体指针是因为malloc动态分配时候是开辟的空间返回一个空类型指针变量,意思就是指针指向的地方的起始位置开始开辟出了你自己定义的一段空间一般用sizeof分配

在实现让方法之前先看看我们定义的状态码等信息

//定义状态码
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXSIZE 20//存储空间默认值

typedef int Status;//函数类型,返回的是状态码时候使用

初始化

Status InitList(LinkList* L) {
	*L = (LinkList)malloc(sizeof(LNode));//产生头结点,并且让L指向它
	if (!(*L)) {
		//如果没创建成功
		return ERROR;
	}
	//注意这里L为二级指针所以解引用之后是个指向Lnode的指针
	(*L)->next = NULL;//初始状态下头节点的next为空
	return OK;
}

初始化其实就是创建头结点的过程

单链表头插法

//头插法
//用到随机数种子创建n个随机数放松分别按头插法方式插入
void CreateListHead(LinkList* L, int n) {
 //建立带头结点的单链表
	*L = (LinkList)malloc(sizeof(LNode));
	(*L)->next = NULL;
	srand(time(0));
	LinkList tmp;
	for (int i = 0; i < n; ++i) {
		tmp = (LinkList)malloc(sizeof(LNode));
		tmp->data = rand() % 20 + 1;//生成20-1的随机数
		tmp->next = (*L)->next;(1)
		(*L)->next = tmp;(2)
	}
}

其实还是很简单的,主要关键点是(1)和(2)步骤,因为是头插法也就是说把原来头结点和头结点的next节点中间插入新来的节点,第一个操作时让新来的节点指向原来头节点的下一个节点然后再让头结点指向新创建的节点

单链表的尾插法

//尾插法
void CreateListTail(LinkList* L, int n) {
	srand(time(0));//初始化随机数种子
	*L = (LinkList)malloc(sizeof(LNode));
	LinkList p, q;
	q = *L;//q用于指向尾部节点
	for (int i = 0; i < n; ++i) {
		p = (LNode*)malloc(sizeof(LNode));
		p->data = rand() % 20 + 1;
		q->next = p;
		q= p;
	}
	q->next = NULL;	
}

如果是尾插,那肯定要创建一个变量一直指向现在最后一个元素的位置,然后新元素加入,让之前监控指针指向的下一个节点赋值上新的结点,最后让监控结点指向当前最后一个元素位置

判空

//当表存在是看看是否为空表
Status IsEmpty(LinkList L) {
	if (L->next == NULL) {
		return TRUE;
	}
	return FALSE;
}

单链表的插入

//单链表的插入

Status ListInsert(LinkList* L, int i, Elemtype e) {
	LinkList temp = *L;
	int point = 1;
	//寻找节点进行插入
	while (temp && point < i) {
		temp = temp->next;
		point++;
	}
	if (temp == NULL || point > i) {
		printf("无法插入");
		return ERROR;
	}
	LinkList s;
	s = (LinkList)malloc(sizeof(LNode));
	s->data = e;
	s->next = temp->next;
	temp->next = s;
	return OK;
}

单链表的删除

//删除元素
Status ListDelte(LinkList* L, int i, Elemtype* e) {
	//遍历寻找对应元素
	LinkList tmp;
	LinkList p = *L;
	int j=0;//计数,p从头结点开始没向后移一次就+1
	while (p && j < i-1) {
		//遍历寻找到对应元素
		//这里的i-1的不懂可以推一推
		//主要是要找到删除节点的前移节点,i-1主要是最后有个j++的动作
		//说明他已经是i-1个元素了就退出
		p = p->next;
		j++;
	}
	//判断一下没找到的情况,一个是j》i一个数超出p范围了
	if (p == NULL || p->next == NULL||j>i) {
		printf("不合法超出表长");
		return ERROR;
	}
	tmp = p->next;
	p->next = tmp->next;
	*e = tmp->data;
	free(tmp);
	return OK;
}

单链表的删表

Status ClearList(LinkList* L)
{
	LinkList p, q;
	p = (*L)->next;
	while (p) {
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = NULL;
	return OK;
}

按序号查找

//找到对应位置元素
Status GetElem(LinkList L, int i, Elemtype* e)
{
	LinkList p;
	p = L->next;
	int calc = 0;//计数器
	while (p&&calc<i) {
		p = p->next;
		calc++;
	}
	if (!p || calc > i) {
		return ERROR;
	}
	*e = p->data;
	return OK;
}

按值查找

//返回对应元素位置
int LocateELem(LinkList L, Elemtype e) {
	LinkList p;
	int i = 0;//记住位置
	p = L->next;
	while (p) {
		++i;
		if (p->data == e) {
			return i;
		}
		p = p->next;
	}
	re

单链表主要功能实现即上面这些了,其他相关操作也类似以上操作

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值