学习数据结构——链表

一直以来都知道自己在数据结构上是个弱点,大学时期学的东西到现在就只能记得一个概念了,自从期末考完试就都还给老师了。要开始找工作面试了,决定把这些东西都重新温习一遍。

数据结构中最基础的应该就是线性表(线性表:零个或多个数据元素的有限序列)了,线性表根据物理结构的不同分为顺序存储结构和链式存储结构,顺序结构实现就是数组了,链式结构就是链表了。

定义:链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表分为单项链表,双向链表和循环链表,一般情况下单项链表居多。推荐看一下J_Knight_在掘金上的文章(末尾有链接,链表要实现节点(Node)和链式结构(List)

节点是链表的基本单元,单项链表节点只有next指针,双向链表包含next和previous两个指针

ADT 节点(node)

Data

 value:持有的数据

Operation

 init:初始化

 previous:指向上一节点的指针

 next:指向下一节点的指针

endADT

链表实现了一些主要的操作功能,如插入节点,删除节点,查询节点等(增删改查)

ADT 链表(linked list)

Data

 linked list:持有的线性表

Operation

 init:初始化

 count:持有节点总个数

 isEmpty:是否为空

 first:头节点

 last:尾节点

 node:传入index返回节点

 insert:插入node到指定index

 insertToHead:插入节点到表头

 appendToTail:插入节点到表尾

 removeAll:移除所有节点

 remove:移除传入的节点

 removeAt:移除传入index的节点

endADT

具体代码就不贴了,J_Knight_在掘金上写的很清楚了,建议自己敲一遍,还是很有作用的,我想分享一下在iOS面试之道上涉及到链表的三道题并记录一下我当时的想法。

question1:给出一个链表和一个值x,要求将链表中所有小于x的值放到左边,大于等于x的值放到右边,并且保证原链表节点的顺序不变,例子:List:1->5->3->2->4->2 x = 3 结果为:1->2->3->5->3->4

解题思路:使用两个链表,一个记录比x小的值,一个记录比x大的值,使用尾插法来保证节点的顺序是不变的,最后将两个链表连接起来。

Dummy节点:它的作用就是作为一个虚拟的头前结点。我们引入它的原因是我们不知道要返回的新链表的头结点是哪一个,它有可能是原链表的第一个节点,可能在原链表的中间,也可能在最后,甚至可能不存在(nil)。

实现代码:


func LinkedListSort(_ head : LinkedList<Int>, _ x : Int) -> LinkedList<Int>{
    let prevDummy = LinkedListNode<Int>(value: 0) , postDummy = LinkedListNode<Int>(value: 0)
    var prev = prevDummy , post = postDummy
    
    var node = head.first
    
    while node != nil {
        if((node?.value)! < x){
            prev.next = node
            prev = node!
        }else{
            post.next = node
            post = node!
        }
        node = node!.next
    }
    
    post.next = nil
    prev.next = postDummy.next
    
    return head
}

question2:如何检测链表中是否有环存在(这道题在去苏宁面试的时候问过的)

解题思路:用两个指针同时访问链表,其中一个的速度是另一个的两倍(快指针和慢指针),如果他们变成相等的,那么链表存在环,反之则链表不存在环(这个理论我思考了很久,不明白为什么快指针一定会和慢指针指向同一个节点,后来想通了,因为有环的链表是不会over的,两个指针一定会指向同一个节点)

实现代码:


func isExistCircle(_ List : LinkedList<Int>) -> Bool{
    let head = List.first
    
    var slow = head
    var fast = head
    
    while fast != nil && fast?.next != nil{
        slow = slow?.next
        fast = fast?.next?.next
        if slow === fast{
            return true
        }
    }
    
    return false
}

Question 3:给出链表和一个值x,要求将链表中倒数第x个节点删掉(链表为一个未知长度的单向链表)

解题思路:使用两个速度相同的指针,快指针领先慢指针x个节点,当快指针到达终点时,慢指针的下一个节点就是我们要删除的节点。(我当时直接用链表里面的size函数获得链表长度,用remove函数删除了对应的节点,但是remove函数是基于双向链表实现的,如果是单项链表的话是不可以直接remove的)

实现代码:

func removeFromEnd(_ List : LinkedList<Int>, _ x : Int ) -> LinkedList<Int>{
    
    let head = List.first
    
    var slow = head
    var fast = head
    
    //设置快指针初始位置
    for _ in 0 ..< x {
        fast = fast?.next
    }
    
    while fast != nil && fast?.next != nil{
        slow = slow?.next
        fast = fast?.next
    }
    
    //List.remove(node: (slow?.next)!)//remove函数是基于双向链表实现的,单项链表无法使用
    slow?.next = slow?.next?.next
    return List
}

链表章节就这三道题了,主要还是学到了dummy节点和快行指针这样的概念,比较有收获。

链接:
J_Knight_掘金地址

iOS面试之道 链表章节内容

iOS面试之道(唐巧,故胤道长)

    原文作者:墨_辰
    原文地址: https://www.jianshu.com/p/1026a9fa2884
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞