链表专题详解

链表相关概念

链表的构成与性质

链表是一种物理存储结构上非连续、非顺序的存储结构( 在内存中不连续 ),数据元素的逻辑顺序是通过链表中的指针链接次序实现的
链表的元素由 数据域指针域两部分构成,这两部分就构成了一个节点
数据域存储数据,指针域存储指向下一个节点的地址
类比火车车厢,钩子就相当于指针,关联起每一个车厢
数据域相当于车厢里的乘客、设备等等
综上链表有如下性质:
  1. 内存(物理)上不一定连续,逻辑上连续
  2. 节点一般是从堆上申请空间的’
  3. 从堆上申请空间,取决于操作系统的内存配策略,所以链表空间分配可能连续也可能是离散的

单链表结构的定义

typedef int DataType;

struct SLTNode{

        DataType data;                        //数据域

        struct SLNode* next;              //指针域,指向下一个节点的结构体指针

}SLTNode;        

单链表的打印

注:单链表中笔者提到的头节点指的是链表的第一个节点,并非哨兵位,请大家理解一下,不要搞混淆了

void SLTPrint(SLTNode* phead){
    SLTNode* pcur = phead;
    while(pcur){
        printf("%d->",pcur->data);
        pcur = pcur->next;
    }
    printf("NULL\n"0);
}

 


单链表:单向不带头不循环链表

新建一个节点

SLTNode* SLTNodeBuild(DataType input) {
	SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));
	newNode->data = input;
	newNode->next = NULL;

	return newNode;

}

插入数据

头插

void SLTInsetFront(SLTNode** pphead, DataType input) {
	assert(pphead);

	SLTNode* newNode = SLTNodeBuild(input);
	//新节点必须先指向*pphead,再给*pphead赋值,否则先赋值的话,*pphead之后的原始数据丢失!
	newNode->next = *pphead;
	*pphead = newNode;
}

 尾插


void SLTInsetBack(SLTNode** pphead, DataType input) {
	assert(pphead);
	assert(*pphead);

	SLTNode* newNode = SLTNodeBuild(input);

	//链表为空 新节点就是首节点
	if (*pphead == NULL) {
		*pphead = newNode;
		return;
	}
	//链表不为空 找尾节点
	SLTNode* ptail = *pphead;
	while (ptail->next) {
		ptail = ptail->next;
	}
	ptail->next = newNode;//ptail就是尾节点

}

指定位置之前插入

void SLTInsetPosFront(SLTNode** pphead, SLTNode* pos, DataType input) {
	assert(pphead);
	assert(pos);

	SLTNode* newNode = SLTNodeBuild(input);
	//pos刚好在第一个位置 头插
	if (*pphead == pos) {
		SLTInsetFront(pphead, input);
		return;
	}
	//pos不在第一个位置
	
	SLTNode* prev = *pphead;
	while (prev->next != pos) {
		prev = prev->next;
	}
	//prev->newNode->next
	prev->next = newNode;
	newNode->next = pos;
}

 指定位置之后插入

void SLTInsetPosBack( SLTNode* pos, DataType input) {
	assert(pos);

	SLTNode* newNode = SLTNodeBuild(input);
	newNode->next = pos->next;
	pos->next = newNode;
}


删除数据

头删

void SLTDeleteFront(SLTNode** pphead) {
	assert(pphead);
	assert(*pphead);

	//让第二个节点成为新的头 释放第一个节点
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;

}

尾删

void SLTdeletetBack(SLTNode** pphead) {
	assert(pphead);
	assert(*pphead);//指向第一个节点的地址

	//只有一个节点
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
		return;
	}
	//链表节点数大于一
	SLTNode* prev = NULL;
	SLTNode* ptail = *pphead;
	while (ptail->next) {
		prev = ptail;//存储尾节点之前的数据
		ptail = ptail->next;//遍历链表寻找尾节点
	}
	prev->next = NULL;
	//销毁尾节点
	free(ptail);
	ptail = NULL;
}

删除指定位置

void SLTDeletePos(SLTNode** pphead, SLTNode* pos) {
	assert(pphead);
	assert(*pphead);
	assert(pos);

	//pos刚好是第一个节点 头删
	if (pos == *pphead) {
		SLTDeleteFront(pphead);
		return;
	}
	//pos不是第一个节点
	SLTNode* prev = *pphead;
	while (prev->next != pos) {
		prev = prev->next;
	}
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}

 删除指定位置之后的节点

void SLTDeletePosBack(SLTNode* pos) {
	assert(pos);
	assert(pos->next);//判断pos之后是否还有节点

	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}


查找指定元素

SLTNode* SLTFind(SLTNode** pphead, DataType target) {
	assert(pphead);

	SLTNode* pcur = *pphead;
	while (pcur) {
		if (pcur->data == target) {
			printf("successfully finding!\n");
		}
		pcur = pcur->next;
	}
	return NULL;
}


销毁

void SLTDestory(SLTNode** pphead) {
	assert(pphead);
	assert(*pphead);

	SLTNode* pcur = *pphead;
	while (pcur) {
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

链表的分类

我们这里说的带头和不带头是所谓的“哨兵位”,哨兵位不存储有效数据,需要与笔者在单链表中所说的“头节点”区分开


双向链表:带头双向循环链表

typedef int SLDataType;
typedef struct LTNode{
    SLDataType val;        //数据域
    struct LTNode* prev;   //指向前驱节点
    struct LTNode* next;   //指向后继节点
}LTNode;

指针域由两部分组成:prev指向先驱节点,next指针指向后继节点

哨兵位不存储有效数据,需要我们自己创建

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值