数据结构上篇 - 线性数据结构

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

本篇文章属于数据结构连载篇的其中之一,共分为上中下三篇。上篇主要介绍线性数据结构数组、链表、栈、队列;中篇介绍树和图;下篇介绍复合数据结构哈希表、跳表等。希望大家持续关注,谢谢。

1 简单介绍

我们所说的数据结构主要是指数据在内存中的存储结构,即程序运行时,我们通过将数据存入不同的容器,通过操作容器内的数据,以完成我们的业务逻辑。

数据结构在物理结构上一般分为顺序结构和链式结构。

所有的数据结构,本质是不是顺序结构就是链式结构,要么就是复合结构。

  • 顺序结构(在内存中是连续的地址空间)

    image.png

  • 链式结构(在内存中是非连续的地址空间,通过存储下一个数据的地址指针(Java中就是对象变量)找到下一个数据)

    image.png

数据结构的逻辑结构,也是我们数据结构系列文章的讲解顺序,下面我仅列出大纲

  • 线性结构
    • 数组
    • 链表
    • 队列
    • 二叉树
    • 非二叉树
  • 复合数据结构
    • 哈希表
    • 跳表
    • 线段树

2 数组

数组是顺序结构的代表,也是线性结构的代表,也是日常我们最常使用的一种数据结构,Java中的ArrayList就是对数组的一种封装,使其可以动态扩容,更方便的增删改查。

image.png

访问的时间复杂度(O(1)通过下标访问,但往往需要结合搜索)

搜索的时间复杂度(O(n)可以通过二分查找优化O(logn))

插入的时间复杂度(O(n)需要移动其他元素)

删除的时间复杂度(O(n)需要移动其他元素)

特点:访问效率高,插入修改效率低,可能需要开辟比较大的连续存储空间

3 链表

链表是链式结构的代表,也是一种线性结构,Java中的LinkedList就是对链表的一种封装。

image.png

访问的时间复杂度(O(n)通过遍历访问)

搜索的时间复杂度(O(n))

插入的时间复杂度(O(1)需要修改地址指针即可)

删除的时间复杂度(O(1)需要修改地址指针即可)

特点:插入修改效率高,访问效率低,不需要连续存储空间,但是需要每个节点存放地址值,会浪费一部分空间

链表有如下几种形态

3.1 单链表

image.png

特点:访问只能向后访问,如果我们想要获取上一个节点必须再次遍历

3.2 双向链表

image.png

特点:弥补了单链表的一些不足,即可向前访问也可以向后访问,占用存储资源也更多,需要每个节点存储两个地址值

3.3 环(单向环和双向环)【严格说不属于线性结构】

image.png

image.png

特点:首尾闭合,可以用来解决一些比较有特点的问题,约瑟夫环,丢手帕问题

4 栈

栈的特点是先入后出,后入先出,也是非常经典的一种数据结构。

image.png

访问的时间复杂度(O(1)只能访问栈顶元素)

搜索的时间复杂度(O(n))

插入的时间复杂度(O(1)操作栈顶元素)

删除的时间复杂度(O(1)操作栈顶元素)

栈只有一个出入口,就像水桶一样,后进先出

Java的代码的执行原理就是借助栈来实现的,有些计算器也是基于栈来实现的

LeetCode第20题也可以巧妙的用栈来解决

package com.zhj.leetcode;

import java.util.Stack;

/**
 * 力扣 20 是否是合法括号
 * @author zhj
 */
public class Test20 {
    public static void main(String[] args) {
        String s = "()(){}[]{}";
        System.out.println(isT(s));
    }
    public static boolean isT(String s) {
        char[] chars = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '(' || chars[i] == '[' || chars[i] == '{') {
                stack.push(chars[i]);
            } else {
                if (stack.size() == 0) {
                    return false;
                } else {
                    Character pop = stack.pop();
                    if (chars[i] == ')') {
                        if (pop != '(') {
                            return false;
                        }
                    } else if (chars[i] == ']') {
                        if (pop != '[') {
                            return false;
                        }
                    } else if (chars[i] == '}') {
                        if (pop != '{') {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            }
        }
        return true;
    }
}
复制代码

5 队列

队列与栈恰恰相反,队列是先进先出,两端开放(单端队列:一进一出;双端队列:两进两出)

image.png

访问的时间复杂度(O(n))

搜索的时间复杂度(O(n))

插入的时间复杂度(O(1))

删除的时间复杂度(O(1))

队列可以解决好多问题,比如上说控制超时时间删除LeetCode933

package com.zhj.leetcode;
​
import java.util.LinkedList;
import java.util.Queue;
​
/**
 * 力扣933 队列计数器
 * @author zhj
 */
public class Test933 {
    public static void main(String[] args) {
        RecentCounter rc = new RecentCounter();
        System.out.println(rc.ping(1));
        System.out.println(rc.ping(100));
        System.out.println(rc.ping(3001));
        System.out.println(rc.ping(3002));
    }
}
class RecentCounter {
    private Queue<Integer> queue;
​
    public RecentCounter() {
        queue = new LinkedList<>();
    }
​
    int ping(int t) {
        queue.add(t);
        // 计算符合3000毫秒以上出队列
        while (!queue.isEmpty() && t - queue.peek() > 3000) {
            queue.poll();
        }
        return queue.size();
    }
}
复制代码

おすすめ

転載: juejin.im/post/7055091102122000421