使用OC实现单链表:创建、删除、插入、查询、遍历、反转、合并、判断相交、求成环入口

一、概念

链表和数组都是一种线性结构,数组有序存储的,链表是无序存储的。

数组中的每一个元素地址是递增或者递减的关系,链表的每一个节点的地址没有此规律,它们是通过指针的指向连接起来。

链表种类:单链表、双向链表、循环链表、双向循环链表

单链表:一个数据域data、一个后继指针域next。也即:上一个节点指向下一个节点,尾节点指向空。

双向链表:一个数据域data、一个前驱指针域previous、一个后继指针域next。也即:上一个节点和下一个节点互相指向,尾节点指向空。

循环链表:一个数据域data、一个后继指针域next。也即:上一个节点指向下一个节点,尾节点指向首节点。

双向循环链表:一个数据域data、一个前驱指针域previous、一个后继指针域next。也即:上一个节点和下一个节点互相指向,尾节点和首节点也互相指向。

如图: 

二、实现 

1、定义节点:

//  SingleLinkNode.h
//  LinkListDemo
//  Created by 夏远全 on 2019/9/24.

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//单链表节点
@interface SingleLinkNode : NSObject
@property (nonatomic, assign) int data; //数据域
@property (nonatomic, strong, nullable) SingleLinkNode *next;//后继指针域
+(instancetype)constructNodeWithData:(int)data;
@end
//  SingleLinkNode.m
//  LinkListDemo
//  Created by 夏远全 on 2019/9/24.

#import "SingleLinkNode.h"

@implementation SingleLinkNode

+(instancetype)constructNodeWithData:(int)data{
    SingleLinkNode *node = [[SingleLinkNode alloc] init];
    node.data = data;
    node.next = nil;
    return node;
}
@end

2、构建链表(遍历打印方法在下面)

//1、构建一个单链表
SingleLinkNode *head = [[SingleLinkNode alloc] init];
SingleLinkNode *node1 = [SingleLinkNode constructNodeWithData:1];
SingleLinkNode *node2 = [SingleLinkNode constructNodeWithData:2];
SingleLinkNode *node3 = [SingleLinkNode constructNodeWithData:3];
head.next = node1;
node1.next = node2;
node2.next = node3;
[FuncontionHelper printFromHeadWithNode:head printPrefixText:@"构造单链表为"];
2019-09-27 11:07:10.363408+0800 LinkList[36953:1729163] 构造单链表为:1->2->3

3、插入节点

3-1:在头部插入

//在头部插入节点
+(void)insetNodeAfterHead:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {

    //判空处理
    if (!headNode) {
        return;
    }
    
    /*
    //方式一:先用一个临时节点记录头结点的下一个节点,然后将头结点指向新节点,新节点再指向临时节点
    if (headNode.next == nil) {
        headNode.next = newNode;
    }else{
        SingleLinkNode *tempNode = headNode.next;
        headNode.next = newNode;
        newNode.next = tempNode;
    }
    */
    
    //方式二:先把新节点指向头结点的下一个节点,再让头结点指向新节点(比较常用)
    if (headNode.next == nil) {
        headNode.next = newNode;
    }else{
        newNode.next = headNode.next;
        headNode.next = newNode;
    }
}
SingleLinkNode *node4 = [SingleLinkNode constructNodeWithData:4];
[FuncontionHelper insetNodeAfterHead:node4 headNode:head];
[FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表头部插入节点4后"];
2019-09-27 11:07:10.363732+0800 LinkList[36953:1729163] 在单链表头部插入节点4后:4->1->2->3   //1->2->3

3-2:在尾部插入

//在尾部插入节点
+(void)insetNodeAfterTail:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode) {
        return;
    }
    if (headNode.next == nil) {
        headNode.next = newNode;
    }
    else{
        SingleLinkNode *pNode = headNode;
        while (pNode.next != nil) { //遍历到尾部
            pNode = pNode.next;
        }
        pNode.next = newNode;
    }
}
SingleLinkNode *node5 = [SingleLinkNode constructNodeWithData:5];
[FuncontionHelper insetNodeAfterTail:node5 headNode:head];
[FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表尾部插入节点5后"];
2019-09-27 11:07:10.363816+0800 LinkList[36953:1729163] 在单链表尾部插入节点5后:4->1->2->3->5   //4->1->2->3

3-3:在指定位置插入

//在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode) {
        return;
    }
    
    if (headNode.next == nil) {
        headNode.next = newNode;
    }
    else{
        SingleLinkNode *pNode = headNode;
        int i = 1;
        while (i < k && pNode.next != nil) {
            pNode = pNode.next;
            i++;
        }
        newNode.next = pNode.next;
        pNode.next = newNode;
    }
}
SingleLinkNode *node6 = [SingleLinkNode constructNodeWithData:6];
[FuncontionHelper insetNodeAtIndex:2 node:node6 headNode:head];
[FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表第2个位置插入节点6后"];
2019-09-27 11:07:10.363872+0800 LinkList[36953:1729163] 在单链表第2个位置插入节点6后:4->6->1->2->3->5  //4->1->2->3->5

4、删除节点

//单链表:删除第k个位置的节点
+(SingleLinkNode *)deleteNodeAtIndex:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next || k==0) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有要删除的第%d个节点!",k]);
        return nil;
    }
    
    SingleLinkNode *pNode = headNode;
    SingleLinkNode *p = pNode;//移动指针
    int i = 0;
    while (pNode.next != nil && i < k) {
        p = pNode;
        pNode = pNode.next;
        i++;
    }
    if (pNode != nil) {
        p.next = p.next.next;
        return pNode;
    }
    
    NSLog(@"%@",[NSString stringWithFormat:@"没有要删除的第%d个节点!",k]);
    return nil;
}
SingleLinkNode *deleteNode = [FuncontionHelper deleteNodeAtIndex:1 headNode:head];
NSString *prefixText = [NSString stringWithFormat:@"删除第1个位置的节点%d后单链表为",deleteNode.data];
[FuncontionHelper printFromHeadWithNode:head printPrefixText:prefixText];
2019-09-27 11:07:10.363902+0800 LinkList[36953:1729163] 删除第1个位置的节点4后单链表为:6->1->2->3->5  //4->6->1->2->3->5

5、查询节点

5-1:正向查询

//单链表:查询第k个位置的节点
+(SingleLinkNode *)queryNodeIndex:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到第%d个节点!",k]);
        return nil;
    }
    
    SingleLinkNode *pNode = headNode.next;
    int i = 1;
    while (pNode != nil && i < k) {
        pNode = pNode.next;
        i++;
    }
    if (pNode != nil) {
        return pNode;
    }
    NSLog(@"%@",[NSString stringWithFormat:@"没有查询到第%d个节点!",k]);
    return nil;
}
SingleLinkNode *node_x = [FuncontionHelper queryNodeIndex:1 headNode:head];
if (node_x) NSLog(@"查询到第1个结点的值是:%d",node_x.data); 
2019-09-27 11:07:10.363916+0800 LinkList[36953:1729163] 查询到第1个结点的值是:6     //6->1->2->3->5

5-2:反向查询(两次遍历法、快慢指针法)

//单链表:查询倒数第k个位置的节点
+(SingleLinkNode *)queryNodeCountdown:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
        return nil;
    }
    
    /*
    //方式一:两次遍历法:第一次遍历获取链表总长度L,第二次遍历区间为[1,L-k+1]即可
    int L = 0;
    SingleLinkNode *pNode = headNode.next;
    while (pNode != nil) {
        pNode = pNode.next;
        L++;
    }
    if (k > L) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
        return nil;
    }
    pNode = headNode.next;
    for (int i=1; i<L-k+1; i++) {
       pNode = pNode.next;
    }
    return pNode;
    */
    
    //方式二:使用快慢指针法,快指针先走k步,然后快慢指针一起走,等到快指针结束时,慢指针所在位置就是目标节点
    SingleLinkNode *quickNode = headNode.next;
    SingleLinkNode *slowNode = headNode.next;
    int i = 0;
    while (quickNode != nil && i<k) {
        quickNode = quickNode.next;
        i++;
    }
    if (i == k) {
        while (quickNode != nil && slowNode != nil) {
            quickNode = quickNode.next;
            slowNode  = slowNode.next;
        }
        if (quickNode == nil && slowNode != nil) {
            return slowNode;
        }else{
            NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
            return nil;
        }
    }
    NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
    return nil;
}
SingleLinkNode *node_y = [FuncontionHelper queryNodeCountdown:1 headNode:head];
if (node_y) NSLog(@"查询到倒数第1个结点的值是:%d",node_y.data);
2019-09-27 11:07:10.363934+0800 LinkList[36953:1729163] 查询到倒数第1个结点的值是:5 //6->1->2->3->5

6、遍历链表

6-1:正向遍历

//正序遍历链表
+(NSArray<NSNumber *>*)printFromHeadWithNode:(SingleLinkNode *)headNode printPrefixText:(NSString *)text {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    SingleLinkNode *pNode = headNode.next;
    NSMutableArray *items = [NSMutableArray array];
    while (pNode!= nil) {
        [items addObject:@(pNode.data)];
        pNode = pNode.next;
    }
    NSLog(@"%@:%@",text,[items componentsJoinedByString:@"->"]);
    return items;
}

6-2:反向遍历(递归法、指针法)

//倒序遍历链表
+(NSArray<NSNumber *>*)printFromTailWithNode:(SingleLinkNode *)headNode{
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //方式一:递归打印
    /*
    NSMutableArray *items = [NSMutableArray array];
    if (headNode.next != nil) {
        headNode = headNode.next;
        [self printFromTailWithNode:headNode];
        [items addObject:@(headNode.data)];
    }
    return items;
    */
    
    //方式二:遍历指针偏移,每次遍历完一次后,要记录最后一个节点,然后将遍历指针移动到开头重新开始,与记录的最后一个节点作比较
    NSMutableArray *items = [NSMutableArray array];
    SingleLinkNode *pNode = headNode;
    SingleLinkNode *lastNode = nil;
    while (pNode != nil && lastNode != pNode) {
        pNode = pNode.next;
        if (pNode.next == nil || pNode.next == lastNode) {
            lastNode = pNode;
            pNode = headNode;
            [items addObject:@(lastNode.data)];
        }
    }
    return items;
}
NSArray *items = [FuncontionHelper printFromTailWithNode:head];
NSLog(@"链表倒序遍历:%@",[items componentsJoinedByString:@""]);
2019-09-27 11:07:10.363962+0800 LinkList[36953:1729163] 链表倒序遍历:53216    //6->1->2->3->5

7、反转链表

//反转链表
+(SingleLinkNode *)reverseWithNode:(SingleLinkNode *)headNode{
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //采用头结点插入的方式反转
    SingleLinkNode *p = headNode.next; ///定义遍历指针
    SingleLinkNode *newHead = [[SingleLinkNode alloc] init]; ///定义反转后头结点
    while (p != nil) {
        SingleLinkNode *temp = p.next;///记录下一个节点
        p.next = newHead.next; ///当前节点指向新头结点的下一个节点
        newHead.next = p; ///更换新头结点指向当前节点
        p = temp; ///移动p指针
    }
    return newHead;
}
SingleLinkNode *newHead = [FuncontionHelper reverseWithNode:head];
[FuncontionHelper printFromHeadWithNode:newHead printPrefixText:@"将单链表反转后"];
2019-09-27 11:07:10.363999+0800 LinkList[36953:1729163] 将单链表反转后:5->3->2->1->6   //6->1->2->3->5

8、合并有序链表

//单链表:两个有序链表合成一个新的有序链表
+(SingleLinkNode *)combineWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return otherNode;
    }
    if (!otherNode || !otherNode.next) {
        return headNode;
    }
    
    //一起开始扫描
    SingleLinkNode *p1 = headNode.next;
    SingleLinkNode *p2 = otherNode.next;
    SingleLinkNode *newHead = [[SingleLinkNode alloc] init];//定义一个新头结点
    while (p1 != nil && p2 != nil) {
        if (p1.data > p2.data) {
            otherNode.next = p2.next; //移动otherNode头结点指向当前节点的下一个节点
            p2.next = nil;//将当前节点从otherNode链表断掉
            [self insetNodeAfterTail:p2 headNode:newHead];//将当前节点插入到新链表newHead的尾部
            p2 = otherNode.next;//获取otherNode链表的下一点节点
        }else{
            headNode.next = p1.next;
            p1.next = nil;
            [self insetNodeAfterTail:p1 headNode:newHead];
            p1 = headNode.next;
        }
    }
    
    //处理没有扫描结束的链表
    while (p1 != nil) {
        headNode.next = p1.next;
        p1.next = nil;
        [self insetNodeAfterTail:p1 headNode:newHead];//尾节点插入
        p1 = headNode.next;
    }
    while (p2 != nil) {
        otherNode.next = p2.next;
        p2.next = nil;
        [self insetNodeAfterTail:p2 headNode:newHead];//尾节点插入
        p2 = otherNode.next;
    }
    return newHead;
}
SingleLinkNode *head1 = [[SingleLinkNode alloc] init];
SingleLinkNode *node11 = [SingleLinkNode constructNodeWithData:1];
SingleLinkNode *node22 = [SingleLinkNode constructNodeWithData:3];
SingleLinkNode *node33 = [SingleLinkNode constructNodeWithData:5];
head1.next = node11;
node11.next = node22;
node22.next = node33;
[FuncontionHelper printFromHeadWithNode:head1 printPrefixText:@"构造第一个有序单链表为:"];

SingleLinkNode *otherHead = [[SingleLinkNode alloc] init];
SingleLinkNode *node44 = [SingleLinkNode constructNodeWithData:2];
SingleLinkNode *node55 = [SingleLinkNode constructNodeWithData:4];
SingleLinkNode *node66 = [SingleLinkNode constructNodeWithData:6];
SingleLinkNode *node77 = [SingleLinkNode constructNodeWithData:8];
otherHead.next = node44;
node44.next = node55;
node55.next = node66;
node66.next = node77;
[FuncontionHelper printFromHeadWithNode:otherHead printPrefixText:@"构造第二个有序单链表为:"];

SingleLinkNode *combieHeadNode = [FuncontionHelper combineWithNode:head1 otherNode:otherHead];
[FuncontionHelper printFromHeadWithNode:combieHeadNode printPrefixText:@"合并后的有序新链表为:"];
2019-09-27 11:07:10.364037+0800 LinkList[36953:1729163] 构造第一个有序单链表为::1->3->5
2019-09-27 11:07:10.364067+0800 LinkList[36953:1729163] 构造第二个有序单链表为::2->4->6->8
2019-09-27 11:07:10.364098+0800 LinkList[36953:1729163] 合并后的有序新链表为::1->2->3->4->5->6->8

9、判断两个链表是否相交

//单链表:判断两个链表是否相交 (只能是某一个链表的尾节点与另一链表的某一个节点相交)
+(BOOL)intersectWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode {
    
    //判空处理
    if (!headNode || !headNode.next || !otherNode || !otherNode.next) {
        return NO;
    }

    //思路:分别获取两个链表的长度,判断谁的链表更长,链表更长的先走完相差的步数,然后齐步走
    SingleLinkNode *p1 = headNode.next;
    SingleLinkNode *p2 = otherNode.next;
    int L1 = 1;
    int L2 = 1;
    while (p1 != nil) {
        L1 ++;
        p1 = p1.next;
    }
    while (p2 != nil) {
        L2 ++;
        p2 = p2.next;
    }
    p1 = headNode.next;  //将p1遍历指针移动到首节点
    p2 = otherNode.next; //将p2遍历指针移动到首节点
    int i=0;
    if (L1 > L2) {
        while (i < L1-L2 && p1 != nil) { //p1先走
            p1 = p1.next;
            i++;
        }
    }else{
        while (i < L2-L1 && p2 != nil) { //p2先走
            p2 = p2.next;
            i++;
        }
    }
    if (i == ABS(L1-L2)) {
        while (p1 != nil && p2 != nil) { //p1、p2齐步走
            if(p1.next == p2.next) return YES; //相交
            p1 = p1.next;
            p2 = p2.next;
        }
    }
    return NO;
}
SingleLinkNode *head2 = [[SingleLinkNode alloc] init];
SingleLinkNode *node111 = [SingleLinkNode constructNodeWithData:1];
SingleLinkNode *node222 = [SingleLinkNode constructNodeWithData:3];
SingleLinkNode *node333 = [SingleLinkNode constructNodeWithData:5];
head2.next = node111;
node111.next = node222;
node222.next = node333;
[FuncontionHelper printFromHeadWithNode:head2 printPrefixText:@"单链表1"];
        
SingleLinkNode *otherHead2 = [[SingleLinkNode alloc] init];
SingleLinkNode *node444 = [SingleLinkNode constructNodeWithData:2];
SingleLinkNode *node555 = [SingleLinkNode constructNodeWithData:4];
SingleLinkNode *node666 = [SingleLinkNode constructNodeWithData:6];
SingleLinkNode *node777 = [SingleLinkNode constructNodeWithData:8];
otherHead2.next = node444;
node444.next = node555;
node555.next = node666;
node666.next = node777;
node777.next = node222;//指向链表1的node222节点
[FuncontionHelper printFromHeadWithNode:otherHead2 printPrefixText:@"单链表2"];
        
BOOL isIntersect = [FuncontionHelper intersectWithNode:head2 otherNode:otherHead2];
NSLog(@"单链表1和单链表2是否相交:%@", isIntersect ? @"相交" : @"不相交");
2019-09-27 11:07:10.364154+0800 LinkList[36953:1729163] 单链表1:1->3->5
2019-09-27 11:07:10.364189+0800 LinkList[36953:1729163] 单链表2:2->4->6->8->3->5
2019-09-27 11:07:10.364201+0800 LinkList[36953:1729163] 单链表1和单链表2是否相交:相交  //相交点从3开始

10、判断链表是否构成环,如果成环,求出环的入口节点

//单链表:判断链表是否构成环,如果成环,返回构成环的那个节点
+(SingleLinkNode *)circleWithNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //思路:采用快慢指针法
    //快指针先走两步,慢指针走一步,如果成环,必然重合。走到第一次重合的地点后,重新设置一个指针p指向头结点,并与慢节点同步伐齐步走,
    //走到第二次相遇的地方,即为构成环的节点
    SingleLinkNode *quick = headNode.next;
    SingleLinkNode *slow  = headNode.next;
    SingleLinkNode *p     = headNode.next;
    while (quick != nil && slow != nil) {
        quick = quick.next.next;
        slow = slow.next;
        if (quick == slow) { //第一次重合,结束循环
            break;
        }
    }
    while (p != nil && slow != nil) {
        p = p.next;
        slow = slow.next;
        if (p == slow) { //第二次重合,找到成环的入口节点
            return p;
        }
    }
    return nil;
}
SingleLinkNode *head3 = [[SingleLinkNode alloc] init];
SingleLinkNode *node1111 = [SingleLinkNode constructNodeWithData:1];
SingleLinkNode *node2222 = [SingleLinkNode constructNodeWithData:3];
SingleLinkNode *node3333 = [SingleLinkNode constructNodeWithData:4];
SingleLinkNode *node4444 = [SingleLinkNode constructNodeWithData:5];
SingleLinkNode *node5555 = [SingleLinkNode constructNodeWithData:6];
SingleLinkNode *node6666 = [SingleLinkNode constructNodeWithData:7];
head3.next = node1111;
node1111.next = node2222;
node2222.next = node3333;
node3333.next = node4444;
node4444.next = node5555;
node5555.next = node6666;
node6666.next = node2222;
        /*
              1 -> 3 -> 4 -> 5
                           /
                     7 -  6
        */
SingleLinkNode *circleNode = [FuncontionHelper circleWithNode:head3];
NSLog(@"链表是否成环:%@, 成环的入口节点是%d:", circleNode != nil ? @"成环" : @"不成环", circleNode.data);
2019-09-27 11:07:10.364225+0800 LinkList[36953:1729163] 链表是否成环:成环, 成环的入口节点是3:

三、源码

FuncontionHelper.h

//
//  FuncontionHelper.h
//  LinkList
//
//  Created by 夏远全 on 2019/9/25.
//

#import <Foundation/Foundation.h>
#import "SingleLinkNode.h"
#import "SingleCycleLinkNode.h"
#import "DoubleLinkNode.h"
#import "DoubleCycleLinkNode.h"

NS_ASSUME_NONNULL_BEGIN

@interface FuncontionHelper : NSObject

//单链表:在头部插入节点
+(void)insetNodeAfterHead:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode;

//单链表:在尾部插入节点
+(void)insetNodeAfterTail:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode;

//单链表:在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode;

//单链表:删除第k个位置的节点
+(SingleLinkNode *)deleteNodeAtIndex:(int)k headNode:(SingleLinkNode *)headNode;

//单链表:查询第k个位置的节点
+(SingleLinkNode *)queryNodeIndex:(int)k headNode:(SingleLinkNode *)headNode;

//单链表:查询倒数第k个位置的节点
+(SingleLinkNode *)queryNodeCountdown:(int)k headNode:(SingleLinkNode *)headNode;

//单链表:正序遍历链表
+(NSArray<NSNumber *>*)printFromHeadWithNode:(SingleLinkNode *)headNode printPrefixText:(NSString *)text;

//单链表:倒序遍历链表
+(NSArray<NSNumber *>*)printFromTailWithNode:(SingleLinkNode *)headNode;

//单链表:反转链表
+(SingleLinkNode *)reverseWithNode:(SingleLinkNode *)headNode;

//单链表:两个有序链表合成一个新的有序链表
+(SingleLinkNode *)combineWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode;

//单链表:判断两个链表是否相交
+(BOOL)intersectWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode;

//单链表:判断链表是否构成环,如果成环,返回构成环的那个节点
+(SingleLinkNode *)circleWithNode:(SingleLinkNode *)headNode;

@end

NS_ASSUME_NONNULL_END
View Code

FuncontionHelper.m

//
//  FuncontionHelper.m
//  LinkList
//
//  Created by 夏远全 on 2019/9/25.
//

#import "FuncontionHelper.h"

@implementation FuncontionHelper


//在头部插入节点
+(void)insetNodeAfterHead:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {

    //判空处理
    if (!headNode) {
        return;
    }
    
    /*
    //方式一:先用一个临时节点记录头结点的下一个节点,然后将头结点指向新节点,新节点再指向临时节点
    if (headNode.next == nil) {
        headNode.next = newNode;
    }else{
        SingleLinkNode *tempNode = headNode.next;
        headNode.next = newNode;
        newNode.next = tempNode;
    }
    */
    
    //方式二:先把新节点指向头结点的下一个节点,再让头结点指向新节点(比较常用)
    if (headNode.next == nil) {
        headNode.next = newNode;
    }else{
        newNode.next = headNode.next;
        headNode.next = newNode;
    }
}

//在尾部插入节点
+(void)insetNodeAfterTail:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode) {
        return;
    }
    if (headNode.next == nil) {
        headNode.next = newNode;
    }
    else{
        SingleLinkNode *pNode = headNode;
        while (pNode.next != nil) {
            pNode = pNode.next;
        }
        pNode.next = newNode;
    }
}

//在指定位置插入节点
+(void)insetNodeAtIndex:(int)k node:(SingleLinkNode *)newNode headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode) {
        return;
    }
    
    if (headNode.next == nil) {
        headNode.next = newNode;
    }
    else{
        SingleLinkNode *pNode = headNode;
        int i = 1;
        while (i < k && pNode.next != nil) {
            pNode = pNode.next;
            i++;
        }
        newNode.next = pNode.next;
        pNode.next = newNode;
    }
}

//单链表:删除第k个位置的节点
+(SingleLinkNode *)deleteNodeAtIndex:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next || k==0) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有要删除的第%d个节点!",k]);
        return nil;
    }
    
    SingleLinkNode *pNode = headNode;
    SingleLinkNode *p = pNode;//移动指针
    int i = 0;
    while (pNode.next != nil && i < k) {
        p = pNode;
        pNode = pNode.next;
        i++;
    }
    if (pNode != nil) {
        p.next = p.next.next;
        return pNode;
    }
    
    NSLog(@"%@",[NSString stringWithFormat:@"没有要删除的第%d个节点!",k]);
    return nil;
}

//单链表:查询第k个位置的节点
+(SingleLinkNode *)queryNodeIndex:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到第%d个节点!",k]);
        return nil;
    }
    
    SingleLinkNode *pNode = headNode.next;
    int i = 1;
    while (pNode != nil && i < k) {
        pNode = pNode.next;
        i++;
    }
    if (pNode != nil) {
        return pNode;
    }
    NSLog(@"%@",[NSString stringWithFormat:@"没有查询到第%d个节点!",k]);
    return nil;
}

//单链表:查询倒数第k个位置的节点
+(SingleLinkNode *)queryNodeCountdown:(int)k headNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
        return nil;
    }
    
    /*
    //方式一:两次遍历法:第一次遍历获取链表总长度L,第二次遍历区间为[1,L-k+1]即可
    int L = 0;
    SingleLinkNode *pNode = headNode.next;
    while (pNode != nil) {
        pNode = pNode.next;
        L++;
    }
    if (k > L) {
        NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
        return nil;
    }
    pNode = headNode.next;
    for (int i=1; i<L-k+1; i++) {
       pNode = pNode.next;
    }
    return pNode;
    */
    
    //方式二:使用快慢指针法,快指针先走k步,然后快慢指针一起走,等到快指针结束时,慢指针所在位置就是目标节点
    SingleLinkNode *quickNode = headNode.next;
    SingleLinkNode *slowNode = headNode.next;
    int i = 0;
    while (quickNode != nil && i<k) {
        quickNode = quickNode.next;
        i++;
    }
    if (i == k) {
        while (quickNode != nil && slowNode != nil) {
            quickNode = quickNode.next;
            slowNode  = slowNode.next;
        }
        if (quickNode == nil && slowNode != nil) {
            return slowNode;
        }else{
            NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
            return nil;
        }
    }
    NSLog(@"%@",[NSString stringWithFormat:@"没有查询到倒数第%d个节点!",k]);
    return nil;
}

//正序遍历链表
+(NSArray<NSNumber *>*)printFromHeadWithNode:(SingleLinkNode *)headNode printPrefixText:(NSString *)text {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    SingleLinkNode *pNode = headNode.next;
    NSMutableArray *items = [NSMutableArray array];
    while (pNode!= nil) {
        [items addObject:@(pNode.data)];
        pNode = pNode.next;
    }
    NSLog(@"%@:%@",text,[items componentsJoinedByString:@"->"]);
    return items;
}

//倒序遍历链表
+(NSArray<NSNumber *>*)printFromTailWithNode:(SingleLinkNode *)headNode{
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //方式一:递归打印
    /*
    NSMutableArray *items = [NSMutableArray array];
    if (headNode.next != nil) {
        headNode = headNode.next;
        [self printFromTailWithNode:headNode];
        [items addObject:@(headNode.data)];
    }
    return items;
    */
    
    //方式二:遍历指针偏移,每次遍历完一次后,要记录最后一个节点,然后将遍历指针移动到开头重新开始,与记录的最后一个节点作比较
    NSMutableArray *items = [NSMutableArray array];
    SingleLinkNode *pNode = headNode;
    SingleLinkNode *lastNode = nil;
    while (pNode != nil && lastNode != pNode) {
        pNode = pNode.next;
        if (pNode.next == nil || pNode.next == lastNode) {
            lastNode = pNode;
            pNode = headNode;
            [items addObject:@(lastNode.data)];
        }
    }
    return items;
}

//反转链表
+(SingleLinkNode *)reverseWithNode:(SingleLinkNode *)headNode{
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //采用头结点插入的方式反转
    SingleLinkNode *p = headNode.next; ///定义遍历指针
    SingleLinkNode *newHead = [[SingleLinkNode alloc] init]; ///定义反转后头结点
    while (p != nil) {
        SingleLinkNode *temp = p.next;///记录下一个节点
        p.next = newHead.next; ///当前节点指向新头结点的下一个节点
        newHead.next = p; ///更换新头结点指向当前节点
        p = temp; ///移动p指针
    }
    return newHead;
}

//单链表:两个有序链表合成一个新的有序链表
+(SingleLinkNode *)combineWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return otherNode;
    }
    if (!otherNode || !otherNode.next) {
        return headNode;
    }
    
    //一起开始扫描
    SingleLinkNode *p1 = headNode.next;
    SingleLinkNode *p2 = otherNode.next;
    SingleLinkNode *newHead = [[SingleLinkNode alloc] init];//定义一个新头结点
    while (p1 != nil && p2 != nil) {
        if (p1.data > p2.data) {
            otherNode.next = p2.next; //移动otherNode头结点指向当前节点的下一个节点
            p2.next = nil;//将当前节点从otherNode链表断掉
            [self insetNodeAfterTail:p2 headNode:newHead];//将当前节点插入到新链表newHead的尾部
            p2 = otherNode.next;//获取otherNode链表的下一点节点
        }else{
            headNode.next = p1.next;
            p1.next = nil;
            [self insetNodeAfterTail:p1 headNode:newHead];
            p1 = headNode.next;
        }
    }
    
    //处理没有扫描结束的链表
    while (p1 != nil) {
        headNode.next = p1.next;
        p1.next = nil;
        [self insetNodeAfterTail:p1 headNode:newHead];//尾节点插入
        p1 = headNode.next;
    }
    while (p2 != nil) {
        otherNode.next = p2.next;
        p2.next = nil;
        [self insetNodeAfterTail:p2 headNode:newHead];//尾节点插入
        p2 = otherNode.next;
    }
    return newHead;
}

//单链表:判断两个链表是否相交 (只能是某一个链表的尾节点与另一链表的某一个节点相交)
+(BOOL)intersectWithNode:(SingleLinkNode *)headNode otherNode:(SingleLinkNode *)otherNode {
    
    //判空处理
    if (!headNode || !headNode.next || !otherNode || !otherNode.next) {
        return NO;
    }

    //思路:分别获取两个链表的长度,判断谁的链表更长,链表更长的先走完相差的步数,然后齐步走
    SingleLinkNode *p1 = headNode.next;
    SingleLinkNode *p2 = otherNode.next;
    int L1 = 1;
    int L2 = 1;
    while (p1 != nil) {
        L1 ++;
        p1 = p1.next;
    }
    while (p2 != nil) {
        L2 ++;
        p2 = p2.next;
    }
    p1 = headNode.next;  //将p1遍历指针移动到首节点
    p2 = otherNode.next; //将p2遍历指针移动到首节点
    int i=0;
    if (L1 > L2) {
        while (i < L1-L2 && p1 != nil) { //p1先走
            p1 = p1.next;
            i++;
        }
    }else{
        while (i < L2-L1 && p2 != nil) { //p2先走
            p2 = p2.next;
            i++;
        }
    }
    if (i == ABS(L1-L2)) {
        while (p1 != nil && p2 != nil) { //p1、p2齐步走
            if(p1.next == p2.next) return YES; //相交
            p1 = p1.next;
            p2 = p2.next;
        }
    }
    return NO;
}

//单链表:判断链表是否构成环,如果成环,返回构成环的那个节点
+(SingleLinkNode *)circleWithNode:(SingleLinkNode *)headNode {
    
    //判空处理
    if (!headNode || !headNode.next) {
        return nil;
    }
    
    //思路:采用快慢指针法
    //快指针先走两步,慢指针走一步,如果成环,必然重合。走到第一次重合的地点后,重新设置一个指针p指向头结点,并与慢节点同步伐齐步走,
    //走到第二次相遇的地方,即为构成环的节点
    SingleLinkNode *quick = headNode.next;
    SingleLinkNode *slow  = headNode.next;
    SingleLinkNode *p     = headNode.next;
    while (quick != nil && slow != nil) {
        quick = quick.next.next;
        slow = slow.next;
        if (quick == slow) { //第一次重合,结束循环
            break;
        }
    }
    while (p != nil && slow != nil) {
        p = p.next;
        slow = slow.next;
        if (p == slow) { //第二次重合,找到成环的入口节点
            return p;
        }
    }
    return nil;
}

@end
View Code

main方法

//
//  main.m
//  LinkList
//
//  Created by 夏远全 on 2019/9/25.
//

#import <Foundation/Foundation.h>
#import "FuncontionHelper.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        //1、构建一个单链表
        SingleLinkNode *head = [[SingleLinkNode alloc] init];
        SingleLinkNode *node1 = [SingleLinkNode constructNodeWithData:1];
        SingleLinkNode *node2 = [SingleLinkNode constructNodeWithData:2];
        SingleLinkNode *node3 = [SingleLinkNode constructNodeWithData:3];
        head.next = node1;
        node1.next = node2;
        node2.next = node3;
        [FuncontionHelper printFromHeadWithNode:head printPrefixText:@"构造单链表为"];
        
        //2、从单链表中插入节点
        SingleLinkNode *node4 = [SingleLinkNode constructNodeWithData:4];
        [FuncontionHelper insetNodeAfterHead:node4 headNode:head];
        [FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表头部插入节点4后"];
        
        SingleLinkNode *node5 = [SingleLinkNode constructNodeWithData:5];
        [FuncontionHelper insetNodeAfterTail:node5 headNode:head];
        [FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表尾部插入节点5后"];

        SingleLinkNode *node6 = [SingleLinkNode constructNodeWithData:6];
        [FuncontionHelper insetNodeAtIndex:2 node:node6 headNode:head];
        [FuncontionHelper printFromHeadWithNode:head printPrefixText:@"在单链表第2个位置插入节点6后"];
        
        //3、删除节点
        SingleLinkNode *deleteNode = [FuncontionHelper deleteNodeAtIndex:1 headNode:head];
        NSString *prefixText = [NSString stringWithFormat:@"删除第1个位置的节点%d后单链表为",deleteNode.data];
        [FuncontionHelper printFromHeadWithNode:head printPrefixText:prefixText];
        
        //4、查询节点
        SingleLinkNode *node_x = [FuncontionHelper queryNodeIndex:1 headNode:head];
        if (node_x) NSLog(@"查询到第1个结点的值是:%d",node_x.data);
        
        SingleLinkNode *node_y = [FuncontionHelper queryNodeCountdown:1 headNode:head];
        if (node_y) NSLog(@"查询到倒数第1个结点的值是:%d",node_y.data);
        
        //5、倒序遍历
        NSArray *items = [FuncontionHelper printFromTailWithNode:head];
        NSLog(@"链表倒序遍历:%@",[items componentsJoinedByString:@""]);
        
        //6、反转链表
        SingleLinkNode *newHead = [FuncontionHelper reverseWithNode:head];
        [FuncontionHelper printFromHeadWithNode:newHead printPrefixText:@"将单链表反转后"];
        
        //7、合并单链表
        SingleLinkNode *head1 = [[SingleLinkNode alloc] init];
        SingleLinkNode *node11 = [SingleLinkNode constructNodeWithData:1];
        SingleLinkNode *node22 = [SingleLinkNode constructNodeWithData:3];
        SingleLinkNode *node33 = [SingleLinkNode constructNodeWithData:5];
        head1.next = node11;
        node11.next = node22;
        node22.next = node33;
        [FuncontionHelper printFromHeadWithNode:head1 printPrefixText:@"构造第一个有序单链表为:"];

        SingleLinkNode *otherHead = [[SingleLinkNode alloc] init];
        SingleLinkNode *node44 = [SingleLinkNode constructNodeWithData:2];
        SingleLinkNode *node55 = [SingleLinkNode constructNodeWithData:4];
        SingleLinkNode *node66 = [SingleLinkNode constructNodeWithData:6];
        SingleLinkNode *node77 = [SingleLinkNode constructNodeWithData:8];
        otherHead.next = node44;
        node44.next = node55;
        node55.next = node66;
        node66.next = node77;
        [FuncontionHelper printFromHeadWithNode:otherHead printPrefixText:@"构造第二个有序单链表为:"];

        SingleLinkNode *combieHeadNode = [FuncontionHelper combineWithNode:head1 otherNode:otherHead];
        [FuncontionHelper printFromHeadWithNode:combieHeadNode printPrefixText:@"合并后的有序新链表为:"];
        
        //8、判断相交
        SingleLinkNode *head2 = [[SingleLinkNode alloc] init];
        SingleLinkNode *node111 = [SingleLinkNode constructNodeWithData:1];
        SingleLinkNode *node222 = [SingleLinkNode constructNodeWithData:3];
        SingleLinkNode *node333 = [SingleLinkNode constructNodeWithData:5];
        head2.next = node111;
        node111.next = node222;
        node222.next = node333;
        [FuncontionHelper printFromHeadWithNode:head2 printPrefixText:@"单链表1"];
        
        SingleLinkNode *otherHead2 = [[SingleLinkNode alloc] init];
        SingleLinkNode *node444 = [SingleLinkNode constructNodeWithData:2];
        SingleLinkNode *node555 = [SingleLinkNode constructNodeWithData:4];
        SingleLinkNode *node666 = [SingleLinkNode constructNodeWithData:6];
        SingleLinkNode *node777 = [SingleLinkNode constructNodeWithData:8];
        otherHead2.next = node444;
        node444.next = node555;
        node555.next = node666;
        node666.next = node777;
        node777.next = node222;//指向链表1的node222节点
        [FuncontionHelper printFromHeadWithNode:otherHead2 printPrefixText:@"单链表2"];
        
        BOOL isIntersect = [FuncontionHelper intersectWithNode:head2 otherNode:otherHead2];
        NSLog(@"单链表1和单链表2是否相交:%@", isIntersect ? @"相交" : @"不相交");
        
        //8、判断成环
        SingleLinkNode *head3 = [[SingleLinkNode alloc] init];
        SingleLinkNode *node1111 = [SingleLinkNode constructNodeWithData:1];
        SingleLinkNode *node2222 = [SingleLinkNode constructNodeWithData:3];
        SingleLinkNode *node3333 = [SingleLinkNode constructNodeWithData:4];
        SingleLinkNode *node4444 = [SingleLinkNode constructNodeWithData:5];
        SingleLinkNode *node5555 = [SingleLinkNode constructNodeWithData:6];
        SingleLinkNode *node6666 = [SingleLinkNode constructNodeWithData:7];
        head3.next = node1111;
        node1111.next = node2222;
        node2222.next = node3333;
        node3333.next = node4444;
        node4444.next = node5555;
        node5555.next = node6666;
        node6666.next = node2222;
        /*
              1 -> 3 -> 4 -> 5
                           /
                     7 -  6
        */
        SingleLinkNode *circleNode = [FuncontionHelper circleWithNode:head3];
        NSLog(@"链表是否成环:%@, 成环的入口节点是%d:", circleNode != nil ? @"成环" : @"不成环", circleNode.data);
        
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/XYQ-208910/p/11597067.html