一、介绍

双向链表:每一个节点前后指针域都和它的上一个节点互相指向,尾节点的next指向空,首节点的pre指向空。

二、使用

注:跟单链表差不多,简单写常用的。循环链表无法形象化打印,后面也暂不实现了,但是要注意循环链表遍历时结束的标志。

循环链表遍历结束:tailNode.next == firstNode

双向循环链表遍历结束:tailNode.next == firstNode  && firstNode.pre == tailNode

***定义双向节点***

//  DoubleLinkNode.h
// LinkListDemo
// Created by 夏远全 on 2019/9/24.
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface DoubleLinkNode : NSObject
@property (nonatomic, assign) int data; //数据域
@property (nonatomic, weak, nullable) DoubleLinkNode *pre; //前驱指针域(防止循环引用)
@property (nonatomic, strong, nullable) DoubleLinkNode *next;//后继指针域
+(instancetype)constructNodeWithData:(int)data;
@end 
//  DoubleLinkNode.m
// LinkListDemo
// Created by 夏远全 on 2019/9/24.
#import "DoubleLinkNode.h" @implementation DoubleLinkNode +(instancetype)constructNodeWithData:(int)data { DoubleLinkNode *node = [[DoubleLinkNode alloc] init];
node.data = data;
node.next = nil;
node.pre = nil;
return node;
}
@end

1、构造双向循环链表

//1、构建一个双向链表
DoubleLinkNode *head = [[DoubleLinkNode alloc] init];
DoubleLinkNode *node1 = [DoubleLinkNode constructNodeWithData:];
DoubleLinkNode *node2 = [DoubleLinkNode constructNodeWithData:];
DoubleLinkNode *node3 = [DoubleLinkNode constructNodeWithData:];
head.next = node1;
node1.next = node2;
node1.pre = head;
node2.next = node3;
node2.pre = node1;
node3.pre = node2;
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"构造双向链表为"];
-- ::43.449741+ LinkList[:] 构造双向链表为:⇄⇄

2、插入节点

2-1:在头部插入节点

//双向链表:在头部插入节点
+(void)insetNodeAfterHead:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} if (headNode.next == nil) {
headNode.next = newNode;
newNode.pre = headNode;
}
else{
newNode.next = headNode.next; //当前节点后继指向的头结点后继
newNode.pre = headNode; //当前节点的前驱指向头结点
headNode.next.pre = newNode; //头结点的后继结点的前驱指向当前节点
headNode.next = newNode; //头结点的后继指向当前节点
}
}
//从头部插入
DoubleLinkNode *node4 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAfterHead:node4 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表头部插入节点4后"];
-- ::43.449741+ LinkList[:] 构造双向链表为:⇄⇄
-- ::43.450118+ LinkList[:] 在双向链表头部插入节点4后:⇄⇄⇄

2-2:在尾部插入节点

//双向链表:在尾部插入节点
+(void)insetNodeAfterTail:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} //设置偏移指针
DoubleLinkNode *pNode = headNode;
while (pNode.next != nil) {
pNode = pNode.next;
}
pNode.next = newNode;
newNode.pre = pNode;
}
//从尾部插入
DoubleLinkNode *node5 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAfterTail:node5 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表尾部插入节点5后"];
-- ::43.449741+ LinkList[:] 构造双向链表为:⇄⇄
-- ::43.450118+ LinkList[:] 在双向链表头部插入节点4后:⇄⇄⇄
-- ::43.450209+ LinkList[:] 在双向链表尾部插入节点5后:⇄⇄⇄⇄

2-3:在指定位置插入节点

//双向链表:在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} //设置偏移指针
DoubleLinkNode *pNode = headNode;
int i = ;
while (pNode!= nil && i<k) {
pNode = pNode.next;
i++;
}
if (i==k) {
//与从头结点插入的方式是一样的方法
newNode.next = pNode.next;
newNode.pre = pNode;
pNode.next.pre = newNode;
pNode.next = newNode;
}
}
//从指定位置插入
DoubleLinkNode *node6 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAtIndex: node:node6 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表第2个位置插入节点6后"];
-- ::43.449741+ LinkList[:] 构造双向链表为:⇄⇄
-- ::43.450118+ LinkList[:] 在双向链表头部插入节点4后:⇄⇄⇄
-- ::43.450209+ LinkList[:] 在双向链表尾部插入节点5后:⇄⇄⇄⇄
-- ::43.450262+ LinkList[:] 在双向链表第2个位置插入节点6后:⇄⇄⇄⇄⇄

3、删除节点

//双向链表:删除第k个位置的节点
+(DoubleLinkNode *)deleteNodeAtIndex:(int)k headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return nil;
} //设置偏移指针
DoubleLinkNode *pNode = headNode.next;
int i = ;
while (pNode!= nil && i<k) {
pNode = pNode.next;
i++;
}
if (i==k) {
pNode.pre.next = pNode.next; //当前节点的前驱节点的后继指向当前节点的后继结点
pNode.next.pre = pNode.pre; //当前节点的后继结点的前驱指向当前节点的前驱节点
return pNode;
}
return nil;
}
//3、删除节点
DoubleLinkNode *deleteNode = [FuncontionHandler deleteNodeAtIndex: headNode:head];
NSString *prefixText = [NSString stringWithFormat:@"删除第2个位置的节点%d后单链表为",deleteNode.data];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:prefixText];
-- ::43.449741+ LinkList[:] 构造双向链表为:⇄⇄
-- ::43.450118+ LinkList[:] 在双向链表头部插入节点4后:⇄⇄⇄
-- ::43.450209+ LinkList[:] 在双向链表尾部插入节点5后:⇄⇄⇄⇄
-- ::43.450262+ LinkList[:] 在双向链表第2个位置插入节点6后:⇄⇄⇄⇄⇄
-- ::43.450336+ LinkList[:] 删除第2个位置的节点6后单链表为:⇄⇄⇄⇄

4、遍历双向循环链表

//双向链表:遍历并打印链表
+(void)printFromHeadWithNode:(DoubleLinkNode *)headNode printPrefixText:(NSString *)text { //判空处理
if (!headNode) {
return;
} DoubleLinkNode *pNode = headNode.next;
NSMutableArray *items = [NSMutableArray array];
while (pNode!= nil) {
[items addObject:@(pNode.data)];
pNode = pNode.next;
}
NSLog(@"%@:%@",text,[items componentsJoinedByString:@"⇄"]);
}

三、源码

FuncontionHandler.h

//
// FuncontionHandler.h
// LinkList
//
// Created by 夏远全 on 2019/9/27.
// #import <Foundation/Foundation.h>
#import "DoubleLinkNode.h" NS_ASSUME_NONNULL_BEGIN @interface FuncontionHandler : NSObject //双向链表:在头部插入节点
+(void)insetNodeAfterHead:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode; //双向链表:在尾部插入节点
+(void)insetNodeAfterTail:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode; //双向链表:在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode; //双向链表:删除第k个位置的节点
+(DoubleLinkNode *)deleteNodeAtIndex:(int)k headNode:(DoubleLinkNode *)headNode; //双向链表:遍历并打印链表
+(void)printFromHeadWithNode:(DoubleLinkNode *)headNode printPrefixText:(NSString *)text; @end NS_ASSUME_NONNULL_END

FuncontionHandler.m

//
// FuncontionHandler.m
// LinkList
//
// Created by 夏远全 on 2019/9/27.
// #import "FuncontionHandler.h" @implementation FuncontionHandler //双向链表:在头部插入节点
+(void)insetNodeAfterHead:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} if (headNode.next == nil) {
headNode.next = newNode;
newNode.pre = headNode;
}
else{
newNode.next = headNode.next; //当前节点后继指向的头结点后继
newNode.pre = headNode; //当前节点的前驱指向头结点
headNode.next.pre = newNode; //头结点的后继结点的前驱指向当前节点
headNode.next = newNode; //头结点的后继指向当前节点
}
} //双向链表:在尾部插入节点
+(void)insetNodeAfterTail:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} //设置偏移指针
DoubleLinkNode *pNode = headNode;
while (pNode.next != nil) {
pNode = pNode.next;
}
pNode.next = newNode;
newNode.pre = pNode;
} //双向链表:在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(DoubleLinkNode *)newNode headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return;
} //设置偏移指针
DoubleLinkNode *pNode = headNode;
int i = ;
while (pNode!= nil && i<k) {
pNode = pNode.next;
i++;
}
if (i==k) {
//与从头结点插入的方式是一样的方法
newNode.next = pNode.next;
newNode.pre = pNode;
pNode.next.pre = newNode;
pNode.next = newNode;
} } //双向链表:删除第k个位置的节点
+(DoubleLinkNode *)deleteNodeAtIndex:(int)k headNode:(DoubleLinkNode *)headNode { //判空处理
if (!headNode) {
return nil;
} //设置偏移指针
DoubleLinkNode *pNode = headNode.next;
int i = ;
while (pNode!= nil && i<k) {
pNode = pNode.next;
i++;
}
if (i==k) {
pNode.pre.next = pNode.next; //当前节点的前驱节点的后继指向当前节点的后继结点
pNode.next.pre = pNode.pre; //当前节点的后继结点的前驱指向当前节点的前驱节点
return pNode;
}
return nil;
} //双向链表:遍历并打印链表
+(void)printFromHeadWithNode:(DoubleLinkNode *)headNode printPrefixText:(NSString *)text { //判空处理
if (!headNode) {
return;
} DoubleLinkNode *pNode = headNode.next;
NSMutableArray *items = [NSMutableArray array];
while (pNode!= nil) {
[items addObject:@(pNode.data)];
pNode = pNode.next;
}
NSLog(@"%@:%@",text,[items componentsJoinedByString:@"⇄"]); } @end

main方法

//
// main.m
// LinkList
//
// Created by 夏远全 on 2019/9/25.
// #import <Foundation/Foundation.h>
#import "FuncontionHandler.h" void testDoubleLink(void); int main(int argc, const char * argv[]) {
@autoreleasepool { testDoubleLink(); } return ;
} void testDoubleLink(void){ //1、构建一个双向链表
DoubleLinkNode *head = [[DoubleLinkNode alloc] init];
DoubleLinkNode *node1 = [DoubleLinkNode constructNodeWithData:];
DoubleLinkNode *node2 = [DoubleLinkNode constructNodeWithData:];
DoubleLinkNode *node3 = [DoubleLinkNode constructNodeWithData:];
head.next = node1;
node1.next = node2;
node1.pre = head;
node2.next = node3;
node2.pre = node1;
node3.pre = node2;
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"构造双向链表为"]; //2、从双向链表中插入节点
DoubleLinkNode *node4 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAfterHead:node4 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表头部插入节点4后"]; DoubleLinkNode *node5 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAfterTail:node5 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表尾部插入节点5后"]; DoubleLinkNode *node6 = [DoubleLinkNode constructNodeWithData:];
[FuncontionHandler insetNodeAtIndex: node:node6 headNode:head];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:@"在双向链表第2个位置插入节点6后"]; //3、删除节点
DoubleLinkNode *deleteNode = [FuncontionHandler deleteNodeAtIndex: headNode:head];
NSString *prefixText = [NSString stringWithFormat:@"删除第2个位置的节点%d后单链表为",deleteNode.data];
[FuncontionHandler printFromHeadWithNode:head printPrefixText:prefixText]; }

用OC实现双向链表:构造链表、插入节点、删除节点、遍历节点的更多相关文章

  1. 链表插入和删除,判断链表是否为空,求链表长度算法的,链表排序算法演示——C语言描述

    关于数据结构等的学习,以及学习算法的感想感悟,听了郝斌老师的数据结构课程,其中他也提到了学习数据结构的或者算法的一些个人见解,我觉的很好,对我的帮助也是很大,算法本就是令人头疼的问题,因为自己并没有学 ...

  2. 二叉搜索树Java实现(查找、插入、删除、遍历)

    由于最近想要阅读下 JDK1.8 中 HashMap 的具体实现,但是由于 HashMap 的实现中用到了红黑树,所以我觉得有必要先复习下红黑树的相关知识,所以写下这篇随笔备忘,有不对的地方请指出- ...

  3. 28_链表插入和删除算法的演示.swf

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

  4. c++ 搜索二叉树 插入,删除,遍历操作

    搜索二叉树是一种具有良好排序和查找性能的二叉树数据结构,包括多种操作,本篇只介绍插入,排序(遍历),和删除操作,重点是删除操作比较复杂,用到的例子也是本人亲自画的 用到的测试图数据例子 第一.构建节点 ...

  5. jaxp的dom方式操作(查找、添加、修改、删除、遍历节点)

    package cn.itcast.jaxptest; import java.io.IOException; import javax.xml.parsers.DocumentBuilder;imp ...

  6. 纯C语言实现循环双向链表创建,插入和删除

    #include <stdio.h> #include <stdlib.h> typedef int ElemType; typedef struct DLNode{ Elem ...

  7. 编写程序,实现在带头结点的单链表L中删除一个最小值节点的算法。

    算法复杂度0(n) #!/usr/bin/env python3 class LNode(object): def __init__(self, elem, next_=None): self.ele ...

  8. Java-二叉树-插入、删除、遍历

    二叉树的具体特性和细节知识点,自行百度,直接上代码. 节点:节点内容.左子孩子.右子孩子.父亲 class Node { private int data; private Node leftChil ...

  9. 数据结构Java实现03----单向链表的插入和删除

    文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定)            概念: 链式存储结构是基于指针实现的.我们把一个数据 ...

  10. 数据结构Java实现02----单向链表的插入和删除

    文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定)            概念: 链式存储结构是基于指针实现的.我们把一个数据 ...

随机推荐

  1. 入职小白随笔之Android四大组件——内容提供器详解(Content Provider)

    Content Provider 内容提供器简介 内容提供器(Content Provider)主要用于在不同的应用程序之间 实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的 ...

  2. 45-管理 Machine

    用 docker-machine 创建 machine 的过程很简洁,非常适合多主机环境.除此之外,Docker Machine 也提供了一些子命令方便对 machine 进行管理.其中最常用的就是无 ...

  3. PHP计算二维数组指定元素的和

    array_sum(array_column($arr, 'num')); //计算二维数组指定元素的和 $arr = [ [ 'id'=>1, 'num'=>3, ], [ 'id'=& ...

  4. SQL Server Agent作业执行CmdExec(bat)命令报权限问题

    写了一个bat命令,定期去清理一些SQL Server的Dump文件,然后配置成SQL Server作业,作业执行时报权限错误,具体错误信息如下所示: Message Executed as user ...

  5. 梁敬彬老师的《收获,不止SQL优化》,关于如何缩短SQL调优时间,给出了三个步骤,

    梁敬彬老师的<收获,不止SQL优化>,关于如何缩短SQL调优时间,给出了三个步骤, 1. 先获取有助调优的数据库整体信息 2. 快速获取SQL运行台前信息 3. 快速获取SQL关联幕后信息 ...

  6. SLES 12: Database Startup Error with ORA-27300 ORA-27301 ORA-27303 While Starting using Srvctl (Doc ID 2340986.1)

    SLES 12: Database Startup Error with ORA-27300 ORA-27301 ORA-27303 While Starting using Srvctl (Doc ...

  7. 用python暴力破解压缩文件并不是万能,至少这个场景我告诉你密码你用代码也破解不了

    看到论坛上各种贴子写用python进行暴力破解的文章,于是自己也想去尝试一下,不试不知道,一试吓一跳,真的就像那句有名的”python由入门到放弃“,把论坛上别人的脚本全部自己敲一遍,运行不报错,但也 ...

  8. 关于input标签不同type下的盒模型

    刚才发现,在Chrome下input标签的不同type类型所取的盒模型是不一样的.浪费了我很多时间去调试,唉. type="text"时,给它设置宽度width:300px,此时的 ...

  9. Redis开发与运维:特性

    Redis 特性 速度快 内存数据库 L1 cache reference 读取CPU的一级缓存 0.5 ns Branch mispredict (转移.分支预测) 5 ns L2 cache re ...

  10. synchronized&volatile

    synchronized(JVM实现的锁) 通过这两个关键字,我们可以很容易的实现同步多个任务的行为,可以实现同一时刻,只能有一条线程去访问共享资源 一: 修饰普通方法 多个线程,共同去竞争访问,方法 ...