数据结构:这是一份全面& 详细的 线性表 学习指南
时间:2022-06-18
本文章向大家介绍数据结构:这是一份全面& 详细的 线性表 学习指南,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
前言
- 本文主要讲解 数据结构中最基础的线性表
- 内容包括其特点、结构(顺序存储结构 & 链式结构)等,希望你们会喜欢。
1. 简介
- 其中,线性表的存储结构最为重要 下面,我将主要讲解其 顺序存储结构 & 链式存储结构
2. 顺序存储结构
- 实现方式:数组
- 下面,我将主要讲解其结构特点 & 相关操作
2.1 结构特点
- 存储线性表的数据元素的方式 = 一段地址连续的存储单元
- 具备:起始位置、数组长度(最大存储容量) & 线性表长度(当前长度)
- 示意图
- 概念说明
概念 |
说明 |
---|---|
数组长度 |
存放线性表的空间长度(固定不变) |
线性表长度 |
存放线性表数据元素的长度(动态变化) |
地址 |
存储单元的编号 |
数组下标 |
第 i 个元素 = 数组下标第 i-1 的位置 |
2.2 对应操作
顺序存储结构的操作包括:插入 & 删除
- 操作1:插入
- 操作2: 删除
注:线性表的存取数据时间性能 = O(1)
2.3 结构优、缺点
2.4 应用场景
已知长度、数据元素个数变化不大、存、取数据频率高的场景
2.5 总结
3. 链式存储结构
- 实现方式:链表
- 下面,我将主要讲解其结构特点 & 相关操作
3.1 结构特点
- 链表示意图(以单链表为例)
- 存储元素说明示意图
下面将主要重点讲解各种链表:(重点讲解)单链表、双向链表、循环链表、静态链表
3.2 单链表
3.2.1 定义
每个结点只有1个指针域
3.2.2 示意图
3.2.3 操作(读取、插入、删除、整表创建、整表删除)
- 读取 算法思路:工作指针向后移
int j ;
// 1. 声明1动态指针
LinkList p ;
// 2. 让p指向链表L的第一个结点
// L = 头结点
p = L ->next
// 3. 设置计数器
j = 1;
while ( p && j<i ){
p = p->next;// 指向下一个结点
++j;
}
// 直到到了i结点,直接取出
e = p->data
- 插入
通过遍历找到i结点,生成一个空结点,然后插入
- 删除
- 整表创建
- 整表删除
- 单链表实现
http://blog.csdn.net/chenleixing/article/details/42392283
public class UnidirectionalLinkedList<T> {
/**
* 设置结点结构
*/
// a. 结点结构
private class Node<T>{
private T data;
private Node<T> next ;
public Node(T data){
this.data = data;
}
}
// b. 头结点
private Node<T> first;
// c. 当前结点
private Node<T> currentNode;
// d. 链表长度
private int size;
/**
* 构造函数
* 作用:初始化结点
*/
public UnidirectionalLinkedList(){
currentNode = first = null;
size = 0;
}
/**
* 1. 添加结点
* 内容:在头 / 尾 添加结点 & 在特定位置插入结点
*/
// a. 在链表头部加入1个结点
// 即,把新加入的结点设置为第一结点
public void addFirstNode(T data){
// 1. 将需添加的内容封装成结点
Node<T> newNode = new Node<T>(data);
// 2. 将新添加结点的指针域指向旧第1个结点
newNode.next = first;
// 3. 将新添加结点设置为第1个结点
first = newNode;
// 4. 链表长度+1
size++;
}
// b. 在链表尾部加入1个结点
// 即,把新加入的结点设置为最后结点
public void addNode(T data){
// 1. 检查当前链表是否为空
if (isEmpty()){
addFirstNode(data);
return;
}
// 2. 把当前指针定位到最后一个结点
locateNode(size-1);
// 3. 将需添加的内容封装成结点
currentNode.next = new Node<T>(data);
// 4. 链表长度+1
size++;
}
// c. 在链表中插入结点
public T insertNode(int index, T data) {
// 1. 检查当前链表是否为空
if (isEmpty()){
addFirstNode(data);
return null;
}
// 2. 把当前指针定位到需插入的结点位置
locateNode(index);
// 3. 将需添加的内容封装成结点
Node<T> insertNode = new Node<T>(data);
// 4. 把需插入结点位置的下1个结点 赋给 插入的结点
insertNode.next = currentNode.next;
// 5. 把插入结点 赋给 需插入的结点的位置
currentNode.next = insertNode;
// 6. 链表长度+1
size++;
// 7. 返回插入结点的数据
return insertNode.data;
}
/**
* 2. 删除结点
* 内容:删除第1个结点 & 删除特定位置的结点
*/
// a. 删除第1个结点,并返回该结点数据
public T removeFirstNode() {
// 1. 检查当前链表第一个结点是否为空
if (first == null){
try {
throw new Exception("链表为空!");
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. 获取被删除结点的数据
T temp = first.data;
// 3. 将第2个结点设置为第1个结点
first = first.next;
// 4. 链表长度减1
size--;
// 5. 返回被删除结点的数据
return temp;
}
// b. 删除特定位置的结点,并将里面的数据返回
public T removeNode(int index) {
// 1. 检查当前链表是否为空
if (isEmpty()){
try {
throw new Exception("链表为空!");
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. 把当前指针(currentNode)定位到 需删除结点(index)的前1个结点
locateNode(index-1);
// 3. 获取被删除结点的数据
T temp = currentNode.next.data;
// 4. 将需删除结点(index)的前1个结点 的下1个结点 设置为 需删除结点(index)的下1个结点
currentNode.next = currentNode.next.next;
// 5. 链表长度减1
size--;
// 6. 返回被删除结点的数据
return temp;
}
/**
* 3. 获取特定位置的结点
* 内容:将当前指针(currentNode)定位到所需结点位置、根据索引位置获取结点数据
*/
// a. 将当前指针(currentNode)定位到所需结点位置
private void locateNode(int index){
// 1. 判断指针是否越界
if (index <0 && index >size){
try {
throw new IndexOutOfBoundsException("参数越界!");
} catch (Exception e) {
e.printStackTrace();
}
}
int i = 0;
// 2. 通过遍历链表,寻找索引index所指结点
for(currentNode = first; currentNode.next != null && i < index; i++){
currentNode = currentNode.next;
}
}
// b. 根据索引位置获取结点数据
public T getNode(int index) {
// 1. 判断链表是否为空
if (isEmpty()){
try {
throw new Exception("链表为空!");
} catch (Exception e) {
e.printStackTrace();
}
}
// 2. 把当前指针(currentNode)定位到 所需索引位置(index)
locateNode(index);
// 3. 返回当前指针的数据,即所需索引位置的数据
return currentNode.data;
}
/**
* 检查当前链表是否为空
*/
public boolean isEmpty(){
if (size == 0){
return true;
}else {
return false;
}
}
public static void main(String[] args){
// 1. 创建链表 & 加入结点数据
UnidirectionalLinkedList<Integer> list = new UnidirectionalLinkedList<Integer>();
list.addNode(1);
list.addNode(2);
list.addNode(3);
list.addNode(4);
list.addNode(5);
// 2. 输出当前链表数据
System.out.println("链表数据如下:");
for (int i = 0; i < list.size;i++){
try {
System.out.print(list.getNode(i)+" ");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("-----------------------");
// 3. 获得某个位置结点的数据
System.out.println("位置3的数据是:" + list.getNode(3));
System.out.println("-----------------------");
// 4. 插入结点:在位置4插入,数据 = 66
System.out.println("在位置4插入的data:"+list.insertNode(3,66));
System.out.println("插入后:");
for (int i = 0; i < list.size;i++){
try {
System.out.print(list.getNode(i)+" ");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("-----------------------");
// 5. 删除结点
System.out.println("删除index为3的data:"+list.removeNode(3));
System.out.println("删除后:");
for (int i = 0; i < list.size;i++){
try {
System.out.print(list.getNode(i)+" ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 测试结果
链表数据如下:1 2 3 4 5
-----------------------
位置3的数据是:4
-----------------------
在位置4插入的data:66
插入后:1 2 3 4 66 5
-----------------------
删除index为3的data:4
删除后:1 2 3 66 5
3.3 循环链表
- 定义 将单链表的终端结点的指针指向头结点、使得单链表头尾相接、形成1个环
也称单循环链表
- 示意图
3.4 双向链表
3.4.1 定义
每个结点有2个指针域:1指向后驱结点元素、2指向前驱结点元素
即 在单链表的结点中,再设置一个指向前驱结点的指针域
3.4.2 示意图
3.4.3 链表操作(插入& 删除)
注:插入 & 删除都需要同时改变2个指针变量
3.4.4 特点
- 缺点:占用空间多 = 记录2个指针
- 优点:算法时间性能高 = 良好对称性(前、后指针)
即,用空间换时间
3.5 静态链表
4. 存储结构对比
5. 总结
- 本文主要讲解了数据结构中最基础的线性表
- 下面我将继续对 数据结构,有兴趣可以继续关注Carson_Ho的安卓开发笔记
请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!
- C加加游戏编程,大神十年的绝技,正确的入门,这才叫学习
- 我们应该担心吗?人工智能现在可以通过交谈来学习新单词!
- 印度财政部:比特币是纯粹投机行为 区块链资产是“庞氏骗局”
- 法律人工智能实验室成立,法官和律师会丢饭碗吗?
- 让GridView中CheckBox列支持FireFox
- 在ASP.NET MVC中通过URL路由实现对多语言的支持
- AI加持下的假肢将会越来越聪明
- 通过几个Hello World感受.NET Core全新的开发体验
- ASP.NET MVC三个重要的描述对象:ControllerDescriptor
- 基于自制数据集的MobileNet-SSD模型训练
- .NET Core采用的全新配置系统[1]: 读取配置数据
- ASP.NET MVC三个重要的描述对象:ActionDescriptor
- 升级比特币区块链后,以特币已叩响成功的大门
- .NET Core采用的全新配置系统[2]: 配置模型设计详解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android自定义View 仿QQ侧滑菜单的实现代码
- Android view随触碰滑动效果
- TextView使用SpannableString设置复合文本 SpannableString实现TextView的链接效果
- FragmentTabHost使用方法详解
- Android编程实现仿优酷圆盘旋转菜单效果的方法详解【附demo源码下载】
- Android绘制圆形百分比加载圈效果
- Android自定义view实现动态柱状图
- Kubernetes集群高可用&备份还原概述 | 知识分享月第三期直播回顾
- SpringCloud2020 学习笔记(一)springboot和springcloud技术选型以及版本选择
- SpringCloud2020 学习笔记(二)父工程搭建
- SpringCloud2020 学习笔记(三) cloud-api-commons通用模块搭建
- SpringCloud2020 学习笔记(四) cloud-provider-payment8001支付模块
- SpringCloud2020 学习笔记(五)cloud-consumer-order80 消费者订单模块
- SpringCloud2020 学习笔记(六)如何开启idea中的Run DashBoard or Services
- SpringCloud2020 学习笔记(七)cloud-eureka-server7001 EurekaServer服务端安装