• 线性数据结构


    线性数据结构就是:顺序表链表

    其他的就是非线性数据结构,比如树,图等。线性数据结构的特点是,一对一,一个数据只有一个后继和一个前驱;而非线性数据结构则是一对多。

    1、顺序表

    顺序表是个逻辑概念,顺序表的相邻元素在物理位置上也是相邻的。通常我们采用数组实现顺序表,我们可能会把这两者混为一谈。数组是基于顺序表逻辑实现的,但在细微之处有些许差别,比如数组下标从0开始,顺序表下标从1开始。

    顺序表的特点是随机存取方便,不可扩充。

    image-20210917105644839

    2、链表

    当我们需要存放大量动态的数据时,这时就需要链表登场了。链表不需要数据存在连续的位置,它的存储结构是每个数据都有下一个结点的地址,所有的数据串成一条链,从第一个数据开始问,我们可以找到任何一个数据。相对于顺序表直接给出了每个数据的位置编号,链表的查找比较费事。

    image-20210917110120388

    • 链表可以有一个空的头节点dummy,不存放数据,只用来做标识。方便数据的增删,并且对于空表方便处理。
    • 除了上面的单向链表,链表还有双向链表,循环链表,十字链表等变式。

    单链表反转

    链表反转一般有三种方法,原地反转,头插法反转,递归反转。其中递归反转最简单简洁,但是空间复杂度更高。下面分别介绍。

    image-20210917111947851

    1.原地反转

    首先让pre的next指向cur的next;再让cur的next指向头节点的下一个结点;这时已经反转了结点,此时链表第一个结点变成了当前反转的结点;再让头节点的next指向cur;最后移动cur到下一个要反转的结点。重复上述步骤,直到cur为null。此时头节点也指向最后一个节点了。

    带头节点的链表:

    static LinkNode reverse1(LinkNode l){
        if(l.next == null || l.next.next == null){
            return l;
        }
        LinkNode pre = l.next;  // pre 不动, 移动头节点
        LinkNode cur = pre.next;
        while(cur != null){
            pre.next = cur.next;
            cur.next = l.next;
            l.next = cur;
            cur = pre.next;
        }
        return l;
    }
    

    不带头结点的链表:

    一样的做法一个指向pre,一个指向cur。而且比有头节点的更加简单。

    public LinkNode reverse2(LinkNode l){
        LinkNode pre = l;
        LinkNode cur = pre.next;
        while(cur != null){
            LinkNode next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        l.next = null;  // 记得将最开始的头节点指向null,否则形成环路
        return pre;
    }
    

    2.头插法

    新建一个链表,遍历原链表,用头插法插入到新链表中。

    值得注意的是,在遍历开始前,还要断开第一个结点和第二个结点之间的链。初始化工作做完后,从第二个结点开始遍历链表,依次插入新链表中,即实现反转。

    image-20210917110559235

    带头节点的链表:

    static LinkNode reverse3(LinkNode l){
        if(l.next == null || l.next.next == null){
            return l;
        }
        LinkNode cur = l.next.next;
        LinkNode next;
        l.next.next = null;
        while (cur != null){
            next = cur.next;
            cur.next = l.next;
            l.next = cur;
            cur = next;
        }
        return l;
    }
    

    不带头结点的链表:

    static LinkNode reverse4(LinkNode l){
        if(l == null || l.next == null){
            return l;
        }
        LinkNode cur = l.next;
        LinkNode next;
        l.next = null;
        while (cur != null){
            next = cur.next;
            cur.next = l;
            l = cur;
            cur = next;
        }
        return l;
    }
    

    3.递归反转

    直接递归得到最后一个节点,然后每层就是将这个节点的下一个节点指向这个节点,这个节点指向null,最后返回末尾节点。(不带头节点)

    static LinkNode reverse5(LinkNode l){
        if(l == null || l.next == null){
            return l;
        }
        LinkNode p = reverse5(l.next);
        l.next.next = l;
        l.next = null;
        return p;
    }
    

    3、栈

    栈是一种特殊的操作受限的线性数据结构,可以用用顺序表实现也可以用链表实现。在计算机中经典的应用就是函数调用或中断时,保护现场数据时使用。

    因为每次都是对栈顶元素进行操作,所以一般需要一个top指针指向栈顶。

    为了操作方便,和链一样,通常我们设置了一个头节点,因此top不是指向栈顶第一个元素,而是第一个元素再上一个位置。由于栈的特性,非常适合使用头插法插入数据。

    • 栈的基本操作:

      push:入栈操作

      pop:出栈操作

      top:查看栈顶元素操作

    • 实现:

      我们可以用链表实现栈,也可以用数组实现。如果是链表,非常简单,只需要一个指针指向头节点即可,链表的优点是出栈入栈非常的快。而使用数组就面临着如何选择数组大小的问题,一般来说我们应该让数组根据元素的个数动态改变大小,java中给出的方法是每次满了就扩大一倍数组大小,同时随着不断弹出元素数组大小也应该减小,策略是元素个数小于数组的1/4,那么就把数组缩小一半。

    • 应用

      • 函数调用
      • 浏览器回退前进(两个栈)
      • 运算表达式求解(双栈,数值一个栈,运算符一个栈)
      • ...

    4、队列

    队列也是一种线状了操作的线性数据结构。和栈不一样,它是先进的先出FIFO,在计算机中经典的应用是进程队列。因为队列类似于现实中的排队,计算机中有排队的地方通常就会有它。

    队列也有多种实现方法,常见的是基于链表实现。

    • 队列的操作有:

      入队(enqueue)添加到队尾

      出队(dequeue)从队头移出

  • 相关阅读:
    python面向对象(进阶篇)
    python面向对象三大特性
    点击头像单独把图片拉取出来.然后再次点击回到初始效果
    关于iOS的自动弹出键盘问题
    为UITextView添加通知..来检测UITextView内容的改变
    null 解决方法
    获取手机的唯一标示uuid
    SDK | 声波传输
    Xcode svn import项目 上传.a文件(静态库)
    点击状态栏回到顶部
  • 原文地址:https://www.cnblogs.com/cpcpp/p/13371542.html
Copyright © 2020-2023  润新知