使用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,   weak) 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] 链表倒序遍历:5、3、2、1、6    //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

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

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;
}

原文地址:https://www.cnblogs.com/XYQ-208910/p/11597067.html

时间: 2024-10-09 21:46:46

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

链表学习一:单链表创建-头插入与尾插入

链表的创建过程是一个动态的生成过程,创建链表有两种思路,一种是从表头插入,另一种是从表尾插入. 表头插入思路:从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表头上,直到读入结束标志为止. 表尾插入思路:从一个空表开始,重复读入数据,生成新结点,将读入数据存放在新结点的数据域中,然后将新结点插入到当前链表的表尾上,直到读入结束标志为止. 两种方法C++实现如下: 1 #include<iostream> 2 using namespace

链表(创建,插入,删除和打印输出(转载)

链表(创建,插入,删除和打印输出 /*----------------------------------------------------------------------------- 文件功能:实现了动态建立一个学生信息的链表包括链表的创建.插入.删除.和打印输出学生信息包括姓名和分数本链表是带有头结点的,头结点的内容为空内容-----------------------------------------------------------------------------*//*

【LeetCode-面试算法经典-Java实现】【083-Remove Duplicates from Sorted List(排序的单链表中删除重复的结点)】

[083-Remove Duplicates from Sorted List(排序的单链表中删除重复的结点)] [LeetCode-面试算法经典-Java实现][所有题目目录索引] 原题 Given a sorted linked list, delete all duplicates such that each element appear only once. For example, Given 1->1->2, return 1->2. Given 1->1->2

从无头单链表中删除节点

1.从无头单链表中删除节点 一个没有头指针的单链表.一个指针指向此单链表中间的一个节点(不是第一个也不是最后一个节点).将该节点删除. A-->B-->C-->D       =====>     A-->C-->D 问题解析:由于只能单向遍历,故与删除B节点,无法得知B的前驱A,即无法像普通删除中那样让A的next指向C; 这里有个技巧,将C当做B,而B复制成C,则删除C节点即可: 2.给定一个链表的头指针,要求只遍历一次,将单链表中的元素顺序反转过来. A-->

编程之美:从无头单链表中删除节点,讨论

1.问题描述 假设有一个没有头指针的单链表.一个指针指向此单链表中间的一个节点(不是第一个,也不是最后一个),请将该节点从单链表中删除. 如下图所示: 这种情况大家都知道,将下一个节点的data和next拷贝到当前节点,并将当前节点的next设置为下下个节点,然后释放下一个节点所占内存(free), 如果红字条件去掉: 还用上面的方法就有问题,一般上面方法的代码如下: void DeleteRandomNode(Node* pCurrent) { if(pCurrent == NULL) ret

单链表的删除

单链表的删除,就是找到链表节点的前面一个节点,将其与待删节点的下一个节点连接,待删节点断链 function remove(head,val){ var pre = head; while(pre.next && pre.next.val != val){ pre = pre.next; } if(pre == null){ return; } if(pre.next != null){ pre.next = pre.next.next; } } 另一种删除方法,已知链表的头节点和待删节点

习题11-8 单链表结点删除 (20分)

习题11-8 单链表结点删除 (20分) 本题要求实现两个函数,分别将读入的数据存储为单链表.将链表中所有存储了某给定值的结点删除.链表结点定义如下: struct ListNode { int data; ListNode *next; }; 函数接口定义: struct ListNode *readlist(); struct ListNode *deletem( struct ListNode *L, int m ); 函数readlist从标准输入读入一系列正整数,按照读入顺序建立单链表

数据结构和算法设计专题之---判断单链表中是否有环,环的长度,环的入口节点

题目: 给定一个单链表,只给出头指针head: 1.如何判断是否存在环? 2.如何知道环的长度? 3.如何找出环的连接点在哪里? 4.带环链表的长度是多少? 解法: 1.对于问题1,使用追赶的方法,设定两个指针slow.fast,从头指针开始,每次分别前进1步.2步.如存在环,则两者相遇:如不存在环,fast遇到NULL退出. 2.对于问题2,记录下问题1的碰撞点p,slow.fast从该点开始,再次碰撞所走过的操作数就是环的长度s. 3.问题3:有定理:碰撞点p到连接点的距离=头指针到连接点的

单链表的基本操作(建立.遍历.添加.删除)

有n个元素,我们把他们用链表表示,执行一些操作在某个位置添加一个数,或者删除某个位置的数: 输入合法的代码: #include<iostream> #include<stdio.h> #include<stdlib.h> using namespace std; struct node { int date; struct node *next; }; node *CreatList(node *head, int n); void TravList(node *hea