一、介绍

双向链表:每一个节点前后指针域都和它的上一个节点互相指向,尾节点的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. python中 遇到的读取坑2.7和3.6版本的问题

    2.7读取,需要使用io.open 3.x使用open 使用io.open的时候路径需要使用\\ 目前io.open的文件名不能为中文

  2. QT Creator: The process could not be started!

    如果往工程里面增加了uac.manifest 文件后,QT creator不通过管理员启动的话,若要debug程序的话,就会提示 “The process could not be started!” ...

  3. 30(1).原型聚类---k-means

    原型聚类prototype-based clustering假设聚类结构能通过一组原型刻画. 常见的原型聚类有: k均值算法k-means 学习向量量化算法Learning Vector Quanti ...

  4. 你真的理解Java 注解吗?

    你真的理解Java 注解吗? 1.什么是注解? 官方解释: Java 注解用于为 Java 代码提供元数据.作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的.Java ...

  5. DNS解析服务结构图

    1.DNS(domain name system) 域名 <==> IP地址 DNS解析过程:

  6. Java中间消息件——ActiveMQ入门级运用

    先来说一说我们为什么要用这个东西啊! 比如,我们现在有这样了个问题要解决: 这样,我们就要用到中间消息间了 然后我们就说一下什么是中间消息间吧. 采用消息传送机制/消息队列 的中间件技术,进行数据交流 ...

  7. [Spring cloud 一步步实现广告系统] 5. 投放系统配置+启动+实体类

    广告投放系统启动主类说明 /** * SponsorApplication for 广告赞助商/投递服务启动类 * 添加注解{@link EnableFeignClients}之后,当前微服务就可以调 ...

  8. 基于SSM的crm管理系统

    学完crm后的第一个项目实践,前端样式与标签库都是现成的. 开发环境 eclipse,mysql,jdk1.7 项目架构 整合思路 Dao层: 1.SqlMapConfig.xml,添加别名,但是需要 ...

  9. Sql sever DateDiff 函数

    函数: DATEDIFF(datepart,startdate,enddate) 具体实例: --相差年数 结果0 SELECT DATEDIFF(yy,'2008-12-29','2008-12-1 ...

  10. 关于scrapy中scrapy.Request中的属性

    一.源码 def __init__(self, url, callback=None, method='GET', headers=None, body=None, cookies=None, met ...