算法--Swift--链表

算法不分语言,重要的是思想,这里我用swift来记录算法的实现,供大家一起学习.
首先我们看下链表的结构

1.单链表
屏幕快照 2017-12-04 上午11.53.23.png

2,双链表
屏幕快照 2017-12-04 上午11.55.11.png

我们主要讲解双链表

在这里我们创建一个链表类和节点类(下方代码)

/// 声明链表类
public final class LinkedList<T>{
    /// 声明节点类
    public class LinkedListNode<T>{
        var value: T
        var next: LinkedListNode<T>?
        weak var previous: LinkedListNode<T>? //这里使用week避免循环引用
        
        public init(value: T) {
            self.value = value
        }
    }

    /// 重命名节点类,方便使用
    public typealias Node = LinkedListNode<T>
    
    /// 链表头节点
    fileprivate var head: Node?
    
    /// 链表初始化
    public init() {}

}

下面为该链表添加基本的属性和方法

/// 判断链表是否空
    public var isEmpty: Bool {
        return head == nil
    }
    
    /// 获取链表第一个节点
    public var first: Node? {
        return head
    }
    
    /// 获取最后一个节点
    public var last: Node?{
        guard var node = head else {
            return nil
        }
        
        while let next = node.next {
            node = next
        }
        return node
    }
    
    /// 计算节点的个数
    public var count: Int {
        
        guard var node = head else {
            return 0
        }
        
        var count: Int = 1
        while let next = node.next {
            node = next
            count += 1
        }
        return count
    }
    
    /// 获得某个位置的节点
    ///
    /// - Parameter index: 链表中第几个节点(从0开始)
    /// - Returns: Optional LinkedListNode
    public func node(at index: Int) -> Node! {
        assert(head != nil ,"链表为空")
        assert(index >= 0, "index 必须大于等于0")
        
        if index == 0 {
            return head!
        }else {
            var node = head!.next
            for _ in 1..<index {
                node = node?.next
                if node == nil {
                    break
                }
            }
            assert(node != nil, "index 超出接线")
            return node!
        }
    }
    
    /// 获得指定下标节点的内容
    ///
    /// - Parameter index: 链表中第几个节点(从0开始)
    public func value(index: Int) -> T {
        let node = self.node(at: index)
        return node!.value
    }
    
    /// 添加一个节点到链表的尾部
    public func append(_ node: Node!){
        if head == nil {
            head = node
            return
        }
        
        last!.next = node
        node.previous = last
    }
    
    /// 添加一个value值到链表的尾部
    public func append(_ value: T) {
        let newNode = Node(value: value)
        self.append(newNode)
    }
    
    /// 插入一个节点到链表中
    public func insert(_ newNode: Node!, at index: Int) {
        if index == 0 {
            newNode.next = head
            head?.previous = newNode
            head = newNode
        }else {
            let nextNode = node(at: index)
            let previousNode = node(at: index - 1)
            
            newNode.next = nextNode
            nextNode?.previous = newNode
            
            newNode.previous = previousNode
            previousNode?.next = newNode
        }
    }
    
    /// 插入一个value到链表中
    public func insert(value: T, at index: Int){
        let newNode = Node(value: value)
        self.insert(newNode, at: index)
    }
    
    /// 移除所有的 node/value
    public func removeAll() {
        head = nil
    }
    
    /// 移除某个节点
    public func remove(node: Node!) {
        assert(head != nil, "该链表没有节点")
        
        let previousNode: Node? = node.previous
        let nextNode: Node? = node.next
        
        if previousNode == nil {
            head = nextNode
        }else{
            previousNode!.next = nextNode
            nextNode?.previous = previousNode
        }

        node.previous = nil
        node.next = nil
    }
    
    /// 移除指定位置的节点
    public func remove(at index: Int) {
        let node = self.node(at: index)
        self.remove(node: node)
    }

写完方法,让我们做下简单的测试吧,验证链表的正确性

let list = LinkedList<String>()
list.isEmpty                //true
list.first                  //nil
list.last                   //nil

list.append("小码儿")        //[小码儿]
list.isEmpty                //false
list.first!.value           //"小码儿"
list.last!.value            //"小码儿"
list.count                  //1

list.append("666")          //[小码儿,666]
list.first!.value           //"小码儿"
list.last!.value            //"666"
list.count                  //2

list.first!.previous        //nil
list.first!.next!.value     //"666"
list.last!.previous!.value  //"666"
list.last!.next             //nil

list.node(at: 0).value      //"小码儿"
list.node(at: 1).value      //"666"
list.insert(value: "mySwift", at: 1)  //[小码儿,mySwift,666]
list.remove(at: 1)          //[小码儿,666]

上方链表的基本操作都已经完成,为了更好的理解,现在让我们一起做下扩展吧:
1.扩展打印链表的方法,方便我们查看

/// 打印链表 当我们调用print 格式 ["value1","value2"]
extension LinkedList: CustomStringConvertible {
    public var description: String {
        var node = head
        var text: String = "["
        while node != nil {
            text += "\(node!.value)"
            node = node!.next
            if node != nil {
                text += ","
            }
        }
        return text + "]"
    }
}

2.将列表反转

//反转链表(通过迭代的方式反转,这里需要重点理解下)
extension LinkedList {
    public func reverse() {
        var node = head
        while let currentNode = node {
            node = currentNode.next
            swap(&currentNode.next, &currentNode.previous)
            head = currentNode
        }
    }
}

同时附上单链表的反转

/// 实现单链表的反转(迭代)
    public func reverseLinkList(){
        var node1: Node? = head
        var node2: Node? = nil
        var node3: Node? = nil
        
        while node1 != nil{
            node2 = node1
            node1 = node1!.next
            
            node2!.next = node3
            
            node3 = node2
            head = node2
        }
    }

今天先到这里,以后关于链表的问题会不断的补充进来

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,059评论 4 62
  • 我爱日下的光景 和那繁星的简洁 爱那枯萎的岁月 和那开了一半的花 细语千言 抵不过宛转千回 寻寻觅觅 抵不过案头的...
    言色阅读 194评论 0 2
  • 起风了 前两天还是阳光明媚 现在是妖风阵阵啊 一个人的时候吹吹风 脑子里想什么是什么 你总是喜欢说我爱乱想 那如果...
    啊瓷阅读 154评论 0 0
  • 耳边的歌都是好妹妹乐队的,伴着蝉鸣,加上桌子的一杯茶,很有夏天的感觉。记得前几天的清晨,知了的叫声吵醒了我...
    CHOK杰阅读 365评论 0 4