用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----单向链表的插入和删除
文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构: (物理存储结构上不连续,逻辑上连续:大小不固定) 概念: 链式存储结构是基于指针实现的.我们把一个数据 ...
随机推荐
- Android五大布局详解——LinearLayout(线性布局)
Android五大布局 本篇开始介绍Android的五大布局的知识,一个丰富的界面显示总是要有众多的控件来组成的,那么怎样才能让这些控件能够按你的想法进行摆放,从而自定义你所想要的用户界面呢?这就牵涉 ...
- 初级模拟电路:4-1 BJT交流分析概述
回到目录 BJT晶体管的交流分析(也叫小信号分析)是模拟电路中的一个难点,也可以说是模电中的一个分水岭.如果你能够把BJT交流分析的原理全都搞懂,那之后的学习就是一马平川了.后面的大部分内容,诸如:场 ...
- Keystone安装与配置
一.实验目的: 1.掌握OpenStack环境搭建的基础工作 2.掌握keystone的安装与配置方法 3.掌握keystone基础接口的调用方法 二.实验步骤: 1.利用最初创建的快照克隆两台Cen ...
- ResultSet RS_resultxtgg=connDbBean.executeQuery(sqlxtgg);
<%String sqlxtgg="select * from dx where leibie='系统公告'"; ResultSet RS_resultxtgg=connDb ...
- 《Google软件测试之道》
Google软件测试之道 Google对质量的理解 质量不等于测试,即质量不是被测出来的 开发和测试应该并肩齐驱,测试就是开发过程中不可缺少的一部分 质量是一种预防行为而不是检测 Google对软件测 ...
- 剑指Offer-45.扑克牌顺子(C++/Java)
题目: LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定 ...
- if(response.isSuccess){}else{}的方式,如果我们由于忽略没有设置success字段的值,就可能导致
在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的. 关于这个”本次请求是否成功”的字段的定义,其实是有很多种讲 ...
- MongoDB数据库常用SQL命令 — MongoDB可视化工具Robo 3T
1.db.collection.updateMany() 修改集合中的多个文档. db.getCollection('user').find({"pId":"3332a5 ...
- 通过idea将maven工程转为web项目
前言 吐槽一下网上的各种转换教程..说的真的是吵来吵去,有用的东西极少.特此自己写一篇好使的. 转换过程 建好的maven工程 建好的maven工程长这个鬼样子~~,没有使用骨架.就是普通的建立方式. ...
- 动态SQL与变量绑定
有时候动态sql需要进行变量的赋值,这个时候就需要调用系统的存储过程sp_executesql了.使用中还是有些注意事项,代码如下: --字符型字段需声明为NVARCHAR类型 ),) --动态SQL ...