用OC实现双向链表:构造链表、插入节点、删除节点、遍历节点
一、介绍
双向链表:每一个节点前后指针域都和它的上一个节点互相指向,尾节点的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实现双向链表:构造链表、插入节点、删除节点、遍历节点的更多相关文章
- 链表插入和删除,判断链表是否为空,求链表长度算法的,链表排序算法演示——C语言描述
关于数据结构等的学习,以及学习算法的感想感悟,听了郝斌老师的数据结构课程,其中他也提到了学习数据结构的或者算法的一些个人见解,我觉的很好,对我的帮助也是很大,算法本就是令人头疼的问题,因为自己并没有学 ...
- 二叉搜索树Java实现(查找、插入、删除、遍历)
由于最近想要阅读下 JDK1.8 中 HashMap 的具体实现,但是由于 HashMap 的实现中用到了红黑树,所以我觉得有必要先复习下红黑树的相关知识,所以写下这篇随笔备忘,有不对的地方请指出- ...
- 28_链表插入和删除算法的演示.swf
#include<stdio.h> #include<malloc.h> #include <stdio.h> #include <stdlib.h> ...
- c++ 搜索二叉树 插入,删除,遍历操作
搜索二叉树是一种具有良好排序和查找性能的二叉树数据结构,包括多种操作,本篇只介绍插入,排序(遍历),和删除操作,重点是删除操作比较复杂,用到的例子也是本人亲自画的 用到的测试图数据例子 第一.构建节点 ...
- jaxp的dom方式操作(查找、添加、修改、删除、遍历节点)
package cn.itcast.jaxptest; import java.io.IOException; import javax.xml.parsers.DocumentBuilder;imp ...
- 纯C语言实现循环双向链表创建,插入和删除
#include <stdio.h> #include <stdlib.h> typedef int ElemType; typedef struct DLNode{ Elem ...
- 编写程序,实现在带头结点的单链表L中删除一个最小值节点的算法。
算法复杂度0(n) #!/usr/bin/env python3 class LNode(object): def __init__(self, elem, next_=None): self.ele ...
- Java-二叉树-插入、删除、遍历
二叉树的具体特性和细节知识点,自行百度,直接上代码. 节点:节点内容.左子孩子.右子孩子.父亲 class Node { private int data; private Node leftChil ...
- 数据结构Java实现03----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
- 数据结构Java实现02----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
随机推荐
- 阿里云RDS for SQL Serrver关于权限的一个严重Bug
阿里云RDS for SQL Server的账号管理有不少小Bug,而且有一个很严重的Bug:任何普通账号,都能创建数据库.注意,我这里是说任意普通账号,任意任意普通账号!任意任意普通账号!重要的事情 ...
- python将字符串插入表中避免单双引号问题
调用pymysql.escape_string('向数据库插入的数据') 例如: import pymysql str = 'as"sdf' print(pymysql.escape_str ...
- 官方版vs2008至vs2013下载地址
Visual Studio 2005 Professional 官方90天试用版 英文版:http://download.microsoft.com/download/e/0/4/e04de840-8 ...
- 【tf.keras】TensorFlow 1.x 到 2.0 的 API 变化
TensorFlow 2.0 版本将 keras 作为高级 API,对于 keras boy/girl 来说,这就很友好了.tf.keras 从 1.x 版本迁移到 2.0 版本,需要修改几个地方. ...
- pytest系列(二):筛选用例新姿势,mark 一下,你就知道。
pytest系列(一)中给大家介绍了pytest的特性,以及它的编写用例的简单至极. 那么在实际工作当中呢,我们要写的自动化用例会比较多,不会都放在一个py文件里. 如下图所示,我们编写的用例存放在不 ...
- Web安全测试学习笔记-DVWA-存储型XSS
XSS(Cross-Site Scripting)大致分为反射型和存储型两种,之前对XSS的认知仅停留在如果网站输入框没有屏蔽类似<script>alert('ok')</scrip ...
- 禧云Redis跨机房双向同步实践
编者荐语: 2019年4月16日跨机房Redis同步中间件(Rotter)上线,团餐率先商用: 以下文章来源于云纵达摩院 ,作者杨海波 禧云信息/研发中心/杨海波 20191115 关键词:Rot ...
- GitLab CI/CD持续集成设置
GitLab CI/CD持续设置 官方文档地址(https://docs.gitlab.com/ee/ci/README.html) GitLab CI.CD功能非常完善,只需要简单几步,就可以完成项 ...
- Python中经典排序方法
数据的排序是在解决实际问题时经常用到的步骤,也是数据结构的考点之一,下面介绍10种经典的排序方法. 首先,排序方法可以大体分为插入排序.选择排序.交换排序.归并排序和桶排序四大类,其中,插入排序又分为 ...
- GO-逻辑判断(if,else if,else,switch)
一.if逻辑判断 package main import "fmt" func main() { var a =10; if a>10 { //大括号前不能回车 fmt.Pr ...