“这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战”
关注我,以下内容持续更新
链表介绍
链表是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点存储了下一个节点的指针。因为不一定是连续存储,链表在插入的时候可以达到 O(1) 的复杂度,比顺序表(比如数组)快得多,但是查找一个节点或者访问特定编号的节点则需要 O(n) 的时间,而顺序表的插入和查询的时间复杂度分别是 O(logn) 和 O(1)。
链表在内存中的存储特点如下:
-
链表的各个节点不一定是连续存储,所以可以充分利用内存中的碎片空间。
-
链表是以节点的方式来存储,是链式存储;
-
链表是由节点来实现的,每个节点包含data域和next域:data存储有效数据,可以是任意类型;next指向下一个节点;
-
因为链表可以动态创建节点,所以链表的长度没有逻辑上的限制;
-
链表分带头节点的链表和没有头节点的链表,可以根据实际需求自行选择
链表 vs 数组
数组:
- 查找快,添加/删除慢。
- 大小固定,若存储空间不足,需进行扩容,一旦扩容就要进行数据复制,非常费时。
链表:
- 查找慢,添加/删除快。
- 内存空间消耗更大,因为链表每个结点都需要消耗额外的存储空间去存储指向下一个结点的指针。对链表进行频繁的插入和删除操作,会导致频繁的内存申请和释放,容易造成内存碎片。
常见种链表结构
- 单向链表
- 双向链表
- 循环链表
- 块状链表
- 其他扩展
本文主要介绍单链表的增删改查
单链表的增删改查
-
插入节点:
插入有两种.一种是在链表尾部插入,另一类为在中间插入。在中间插入又分为在指定结点前插入和指定结点后插入,相对复杂;本文采用尾插法,这样可以保证输出的时候按插入顺序输出.
队尾插入逻辑:用 while 循环遍历找到尾结点,然后插入:
current.next = newNode -
删除节点
先判断链表是否为空,若为空直接返回;若不为空则删除.
删除的逻辑,先定义一个flag 标记是否找到删除的节点;用while循环遍历找出待删除的节点的上一个节点(注意用current.next判断,因为要找到待删除节点的上一个节点:
if ([current.next.data.ID isEqualToString:stu.ID])),如果找到设置 flag 为 YES,直接break;遍历完后,判断flag,若为YES执行删除操作,若为 NO 则输出"未找到该节点,无法删除". 删除的代码:current.next = current.next.next -
修改节点
先判断链表是否为空,若为空直接返回,若不为空,再执行修改逻辑.
修改的逻辑:先定义一个flag 标记是否找到待修改的节点;用while循环遍历找出待修改的节点,如果找到设置 flag 为 YES,直接break;遍历完后,判断flag,若为YES执行修改操作,若为 NO 则输出"未找到该节点,无法修改". 修改的代码:
current.data.name = newEle.name -
查询链表
先判断链表是否为空,若为空直接返回,若不为空,再执行查询逻辑. 查询逻辑:用 while 循环遍历输出
SingleLinkList.h 文件
//内置类:LinkNode
@interface LinkNode : NSObject
/**data可以是任何类型*/
@property (nonatomic,strong) Student * data;
/**指向下一个元素*/
@property (nonatomic,strong) LinkNode *_Nullable next;
- (instancetype)initWithData:(Student*_Nullable)data;
@end
@interface SingleLinkList : NSObject
/**链表的头结点*/
@property (nonatomic,strong) LinkNode * head;
-(BOOL)isEmpty;
-(void)add:(Student*)stu;
//面试题6:按id顺序插入学生
/**
根据id从小到大插入(也可以实现根据排名插入等类似问题)
返回 1代表插入成功,返回 0 代表无法插入,已有相同的id
*/
-(BOOL)addByOrder:(Student*)stu;
-(void)del:(Student*)stu;
-(void)updateNode:(Student*)node newNode:(Student*)stu;
-(void)show;
@end
SingleLinkList.m 文件
//LinkNode类
@implementation LinkNode
- (instancetype)initWithData:(Student*)data
{
self = [super init];
if (self) {
self.data = data;
}
return self;
}
@end
@implementation SingleLinkList
- (instancetype)init
{
self = [super init];
if (self) {
self.head = [[LinkNode alloc]initWithData:nil];
}
return self;
}
-(BOOL)isEmpty{
return self.head.next == NULL;
}
-(void)add:(Student*)stu{
LinkNode*newNode = [[LinkNode alloc]initWithData:stu];
LinkNode*current = self.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
/**
根据id从小到大插入(也可以实现根据排名插入等类似问题)
返回 1代表插入成功,返回 0 代表无法插入,已有相同的id
*/
-(BOOL)addByOrder:(Student*)stu{
LinkNode*newNode = [[LinkNode alloc]initWithData:stu];
BOOL flag = YES;//标记是否可以插入
LinkNode*current = self.head;
while (current.next) {
if ([stu.ID intValue] < [current.next.data.ID intValue]) {
break;//找到正确的位置就返回,在最后插入
}else if([stu.ID intValue] == [current.next.data.ID intValue]){
flag = NO;//如果存在相同的id,则不能插入
break;
}
current = current.next;
}
if (flag) {
//插入到正确的位置
newNode.next = current.next;
current.next = newNode;
}else{
printf("已有相同的id,无法插入\n");
}
return flag;
}
-(void)del:(Student*)stu{
//先判断链表是否为空
if (self.head.next == NULL) {
printf("链表为空,无法删除 \n");
return;
}
//标记是否可以找到待删除节点,YES代表可以找到
BOOL flag = NO;
LinkNode*current = self.head;
while (current.next) {
if ([current.next.data.ID isEqualToString:stu.ID]) {
flag = YES;//已找到待删除节点的上一个节点
break;
}
current = current.next;
}
if (flag) {
current.next = current.next.next;//删除节点
}else{
printf("没有找到该数据\n");
}
}
-(void)updateNode:(Student*)newEle{
// 判断链表是否为空
if ([self isEmpty]) {
printf("链表为空,无法修改\n");
return;
}
//标记是否可以修改
BOOL flag = NO;
LinkNode*cur = self.head.next;
while (cur) {
if ([cur.data.ID isEqualToString:newEle.ID]) {
flag = YES;
cur.data.name = newEle.name;
break;
}
cur = cur.next;
}
if (!flag) {
printf("没有找到,无法修改");
}
}
-(void)show{
printf("打印链表:");
if ([self isEmpty]) {
printf("链表为空 \n");
return;
}
LinkNode*current = self.head;
while (current.next != NULL) {
current = current.next;
printf("%s-->",current.data.ID.UTF8String);
}
printf("\n");
}
@end
这是单链表增删改查的测试代码
-(void)singleLinkListCase{
SingleLinkList*linkedList = [SingleLinkList new];
[linkedList del:[[Student alloc]initWithID:@"2" name:@"红"]];
//打印队列
[linkedList show];
[linkedList add:[[Student alloc]initWithID:@"1" name:@"明"]];
[linkedList add:[[Student alloc]initWithID:@"2" name:@"红"]];
[linkedList add:[[Student alloc]initWithID:@"3" name:@"军"]];
[linkedList add:[[Student alloc]initWithID:@"4" name:@"国"]];
[linkedList addByOrder:[[Student alloc]initWithID:@"0" name:@"a"]];
[linkedList addByOrder:[[Student alloc]initWithID:@"6" name:@"b"]];
[linkedList addByOrder:[[Student alloc]initWithID:@"5" name:@"c"]];
[linkedList addByOrder:[[Student alloc]initWithID:@"0" name:@"a"]];//这里测试插入失败
[linkedList show];
[linkedList del:[[Student alloc]initWithID:@"2" name:@"红"]];
[linkedList del:[[Student alloc]initWithID:@"1" name:@"明"]];
[linkedList show];
[linkedList del:[[Student alloc]initWithID:@"1" name:@"明"]];
[linkedList show];
}
关注我
如果觉得我写的不错,请点个赞 关注我 您的支持是我更文最大的动力!