- 什么是链表
- 概念
- 链表是一种物理存储单元上非连续/非顺序的存储结构.
- 节点
- 概念
- 所谓链表即是由多个节点组成,像一条铁链,铁链的每一个环就相当于链表的节点.
- 组成
- 数据域
- 一个节点用于储存数据的内存控件
- 指针域
- 一个节点用于储存和它连接的下一个节点地址的内存空间
- 数据域
- 概念
- 优点
- 创建节点
- 不需要指定内存空间的大小,非常具有灵活性
- 删除节点
- 删除一个节点不需要像数组那样将其余的数据整体向前移动,只需要修改节点的指针域即可完成删除节点的操作
- 创建节点
-
- 缺点
- 访问节点
- 可以通过循环或者递归访问到链表的任何数据,但是访问效率低于数组(线性数据结构)
- 储存方面
- 由于不是线性结构,并且相对于线性的储存结构来说,需要保存每个节点的指针域,这又是一笔内存开销,占用了内存空间,对内存空间的利用效率低
- 访问节点
- 缺点
- 概念
- 链表的种类
- 方向
- 单向链表
- 是连接方向单一的链表,从头结点开始可以一直指向尾节点,方向不会改变(即从头结点走到尾节点只有一条路径).其中每一个节点的指针域只有一个指向,也就是一个节点有一个指针,可以保存下一个节点
- 双向链表
- 每一个节点的指针域中保存有两个指针(引用),可以同时指向该节点的前驱节点和后继节点
- 单向链表
- 形状
- 单链表
- 单链表:是一种逻辑上的线性结构,只有一条链,首尾不相接
- 环形链表
- 环形链表:首尾相接的链表(类比丢手帕)(约瑟夫问题)
- 单链表
- 方向
- 如何创建链表
- 思路分析
- 图形化结构
- 链表
- 节点
- 节点类图
- 节点属性
- 数据域 : 节点中储存数据内存控件
- 指针域 : 节点中储存其他节点(可能是下一个,也可能是上一个)的地址信息的内存空间
- 类图
- [Java]链表类图
- 文本思路
- 单向链表
- 双向链表
- 单向环形链表
- 双向环形链表
- 图形化结构
- 代码实现
- 思路分析
- 如何使用链表
- 节点
- 属性
- 数据域 : Object data
- 指针域 : Node nextNode
- 方法
- 查询节点数据 : private Object getData()
- 修改节点数据 : private void setData(Object newData)
- 属性
- 链表
- 属性
- 长度 : int length = 0
- 头结点引用 : Node firstNode = null
- 前驱节点引用 : Node lastNode = null
- 临时节点引用 : Node point = null
- 方法
- 初始化链表(利用构造器) : public LinkedList()
- 增加尾节点 : public void add()
- 插入指定节点 : public void insert(int index)
- 删除尾节点 : public void del()
- 删除指定节点 : public void del(int index)
- 修改尾节点 : public void update(Object newData)
- 修改指定节点 : public void update(int index, Object newData)
- 获取尾节点 : public Node getNode()
- 获取指定节点 : public Node getNode(int index)
- 链表长度 : public int length()
- 判断空链表 : public boolean isEmpty()
- 排序 : public void sort()
- 截取链表 : public LinkedList subLinkedList(int start, int end)
- 属性
- 节点
- 如何优化链表
- 算法方面
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 检索算法
- 排序算法
- 操作方法
- 待续
- 算法方面
package linkedList; /** * 节点类(单向链表) * @author 王一航 */ class Node { //数据域 Object data; //指针域 Node nextNode = null;//单向链表因此只有一个指向自身类型的引用 //构造函数 public Node(Object data){ this.data = data; } }
package linkedList;
/**
* 单向链表类
* @author 王一航
*/
public class LinkedList {
/**
* 成员变量
*/
int length = 0;
Node firstNode = null;
Node lastNode = null;
Node point = null;
/**
* 成员方法
*/
//初始化链表(利用构造器)
public LinkedList(){
int length = 0;
Node firstNode = null;
Node lastNode = null;
Node point = null;
}
//增加尾节点
public void add(Object data){
insert(length, data);
}
//插入指定节点
public void insert(int index, Object data){
Node temp = getNode(index);
Node node = new Node(data);
if(index > 0){
getNode(index - 1).nextNode = node;
}else{
firstNode = node;
}
node.nextNode = temp;
length++;
}
//删除尾节点
public void del(){
del(length - 1);
}
//删除指定节点
public void del(int index){
//TODO 检查入口参数
if(index > 0){
getNode(index - 1).nextNode = getNode(index + 1);
length--;
}
if(index == 0){
firstNode = getNode(1);
length--;
}
}
//修改尾节点
public void update(Object newData){
update(length - 1, newData);
}
//修改指定节点
public void update(int index, Object newData){
getNode(index).data = newData;
}
//获取尾节点
public Node getNode(){
return getNode(length - 1);
}
//获取指定节点
public Node getNode(int index){
try{
point = firstNode;
int count = 0;
while(count < index){
point = point.nextNode;
count++;
}
return point;
}catch(NullPointerException e){
System.out.println("请检查链表是否越界! 链表长度 : " + length);
return null;
}
}
//链表长度
public int length(){
return length;
}
//判断空链表
public boolean isEmpty(){
return length == 0 ? true : false;
}
//排序
public void sort(){
//TODO 完成排序功能
System.out.println("请完成排序功能!");
}
//截取链表
public LinkedList subLinkedList(int start, int end){
if(end >= start){
firstNode = getNode(start);
getNode(end - start).nextNode = null;
length = end - start;
}
return this;
//TODO 释放不需要的内存
}
//打印链表所有元素
public void show(){
for(int i = 0; i < length; i++){
System.out.println("索引 : " + i + " 第" + (i+1) + "个" + getNode(i).data);
}
}
}
经验总结(个人理解):
关于健壮性 :
当某一个方法需要入口参数的时候,必须对入口参数的合法性进行检查(以后要将这种意识变成一种意识)
关于代码结构 :
关于方法的组织 :
良好的代码中方法的组织结构应该具有”高内聚低耦合”的特点
高内聚 :
低耦合 :
关于属性和方法的命名 :
关于类图的使用 :
注意 :
1 . 杜绝魔术数字的出现,程序中一旦需要使用到某些常量,则必须首先声明常量为一个有意义的名字,然后根据这个名字去访问该常量