数据结构篇—八大数据结构及代码实现

本文深入探讨了数据结构的重要性,指出良好的数据结构和算法策略是提升程序性能的关键。文章详细介绍了Array、Stack、Queue、Linked List、Binary Tree、Heap、Hashing和Graph等八大数据结构的基本概念、适用场景和模拟实现,强调了理解数据结构对于解决实际问题和优化性能的基础作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简述

​ 在编程过程中,经常会遇到一个问题就是,性能瓶颈,很多时候考虑的都是怎么去做横向扩展,但偏偏忽略了最基本的问题就是系统是否真的已经达到了瓶颈?

​ 性能瓶颈通常的表象:

​ 1、资源消耗过多,外部处理系统的性能不足;

​ 2、或者资源消耗不多但程序的响应速度却仍达不到要求。

​ 调优的方式:

​ 1、寻找过度消耗资源的代码

​ 2、 寻找未充分使用资源但程序执行慢的原因和代码。

基础决定高度

​ 就拿汽车来比较,通常不懂变速箱、发动机的原理但也是能开车,同样一个不懂数据结构和算法的人也能编程。很多人觉得基本的数据结构及操作已经在高级语言中封装,都可以直接调用库函数,那么到底有没有必要好好学习数据结构?

数据结构+算法=程序

通常在程序中,遇到一个实际问题,充分利用数据结构,将数据及其之间的关系有效地存储在计算机中,然后选择合适的算法策略,并用程序高效实现,这才是提高程序性能的主要方式。

如何有效地存储数据,不同的数据结构产生什么样的算法复杂性,有没有更好的存储方法提高算法的效率?

如果没有具备这块相应的知识,怎么完成上述的实现?如果脱离了原有的调用,怎么完成程序的高效实现?而所有的应用实现都依赖于基础,基础就是数据结构和算法。了解这块,才能做到无惧任何技术的演变。所有说基础决定高度!

二、基本的概念

​ 数据结构表示数据在计算机中的存储和组织形式,主要描述数据元素之间和位置关系等。

​ 选择适当的数据结构可以提高计算机程序的运行效率(时间复杂度)和存储效率(空间复杂度)。

数据结构的三种层次:

1、逻辑结构–抽象层: 主要描述的是数据元素之间的逻辑关系

​ **集合结构(集):**所有的元素都属于一个总体,除了同属于一个集合外没有其他关系。集合结构不强调元素之间的任何关联性。

​ **线性结构(表):**数据元素之间具有一对一的前后关系。结构中必须存在唯一的首元素和唯一的尾元素。

​ **树状结构(树):**数据元素之间一对多的关系

​ **图状结构(图):**图状结构或网状结构 结构中的数据元素之间存在多对多的关系

image-20200702113420

2、物理结构–存储层: 主要描述的是数据元素之间的位置关系

描述 优点 缺点
顺序结构 顺序结构就是使用一组连续的存储单元依次存储逻辑上相邻的各个元素 只需要申请存放数据本身的内存空间即可,支持下标访问,也可以实现随机访问。 必须静态分配连续空间,内存空间的利用率比较低。插入或删除可能需要移动大量元素,效率比较低
链式结构 链式存储结构不使用连续的存储空间存放结构的元素,而是为每一个元素构造一个节点。节点中除了存放数据本身以外,还需要存放指向下一个节点的指针。 不采用连续的存储空间导致内存空间利用率比较高,克服顺序存储结构中预知元素个数的缺点 插入或删除元素时,不需要移动大量的元素。 需要额外的空间来表达数据之间的逻辑关系, 不支持下标访问和随机访问。
索引结构 除建立存储节点信息外,还建立附加的索引表来标节点的地址。索引表由若干索引项组成。 是用节点的索引号来确定结点存储地址,检索速度块 增加了附加的索引表,会占用较多的存储空间。
散列结构 由节点的关键码值决定节点的存储地址。散列技术除了可以用于查找外,还可以用于存储。 散列是数组存储方式的一种发展,采用存储数组中内容的部分元素作为映射函数的输入,映射函数的输出就是存储数据的位置, 相比数组,散列的数据访问速度要高于数组 不支持排序,一般比用线性表存储需要更多的空间,并且记录的关键字不能重复。

**3、运算结构–实现层:**主要描述的是如何实现数据结构

  • 分配资源,建立结构,释放资源
  • 插入和删除
  • 获取和遍历
  • 修改和排序

image-20200702115442997

每种逻辑结构采用何种物理结构来实现,并没有具体的规定。当一个结构,在逻辑结构中只有一种定义,而在物理结构中却有两种选择,那么这个结构就属于逻辑结构;

数据结构比较:

数据结构 优点 缺点
数组 查询快 增删快,大小固定,只能存储单一元素
有序数组 比无序数组查询快 插入慢,删除慢,大小固定,只能存储单一元素
先进后出的存取方式 存取其他项很慢
队列 先进先出的存取方式 存取其他项很慢
链表 插入快,删除快 查找慢
二叉树 如果树是平衡的,则增删找都很快 删除算法复杂
红黑树 增删找都快,树总是平衡的算法复杂 算法复杂
2-3-4树 增删找都快,树总是平衡的。类似的树对磁盘存储有效 算法复杂
哈希表 如果关键字已知,则存取极快 删除慢,如果不知关键字,对存储空间使用不充分
增删快,对最大数据项存取快 对其他项数据存储慢
对现实世界建模 有些算法慢且复杂
数据结构 查找 插入 删除 遍历
数组 O(N) O(1) O(N) ——
有序数组 O(logN) O(N) O(N) O(N)
链表 O(N) O(1) O(N) ——
有序链表 O(N) O(N) O(N) O(N)
二叉树(一般情况) O(logN) O(logN) O(logN) O(N)
二叉树(最坏情况) O(N) O(N) O(N) O(N)
平衡树(一般情况和最坏情况) O(logN) O(logN) O(logN) O(N)
哈希表 O(1) O(1) O(1) ——

常见数据结构

image-20200115162545547

数据结构的选择

image-20200115162628634

O符号

​ O在算法当中表述的是时间复杂度,它在分析算法复杂性的方面非常有用。常见的有:

​ 1、O(1):最低复杂度,无论数据量大小,耗时都不变,都可以在一次计算后获得。哈希算法就是典型的O(1)

​ 2、O(n):线性,n表示数据的量,当量增大,耗时也增大,常见有遍历算法

​ 3、O(n²):平方,表示耗时是n的平方倍,当看到循环嵌循环的时候,基本上这个算法就是平方级的,如:冒泡排序等

​ 4、O(log n):对数,通常ax=n,那么数x叫做以a为底n的对数,也就是x=logan,这里是a通常是2,如:数量增大8倍,耗时只增加了3倍,二分查找就是对数级的算法,每次剔除一半

​ 5、O(n log n):线性对数,就是n乘以log n,按照上面说的数据增大8倍,耗时就是8*3=24倍,归并排序就是线性对数级的算法

三、Array

1、介绍

数组是一种连续存储线性结构,元素类型相同,大小相等。

可以在内存中连续存储多个元素的结构。在内存分配也是连续的。数组中的元素通过数组下标进行访问,从0开始。

//只声明了类型和长度
数据类型 []  数组名称 = new 数据类型[数组长度];
//声明了类型,初始化赋值,大小由元素个数决定
数据类型 [] 数组名称 = {数组元素1,数组元素2,......}

大小固定,不能动态扩展(初始化给大了,浪费;给小了,不够用),插入快,删除和查找慢

image-20200702141342102

优点:
		查询快
		1、按照索引查询元素速度快
		2、按照索引遍历数组方便
缺点:
		增加删除慢
		1、事先必须知道数组的长度
		2、空间通常是有限制的,数组的大小固定后就无法扩容了
		3、数组只能存储一种类型的数据
		4、添加,删除的操作慢,因为要移动其他的元素。

2、适用场景

频繁查询,对存储空间要求不大,很少增加和删除的情况。

3、模拟实现

public class Array {
   
   
    
    private int[] intArray;
    private int elems;
    private int length;
    
    public Array(int max) {
   
   
        length = max;
        intArray = new int[max];
        elems = 0;
    }
    
    /**
     * 添加
     * @param value
     */
    public void add(int value){
   
   
        if(elems == length){
   
   
            System.out.println("error");
            return;
        }
        intArray[elems] = value;
        elems++;
    }
    
    /**
     * 查找
     * @param searchKey
     * @return
     */
    public int find(int searchKey){
   
   
        int i;
        for(i = 0; i < elems; i++){
   
   
            if(intArray[i] == searchKey)
                break;
        }
        if(i == elems){
   
   
            return -1;
        }
        return i;
    }
    
    /**
     * 删除
     * @param value
     * @return
     */
    public boolean delete(int value){
   
   
        int i = find(value);
        if(i == -1){
   
   
            return false;
        }
        for (int j = i; j < elems-1; j++) {
   
   
            //后面的数据往前移
            intArray[j] = intArray[j + 1];
        }
        elems--;
        return true;
    }
    
    /**
     * 更新
     * @param oldValue
     * @param newValue
     * @return
     */
    public boolean update(int oldValue,int newValue){
   
   
        int i = find(oldValue);
        if(i == -1){
   
   
            return false;
        }
        intArray[i] = newValue;
        return true;
    }
     /**
     * 遍历
     */
    public void display(){
   
   
        for(int i = 0 ; i < elems ; i++){
   
   
            System.out.print(intArray[i]+" ");
        }
        System.out.print("\n");
    }
     /**
     * 冒泡排序
     * 每趟冒出一个最大数/最小数
     * 每次运行数量:总数量-运行的趟数(已冒出)
     */
    public void bubbleSort(){
   
   
        //排序趟数  n-1次就行了
        for(int i = 0; i < elems-1; i++){
   
   
            System.out.println("第"+(i+1)+"趟:");
            //每趟次数 (n-i) -1是防止下标越界,后面赋值用到了+1
            for(int j = 0; j < elems-i-1; j++){
   
   
                //控制按大冒泡还是按小冒泡
                if(intArray[j] > intArray[j+1]){
   
    
                    int temp = intArray[j];
                    intArray[j] =  intArray[j+1];
                    intArray[j+1] = temp;
                }
                display();
            }
        }
    }
    
     /***
     * 选择排序
     * 每趟选择一个最大数/最小数
     * 每次运行数量:总数量-运行的趟数(已选出)
     */
    public void selectSort(){
   
   
        for(int i = 0; i < elems-1; i++) {
   
   //排序趟数  n-1次就行了
            int min = i;
            for(int j = i+1; j < elems; j++){
   
    //排序次数 每趟选择一个数出来,-1次
                if(intArray[j] < intArray[min]){
   
   
                    min = j;
                }
            }
            if(i != min ){
   
   
                int temp = intArray[i];
                intArray[i] = intArray[min];
                intArray[min] = temp;
            }
            display();
        }
    }
	 /**
     * 插入排序
     * 每趟选择一个待插入的数
     * 每次运行把待插入的数放在比它大/小后面
     */
    public void insertSort(){
   
   
        int j;
        for(int i = 1; i < elems; i++){
   
   
            int temp = intArray[i];
            j = i;
            while (j > 0 && temp < intArray[j-1]){
   
   
                intArray[j] = intArray[j-1];
                j--;
            }
            intArray[j] = temp;
        }
        display();
    }
    public void display() {
   
   
    	System.out.println(array.toString());    
    }
    
    public static void main(String[] args) {
   
   
        Array array = new Array(10);
        array.add(6);
        array.add(3);
        array.add(8);
        array.add(2);
        array.add(11);
        array.add(5);
        array.add(7);
        array.add(4);
        array.add(9);
        array.add(10);
//        array.bubbleSort();
//        array.selectSort();
        array.insertSort();
//        array.display();
//        System.out.println(array.find(4));
//        System.out.println(array.delete(1));
//        array.display();
//        System.out.println(array.update(2,6));
//        array.display();
    }
}

四、Stack

1、介绍

  • 栈(stack)又称为堆栈或堆叠,栈作为一种数据结构,

    ​ 它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶

  • java中Stack是Vector的一个子类,只定义了默认构造函数,用来创建一个空栈。

  • 栈是元素的集合,其包含了两个基本操作:

    ​ push 操作可以用于将元素压入栈,

    ​ pop 操作可以将栈顶元素移除。

  • 遵循先进后出原则(LIFO, Last In First Out)。

  • 时间复杂度:

    ​ 索引: O(n)

    ​ 搜索: O(n)

    ​ 插入: O(1)

    ​ 移除: O(1)

    image-20200702144401094

2、适用场景:

	栈常应用于实现递归功能方面的场景,例如斐波那契数列。

3、模拟实现

public class Stack {
   
   
    
    //小贴士:通常可以利用栈实现字符串逆序,还可以利用栈判断分隔符是否匹配,如<a[b{c}]>,可以正进反出,栈为空则成功。
    
    
    /**基于数组实现的顺序栈,连续存储的线性实现,需要初始化容量**/
    //固定数据类型
    //private int[] array;
    //动态数据类型
    private Object[] objArray;
	private int maxSize;
    private int top;
    
    public Stack() {
   
   }
    
    public Stack(int maxSize) {
   
   
        if(maxSize > 0){
   
   
            objArray = new Object[maxSize];
            this.maxSize = maxSize;
            top = -1;
        }else{
   
   
            throw new RuntimeException("初始化大小错误:maxSize=" + maxSize);
        }
    }
    /**
     * 入栈
     * @param obj
     */
    public void objPush(Object obj){
   
   
        //扩容
        grow();
        //++在前表示先运算再赋值,优先级高,在后表示先赋值再运算,优先级低
        objArray[++top] = obj;
    }
	 /**
     * 出栈
     * @return
     */
    public Object objPop(){
   
   
        Object obj = peekTop();
        //声明原顶栈可以回收空间(GC)
        objArray[top--] = null;
        return obj;
    }
    /**
     * 查看栈顶
     * @return
     */
    public Object peekTop(){
   
   
        if(top != -1){
   
   
            return objArray[top];
        }else{
   
   
            throw new RuntimeException("stack is null");
        }
    }
    /**
     * 动态扩容
     */
    public void grow(){
   
   
        // << 左移运算符,1表示乘以2的1次方
        if(top == maxSize-1){
   
   
            maxSize = maxSize<<1;
            objArray = Arrays
16进制10进制.txt 32.txt asm.txt Crctable.txt C标志符命名源程序.txt erre.txt erre2.txt ff.txt for循环的.txt list.log N皇后问题回溯算法.txt ping.txt re.txt source.txt winsock2.txt ww.txt 万年历.txt 万年历的算法 .txt 乘方函数桃子猴.txt 乘法矩阵.txt 二分查找1.txt 二分查找2.txt 二叉排序树.txt 二叉树.txt 二叉树实例.txt 二进制数.txt 二进制数2.txt 余弦曲线.txt 余弦直线.txt 傻瓜递归.txt 冒泡排序.txt 冒泡法改进.txt 动态计算网络最长最短路线.txt 十五人排序.txt 单循环链表.txt 单词倒转.txt 单链表.txt 单链表1.txt 单链表2.txt 单链表倒序.txt 单链表的处理全集.txt 双链表正排序.txt 反出字符.txt 叠代整除.txt 各种排序法.txt 哈夫曼算法.txt 哈慢树.txt 四分砝码.txt 四塔1.txt 四塔2.txt 回文.txt 图.txt 圆周率.txt 多位阶乘.txt 多位阶乘2.txt 大加数.txt 大小倍约.txt 大整数.txt 字符串查找.txt 字符编辑.txt 字符编辑技术(插入和删除) .txt 完数.txt 定长串.txt 实例1.txt 实例2.txt 实例3.txt 小写数字转换成大写数字1.txt 小写数字转换成大写数字2.txt 小写数字转换成大写数字3.txt 小字库DIY-.txt 小字库DIY.txt 小孩分糖果.txt 小明买书.txt 小白鼠钻迷宫.txt 带头结点双链循环线性表.txt 平方根.txt 建树和遍历.txt 建立链表1.txt 扫描码.txt 挽救软盘.txt 换位递归.txt 排序法.txt 推箱子.txt 数字移动.txt 数据结构.txt 数据结构2.txt 数据结构3.txt 数组完全单元.txt 数组操作.txt 数组递归退出.txt 数组递归退出2.txt 文件加密.txt 文件复制.txt 文件连接.txt 无向图.txt 时间陷阱.txt 杨辉三角形.txt 栈单元加.txt 栈操作.txt 桃子猴.txt 桶排序.txt 检出错误.txt 检测鼠标.txt 汉字字模.txt 汉诺塔.txt 汉诺塔2.txt 灯塔问题.txt 猴子和桃.txt 百鸡百钱.txt 矩阵乘法动态规划.txt 矩阵转换.txt 硬币分法.txt 神经元模型.txt 穷举搜索法.txt 符号图形.txt 简单数据库.txt 简单计算器.txt 简单逆阵.txt 线性顺序存储结构.txt 线索化二叉树.txt 绘制圆.txt 编随机数.txt 网络最短路径Dijkstra算法.txt 自我复制.txt 节点.txt 苹果分法.txt 螺旋数组1.txt 螺旋数组2.txt 试题.txt 诺汉塔画图版.txt 读写文本文件.txt 货郎担分枝限界图形演示.txt 货郎担限界算法.txt 质因子.txt 输出自已.txt 迷宫.txt 迷宫问题.txt 逆波兰计算器.txt 逆矩阵.txt 逆阵.txt 递堆法.txt 递归桃猴.txt 递归车厢.txt 递推.txt 逻辑移动.txt 链串.txt 链栈.txt 链表十五人排序.txt 链表(递归).txt 链队列.txt 队列.txt 阶乘递归.txt 阿姆斯特朗数.txt 非递归.txt 顺序栈.txt 顺序表.txt 顺序队列.txt 骑士遍历1.txt 骑士遍历2.txt 骑士遍历回逆.txt 黑白.txt
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值