数据结构与算法--------Java实现(第二章数据结构 上)

第二章 数据结构

1.数据结构的概述

  1. 数据结构是一切算法实现的基础,也是程序设计语言的基础。
  2. 数据结构是计算机对数据的一种存储和组织方式,同时也泛指相互之间存在一种或者多种关系的数据的集合。

2.数据结构的基本含义

  1. 一个数据结构是由数据元素依据某种逻辑联系组织起来的,对数据元素间逻辑关系的描述称为数据的逻辑结构。由于数据必须在计算机内存储,数据的存储结构是其在计算机内的表示,也就是数据结构的实现形式。

3.数据结构中的基本概念

  1. 数据(Data): 数据是信息的载体;
  2. 数据元素(Data Element):数据的基本单位;
  3. 数据结构(Data Structure):数据之间的相互关系,也就是数据的组织形式。

4.数据结构的内容

  1. 数据的逻辑结构:数据元素之间的逻辑关系
  2. 数据的存储结构:数据元素及其逻辑关系在计算机存储器中的表现形式
  3. 数据的运算:对数据施加的操作
  4. 数据结构是一个有机的整体

同一个逻辑结构可以有不同的存储结构(比如:线性表可以分为 顺序表 和 链表);

同一个存储结构也可以有不同的数据运算集合(比如:线性表的插入和删除位置不同得到 堆和栈)。

5.数据结构的分类

  1. 线性结构:各个结点之间是线性关系

线性结构是非空集合

有且仅有一个开始结点和一个终结点

最多只有一个直接前驱结点和一个直接后继结点

    2.非线性结构:各个结点之间是多对应关系

非线性结构是非空集合

一个结点可能有多个直接前驱结点和直接后继结点

6.数据结构的几种存储方式

  1. 顺序存储:在一块连续的存储区域内一个接一个存储,主要用于描述现行逻辑结构的存放
  2. 链接存储:不要求逻辑上相邻的结点在物理位置上也相邻,结点间的逻辑关系用引用字段表示
  3. 索引存储:采用附加索引表的方式存储结点信息,分为稠密索引和稀疏索引两种
  4. 散列存储:根据节点的关键字直接计算出结点在存储地址中的位置

7.数据类型 

  1. 基本数据类型:值不能进一步分解 eg:整型、字符型、浮点型
  2. 聚合数据类型:可以进一步分解为若干个分量 eg:数组、用户自定义数据类型
  3. 抽象数据类型ADT:数据的组织及其相关操作,可以看做是数据的逻辑结构及其逻辑结果上定义的操作

一般具有两个重要特征:数据抽象 、数据封装

优点:将数据和操作封装在一起,实现了信息的隐藏。

在Java中是使用接口来表示ADT,用接口的实现类来实现ADT,ADT是概念层的抽象,而接口是实现层的抽象。

8.常用的数据结构

数组(Array)、栈(Stack)、队列(Queue)、链表(Linked List)、树(Tree)、图(Graph)、堆(Heap)、散列表(Hash)

9.选择合适的数据结构解决实际问题

数值计算问题:数学和工程的计算算法

非数值计算问题:数据结构

10.线性表

       1 .什么是线性表

         由n(n>0)个数据元素组成的有限序列.

       2. 线性表的基本运算

         初始化、计算表长度、获取结点、查找结点、插入结点、删除结点

11.顺序表结构

        1.按照顺序存储方式存储的线性表,该线性表的结点按照逻辑次序一次存放在计算机的一组连续的存储单元中。2

        2.操作顺序表运算的基本规则 : LOC(ai)= LOC(a1) + (i-1)* c   (1<= i <= n)

        3.顺序表结构的程序设计

         代码展示:

import java.util.Scanner;

/**
 * 准备数据
 * 顺序表本身就是一个数组
 */
//顺序表数据元素的类
class DATA {
    String key;                                 //结点的关键字
    String name;
    int age;
}                                               //定义结点

//顺序表的类
class SLType {                                  //定义顺序表结构
    static final int MAXLEN = 2;              //定义顺序表的最大长度(常量名称全大写)
    DATA[] ListData = new DATA[MAXLEN + 1];    //保存结构表的结构数组
    int ListLen;                                //顺序表已存结点的数量

    //初始化数据
    void SLInit(SLType SL) { //初始化顺序表
        SL.ListLen = 0;       //初始化为空表
    }

    //计算顺序表长度(计算线性表L中结点的个数)
    int SLLength(SLType SL) {
        return SL.ListLen;
    }

    //插入结点
    int SLInsert(SLType SL, int n, DATA data) {
        int i;
        if (SL.ListLen >= MAXLEN) {
            System.out.println("顺序表已满,无法继续插入");
            return 0;       //表示插入失败
        }
        if (n < 1 || n > SL.ListLen - 1) {
            System.out.println("插入元素序号错误,不能插入!");
            return 0;
        }
        //将顺序表中的数据向后移动
        for (i = 0; i < SL.ListLen; i++) {
            SL.ListData[i + 1] = SL.ListData[i];
        }
        SL.ListData[n] = data;
        SL.ListLen++;
        return 1;   //插入成功
    }
    
    //追加结点
    int SLAdd(SLType SL, DATA data) {
        if (SL.ListLen >= MAXLEN) {
            System.out.println("顺序表已满,无法继续插入数据");
            return 0;
        }
        SL.ListData[++SL.ListLen] = data;
        return 1;
    }
    
    // 删除结点
    int SLDelete(SLType SL, int n) {
        int i;
        if (n < 1 || n > SL.ListLen + 1) {
            System.out.println("删除结点序号错误,无法删除!");
            return 0;
        }
        for (i = 0; i < SL.ListLen; i++) {
            SL.ListData[i] = SL.ListData[i + 1];
        }
        SL.ListLen--;
        return 1;
    }

    /**
     * 查找结点(根据序号返回查询结点)
     */

    //按照序号查找结点
    DATA SLFindByNum(SLType SL, int n) {
        if (n < 1 || n > SL.ListLen) {
            System.out.println("结点序号错误,无法返回结点!");
            return null;
        }
        return SL.ListData[n];
    }

    //按照关键字查找结点
    int SLFindByCont(SLType SL, String key) {
        int i;
        for (i = 1; i <= SL.ListLen; i++) {
            if (SL.ListData[i].key.compareTo(key) == 0) {
                return i;       //返回结点序号
            }
        }
        return 0;
    }

    //显示所有结点
    int SLAll(SLType SL) {
        int i;
        for (i = 1; i <= SL.ListLen; i++) {
            System.out.println(SL.ListData[i].key + " " + SL.ListData[i].name + " " + SL.ListData[i].age);
        }
        return 0;
    }
}
//-------------------------------------------------------------------//
/**
 * 利用前面顺序表的基本运算完成对数据表的操作
 */
public class SortTable {
    public static void main(String[] args) {
        
        int i;
        SLType SL = new SLType();
        DATA pdata;
        String key;

        System.out.println("顺序表操作演示!");

        SL.SLInit(SL);  //初始化顺序表
        System.out.println("顺序表初始化完成");
        Scanner scanner = new Scanner(System.in);

        do {    //循环添加结点数据
            System.out.println("请输入添加结点的(学号 姓名 年龄)");
            DATA data = new DATA();
            data.key = scanner.next();
            data.name = scanner.next();
            data.age = scanner.nextInt();
            if (data.age != 0) {    //若年龄不等于0,开始添加
                if (SL.SLAdd(SL, data) == 0) {   //若添加结点失败
                    break;
                }
            } else {  //若年龄为0
                break;
            }

        } while (true);
        System.out.println("顺序表中的结点顺序为:");
        SL.SLAll(SL);   //显示所有结点顺序

        System.out.println("要取出结点的序号:");
        i = scanner.nextInt();
        pdata = SL.SLFindByNum(SL, i);
        if (pdata != null) {
            System.out.println("第" + i + "个结点为:" + pdata.key + " " + pdata.name + " " + pdata.age);
        }

        System.out.println("要查找结点的关键字:");
        key = scanner.next(); //输入要查找结点的关键字
        i = SL.SLFindByCont(SL, key);
        pdata = SL.SLFindByNum(SL, i);
        if (pdata != null) {
            System.out.println("关键字为" + key + "的结点为:" + pdata.key + " " + pdata.name + " " + pdata.age);
        }
    }
}


//结果:
顺序表操作演示!
顺序表初始化完成
请输入添加结点的(学号 姓名 年龄)
张
张三
18
请输入添加结点的(学号 姓名 年龄)
李
李四
30
请输入添加结点的(学号 姓名 年龄)
王
王五
30
顺序表已满,无法继续插入数据
顺序表中的结点顺序为:
张 张三 18
李 李四 30
要取出结点的序号:
1
第1个结点为:张 张三 18
要查找结点的关键字:
李
关键字为李的结点为:李 李四 30

    代码较长,建议放在开发工具中查看。

    4.顺序结构的缺点:

            4.1插入和删除结点时往往需要移动大量的数据

            4.2如果表较大,有时难以分配足够的存储空间,导致内存分配失败。

12.链表结构

         1.什么是链表结构:是一种动态存储分配的结构形式,可以根据需要动态申请所需的内存单元。

         2.链表结构的组成:

            数据部分:保存实际数据

            地址部分:保存下一个结点的地址

         3.优缺点

                       优点:逻辑连续实际地址不连续,保存大量数据时不需要分配大量的连续空间。

                        缺点:每个结点都需要额外保存一个引用变量,占用存储空间

          4.链表分类:

                  单链表、双向链表、单循环链表、多重链的循环链表

           5.代码展示

import java.util.Scanner;

/**
 * 1.准备数据
 */

//链式表数据元素的类
class DATA {
    String key;                                 //结点的关键字
    String name;
    int age;
}                                               //数据结点类型

//链式表的类
class CLType {                                  //定义链式表结构
    DATA nodeData = new DATA();                 //保存具体数据
    CLType nextNode;                            //指向下一个结点

    /**
     * 追加结点
     * 典型追加步骤:
     * 1.分配内存地址保存新增结点
     * 2.从头引用head逐个检查,找到尾结点
     * 3.将表尾结点的地址设置为新增结点的地址
     * 4.新增结点的地址设置为null
     */
    CLType CLAddEnd(CLType head, DATA nodeData) {
        CLType node, htemp;
        if ((node = new CLType()) == null) { //通过new关键字申请保存结点的内存空间
            System.out.println("申请内存失败!");
            return null;
        } else {
            node.nodeData = nodeData; //保存数据
            node.nextNode = null; //设置新增结点引用为空,因为新增节点将是尾节点,所以引用地址为null
            if (head == null) {     //头引用
                head = node;
                return head;
            }
            htemp = head;
            while (htemp.nextNode != null) {   //查找链表的末尾
                htemp = htemp.nextNode;   //逐个查找,直到找到尾结点
            }
            htemp.nextNode = node;    //将新增结点设置为尾节点的引用节点,即产生了新的尾节点
            return head;
        }
    }

    /**
     * 插入头结点
     * 1.分配内存空间,保存新增的结点
     * 2.使新增结点指向头引用head所指向的结点
     * 3.使头引用head指向新增结点
     */
    CLType CLAddFirst(CLType head, DATA nodeData) {
        CLType node;
        if ((node = new CLType()) == null) {
            System.out.println("申请内存失败!");
            return null;
        } else {
            node.nodeData = nodeData; //保存新增结点数据
            node.nextNode = head;     //指向头引用所指向的结点
            head = node;               //头引用指向新增结点,即新增结点变为头结点
            return head;
        }
    }

    /**
     * 查找结点
     * 在链表中查找需要的元素
     * 通过关键字查找需要元素
     */
    CLType CLFindNode(CLType head, String key) {
        CLType htemp;
        htemp = head;
        while (htemp != null) {
            //如果当前结点的关键字和传入的关键字相同
            if (htemp.nodeData.key.compareTo(key) == 0) {
                return htemp;
            }
            htemp = htemp.nextNode;    //处理下一个结点
        }
        return null;     //返回空引用
    }

    /**
     * 插入结点(在链表中间指定位置插入数据)
     * 1.分配内存地址,保存新增的结点
     * 2.从head开始找到要插入的逻辑位置,也就是位于那两个结点之间
     * 3.修改要插入位置前结点的指向为新插入的结点,修改新插入结点的为后结点
     */
    CLType CLInsert(CLType head, String findKey, DATA nodeData) {

        CLType node, nodeTemp;
        if ((node = new CLType()) == null) {
            System.out.println("申请内存失败!");
            return null;
        }
        node.nodeData = nodeData; //保存新增的结点
        nodeTemp = CLFindNode(head, findKey);   //找出要插入的位置的关键结点

        if (nodeTemp != null) {
            node.nextNode = nodeTemp.nextNode;   //新插入结点指向关键结点的下一个结点
            nodeTemp.nextNode = node;              //关键结点的下一个结点指向新增结点
        } else {
            System.out.println("未找到要正确插入的位置!");
        }
        return head;
    }

    /**
     * 删除结点
     * 1.查找需要删除的结点
     * 2.使前一个结点指向要删除节点的下一个结点
     * 3.删除结点
     */

    int CLDelete(CLType head, String key) {
        CLType node, hTemp;      //node保存删除结点的前一结点
        hTemp = head;
        node = head;
        while (hTemp != null) {
            if (hTemp.nodeData.key.compareTo(key) == 0) {  //找到关键字,执行删除
                node.nextNode = hTemp.nextNode;   //使前一结点指向当前结点的下一结点
                hTemp = null;                       //释放内存
                return 1;
            } else {
                node = hTemp;                       //指向当前结点
                hTemp = hTemp.nextNode;             //指向下一节点
            }
        }
        return 0;                                   //删除失败
    }

    /**
     * 计算链表长度
     */
    int CLLength(CLType head) {
        CLType htemp;
        int Len = 0;
        htemp = head;
        while (htemp != null) {        //遍历整个列表
            Len++;
            htemp = htemp.nextNode;
        }
        return Len;
    }

    /**
     * 显示所有结点
     */
    void CLAllNode(CLType head) {
        CLType htemp;
        DATA nodeData;
        htemp = head;
        System.out.println("当前链表中共存放了" + CLLength(head) + "条数据,其结构如下:");
        while (htemp != null) {
            nodeData = htemp.nodeData;  //循环每个结点的值
            System.out.println(nodeData.key + " " + nodeData.name + " " + nodeData.age);
            htemp = htemp.nextNode;
        }
    }
}
//-------------------------------------------------------------------------

/**
 * 2.对链式表的操作
 */
public class LInkedTable {

    public static void main(String[] args) {
        //初始化
        CLType node, head = null;
        CLType CL = new CLType();
        String key, findKey;


        Scanner input = new Scanner(System.in);
        System.out.println("链表测试.先输入链表中的数据,格式为(关键字 姓名 年龄)");
        //添加数据
        do {
            DATA nodeData = new DATA();
            nodeData.key = input.next();
            if (nodeData.key.equals("0")) {
                break;                   //若输入0 则退出,为了不一直循环
            } else {
                nodeData.name = input.next();
                nodeData.age = input.nextInt();
                head = CL.CLAddEnd(head, nodeData);  //在链表尾追加数据
            }
        } while (true);
        CL.CLAllNode(head);         //显示所有结点

        System.out.println("演示插入结点,输入插入位置的关键字:");
        findKey = input.next();
        System.out.println("请输入要插入的结点数据:(关键字(非0) 姓名 年龄)");
        DATA nodeData = new DATA();
        nodeData.key = input.next();
        nodeData.name = input.next();
        nodeData.age = input.nextInt();
        CL.CLInsert(head, findKey, nodeData);
        CL.CLAllNode(head);         //显示所有结点

        System.out.println("演示删除结点,输入要删除结点的关键字:");
        key=input.next();
        CL.CLDelete(head,key);
        CL.CLAllNode(head);         //显示所有结点

        System.out.println("演示在链表中查找,输入要查找结点的关键字:");
        key=input.next();
        node = CL.CLFindNode(head, key);
        if(node==null){
            System.out.println("查询的结点"+key+"不存在!");
        }else{
            nodeData = node.nodeData;
            System.out.println("关键字对应的节点为:"+nodeData.key+" "+nodeData.name+" "+nodeData.age);
        }

    }
}

//结果:
链表测试.先输入链表中的数据,格式为(关键字 姓名 年龄)
        1
        张三
        18
        2
        里斯
        20
        3
        王五
        30
        0
        当前链表中共存放了3条数据,其结构如下:
        1 张三 18
        2 里斯 20
        3 王五 30
        演示插入结点,输入插入位置的关键字:
        2
        请输入要插入的结点数据:(关键字(非0) 姓名 年龄)
        4
        赵六
        32
        当前链表中共存放了4条数据,其结构如下:
        1 张三 18
        2 里斯 20
        4 赵六 32
        3 王五 30
        演示删除结点,输入要删除结点的关键字:
        4
        当前链表中共存放了3条数据,其结构如下:
        1 张三 18
        2 里斯 20
        3 王五 30
        演示在链表中查找,输入要查找结点的关键字:
        3
        关键字对应的节点为:3 王五 30

           6.动手永远比只用眼睛看效果要好。

温馨提示:代码放在文章中感觉太占篇幅了,后面的长代码我将放在github上,希望有需要的伙伴自行下载借鉴指导。

Github地址:https://github.com/flakkaqi/DataStructure_and_Algorithms.git

你可能感兴趣的:(数据结构与算法)