1.数据结构的概述
2.数据结构的基本含义
3.数据结构中的基本概念
4.数据结构的内容
同一个逻辑结构可以有不同的存储结构(比如:线性表可以分为 顺序表 和 链表);
同一个存储结构也可以有不同的数据运算集合(比如:线性表的插入和删除位置不同得到 堆和栈)。
5.数据结构的分类
线性结构是非空集合
有且仅有一个开始结点和一个终结点
最多只有一个直接前驱结点和一个直接后继结点
2.非线性结构:各个结点之间是多对应关系
非线性结构是非空集合
一个结点可能有多个直接前驱结点和直接后继结点
6.数据结构的几种存储方式
7.数据类型
一般具有两个重要特征:数据抽象 、数据封装
优点:将数据和操作封装在一起,实现了信息的隐藏。
在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