美文网首页程序员
写给给女朋友的算法入门 - 链表

写给给女朋友的算法入门 - 链表

作者: winterszhang | 来源:发表于2018-07-15 20:00 被阅读51次

    什么是链表

    数据结构里,除了数组之外,最常用的应该就是链表(linked list)了。

    那么什么是链表呢,如其名,就是由一个一个的结点(node)连起来的链啦。

    所谓结点,是指一个包含有当前节点的值一个指向下一个节点的链接的实体。

    比如在Java中,一个结点的定义可以是这样:

    class Node {
        // 存放本结点数据的地方
        Data data;
        // 指向同种类结点的指针
        Node next;
        // 生成新结点时,指定结点的值
        Node (Data d) {
            this.data = d;
        }
    }
    

    一个单向链表包含部分: 当前节点的值和一个指向下一个节点的指针

    以上就是一个单向链表(singly linked list)的结点的结构,单向链表就是由一个个这样的结点组成的,链表中的每一个结点都指向下一个节点(next),每个结点中可以存放任意数据(data)。

    还有一种双向链表(Doubly linked list),其实跟单向链表很像。只不过每个结点既有指向下一个结点的指针,也有指向前一个结点的指针。

    链表的实现

    构造一个链表其实十分简单,每次生成一个新的结点,然后将当前的结点指向这个新生成的结点。

    // 生成结点
    Node first = new Node();
    Node second = new Node();
    Node third = new Node();
    
    // 把结点连起来
    first.next = second;
    second.next = third;
    

    这样,一个有三个结点的单向链表就实现了。当知道了first,就可以通过first.next访问second,通过first.next.next访问third

    当然以上是为了便于理解,实际上一般应用中,会通过下面的方式构造一个单向链表。

    // head指向最开始的那个结点,一旦生成就不会改变
    Node head = new Node();
    // pointer是一个不断向后移动的指针
    Node pointer = head;
    // 每生成一个新的结点,pointer指针就向后移动到新生成的结点上
    pointer.next = new Node();
    pointer = pointer.next;
    // 重复以上步骤
    pointer.next = new Node();
    pointer = pointer.next;
    

    这样,当知道了head结点时,就能通过不断的next访问到后续的所有结点了。

    链表的操作

    遍历链表

    一般只有指向链表第一个结点的head是已知的,通过不断的将指针移动到下一个结点,就能遍历整个链表。

    // 遍历整个链表
    public void visitLinkedList(Node head) {
        // 一般不移动head指针,否则移动之后无法再从头遍历整个链表
        Node pointer = head;
        while (pointer != null) {
            // Do anything you want
            pointer = pointer.next;
        }
    }
    

    插入结点

    在链表中插入结点是十分省时的,只需要找到指定的位置,然后新生成一个结点插入即可。

    // 在指定的结点(第position个结点)后面插入一个新的结点,新结点的值为data
    public void insertNode(Node head, int postion, Data data) {
        Node pointer = head;
        // 移动到指定结点
        for (int i = 0; i < position; i++)
            pointer = pointer.next;
        
        // 先将指定结点的后一个结点保存起来
        Node tmp = pointer.next;
        // 在指定结点后面插入新的结点
        pointer.next = new Node(data);
        // 将新生成的结点指向原来指定结点的后一个结点
        pointer.next.next = tmp;
    }
    

    删除结点

    删除结点跟插入结点的操作比较类似。

    // 删除指定位置的结点
    public void deleteNode(Node head, int position) {
        Node pointer = head;
        // 移动到指定结点的前一个结点
        for (int i = 0; i < position - 1; i++)
            pointer = pointer.next;
        
        // 直接将指定结点的前一个结点,指向指定结点的后一个结点,
        // 类似于指定结点被跳过了,就相当于指定结点删除了
        pointer.next = pointer.next.next;
    }
    

    总结

    其实链表本身的结构还是非常简单的,只不过使用的时候,一定要注意各种操作的先后顺序,否则很容易变成一边捡芝麻,一边丢西瓜的状况(笑。

    比如分清,是先进行操作再访问下一个结点,还是先访问下一个结点再进行操作。

    // 比如生成一个新的链表,一开始的时候很容易写出下面这样的代码
    Node pointer = new Node();
    Node head = pointer;
    for (int i = 0; i < length; i++) {
        pointer = pointer.next;
        pointer = new Node();
    }
    

    然后发现,咦?为什么我的链表永远都只有一个结点,到底哪里出了问题......

    相关文章

      网友评论

        本文标题:写给给女朋友的算法入门 - 链表

        本文链接:https://www.haomeiwen.com/subject/arxmpftx.html