用ios实现单向链表

链表的定义

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。由于不必须按顺序存储,链表的插入和删除操作可以达到O(1)的复杂度

链表和数组
链表和数组的区别
  • 数组静态分配内存,链表动态分配内存。
  • 数组在内存中是连续的,链表是不连续的。
  • 数组利用下标定位,查找的时间复杂度是O(1),链表通过遍历定位元素
  • 查找的时间复杂度是O(N)。
  • 数组插入和删除需要移动其他元素,时间复杂度是O(N),链表的插入或删
  • 除不需要移动其他元素,时间复杂度是O(1)。

数组的优点

  • 随机访问性比较强,可以通过下标进行快速定位。
  • 查找速度快

数组的缺点

  • 插入和删除的效率低,需要移动其他元素。
  • 会造成内存的浪费,因为内存是连续的,所以在申请数组的时候就必须规定七内存的大小,如果不合适,就会造成内存的浪费。
  • 内存空间要求高,创建一个数组,必须要有足够的连续内存空间。
  • 数组的大小是固定的,在创建数组的时候就已经规定好,不能动态拓展。

链表的优点

  • 插入和删除的效率高,只需要改变指针的指向就可以进行插入和删除。
  • 内存利用率高,不会浪费内存,可以使用内存中细小的不连续的空间,只有在需要的时候才去创建空间。大小不固定,拓展很灵活。

链表的缺点

查找的效率低

单链表
单链表.png

头指针指向头结点;
普通Node即用于数据存储和直接后继指针存储的节点;
尾结点即链表中最后一个节点,其指针域next为空,其后无节点;

//
//  LinkList.m
//  LinkDemo
//
//  Created by 李台辉 on 2021/4/12.
//

#import "LinkList.h"


/// 节点
@interface Node : NSObject
// 保存value
@property (nonatomic, strong) NSObject *ele;
// 指向下一个节点
@property (nonatomic, strong) Node *next;

@end


@implementation Node



- (instancetype)initWithEle:(NSObject *)ele next:(Node *)next{
    if (self = [super init]) {
        _ele = ele;
        _next = next;
    }
    return self;
}

@end



@interface LinkList ()

/// 存链表的头节点
@property (nonatomic, strong) Node *first;

/// 节点个数
@property (nonatomic, assign) NSInteger size;

@end


@implementation LinkList


/// 返回链表节点个数
- (NSInteger)size{
    return _size;
}


- (BOOL)isEmpty{
    return self.size == 0;
}

//获取obj在链表中的索引
- (NSInteger)indexOfObject:(NSObject *)obj{
    Node *node = self.first;
    // 从头节点开始,依次比对 ele
    for (int i = 0; i < self.size; i ++) {
        if ([obj isEqual:node.ele]) return i;
        node = node.next;
    }
    return -1;
}


// 是否包含某个对象
- (BOOL)isContainObject:(NSObject *)obj{
    NSInteger index = [self indexOfObject:obj];
    return index != -1;
}

#pragma - mark 增删改查

// 增
//插入obj到指定位置
- (void)addObject:(NSObject *)obj atIndex:(NSInteger)index{
    if (index <0) {
        NSLog(@"索引不对");
        return;
    }
    if (index > self.size) {
        NSLog(@"索引太大");
        return;
    }
    
    // 添加到头节点
    if (index == 0) {
        // 创建节点,将节点的next 指向当前头节点
        Node *node = [[Node alloc] initWithEle:obj next:self.first];
        
        // 更新头节点
        self.first = node;
    }else{
        // 获取index 前面一个的节点
        Node *preNode =  self.first;
        for (int i = 0; i < index - 1 ; i ++) {
            preNode = preNode.next;
        }
        
        //构建node
        Node *insertNode = [[Node alloc] initWithEle:obj next:preNode.next];
        
        //前一个节点指向插入的节点
        preNode.next = insertNode;
    }
    
    self.size ++ ;
    
}

// 头部
- (void)addObjectToHead:(NSObject *)obj{
    [self addObject:obj atIndex:0];
}

// 尾部
- (void)addObject:(NSObject *)obj{
    [self addObject:obj atIndex:self.size];
}



// 删
- (void)removeObjectAtIndex:(NSInteger)index{
    if (index <0) {
        NSLog(@"索引不对");
        return;
    }
    
    if (index > self.size -1) {
        NSLog(@"越界");
        return;
    }
    
    if (index == 0) {
        self.first = self.first.next;
    }else{
        //获取index前面的一个节点
        Node *preNode =  self.first;
        for (int i = 0; i < index - 1 ; i ++) {
            preNode = preNode.next;
        }
        // 获取index 的下一个节点
        Node *nextNodel = preNode.next.next;

        //前一个节点指向后一个节点
        preNode.next = nextNodel;
    }

    self.size --;
}

// 清空
- (void)clear{
    self.size = 0;
    self.first = nil;
}


/// 改
- (void)setObject:(NSObject *)obj AtIndex:(NSInteger)index{
    //获取index 节点
    Node *node =  self.first;
    for (int i = 0; i < index ; i ++) {
        node = node.next;
    }
    node.ele = obj;
}

/// 查
- (NSObject *)objectAtIndex:(NSInteger)index{
    if (index <0) {
        NSLog(@"索引不对");
        return nil;
    }
    
    if (index > self.size -1) {
        NSLog(@"越界");
        return nil;
    }
    Node *node = self.first;
    for (int i = 0; i < index ; i ++) {
        node = node.next;
    }
    return node.ele;
}



- (NSString *)description{
    NSMutableString *string = [NSMutableString string];
    [string appendString:@"["];
    Node *node = self.first;
    // 遍历链表
    for (int i = 0; i < self.size; i ++ ) {
        [string appendString:[NSString stringWithFormat:@"%@",node.ele]];
        if (i != self.size -1) {
            [string appendString:@","];
        }
        
        //更新当前节点
        node = node.next;
    }
    
    [string appendString:@"]"];
    return string;
}

@end

单链表反转

LeetCode-链表反转

public class ListNode {
    
    public var val: Int
    
    public var next: ListNode?
        
    public init() {
        self.val = 0; self.next = nil;
        
    }
    public init(_ val: Int) {
        self.val = val; self.next = nil;
        
    }
    public init(_ val: Int, _ next: ListNode?) {
        self.val = val; self.next = next;
        
    }
}

递归方式

class Solution {
    
    func reverseList(_ head: ListNode?) -> ListNode? {
        // 空链 或者一个节点
        if (head == nil || head?.next == nil) {
            return head
        }
        // 递归调用
        let newHead = reverseList(head?.next)
        head?.next?.next = head
        head?.next = nil
        return newHead

    }
}

迭代方式

class Solution1 {
 
    func reverseList(_ head: ListNode?) -> ListNode? {
        // 空链 或者一个节点
        if (head == nil || head?.next == nil) {
            return head;
        }

       //保存当前节点指针
       var curr = head
        // 新的头节点指针
       var newHead : ListNode? = nil
        
      // 从当前链表的第一个节点开始循环,直到最后一个节点
       while curr != nil {
           // 临时指针,执行当前节点的下一个节点,以便后面能访问到下一个节点
           let nextTemp = curr?.next
           // 后面节点的next指向前面一个节点
           curr?.next = newHead
           // 更新 newhead
           newHead = curr
           // 当前节点后移
           curr = nextTemp
       }
       return newHead
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • 夜莺2517阅读 127,720评论 1 9
  • 版本:ios 1.2.1 亮点: 1.app角标可以实时更新天气温度或选择空气质量,建议处女座就不要选了,不然老想...
    我就是沉沉阅读 6,896评论 1 6
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,536评论 28 53
  • 兔子虽然是枚小硕 但学校的硕士四人寝不够 就被分到了博士楼里 两人一间 在学校的最西边 靠山 兔子的室友身体不好 ...
    待业的兔子阅读 2,603评论 2 9