链表集合是java中的多种集合之一,从名字就能听的出,这种集合它的底层实现使用了链表。
链表是一种数据结构,它的排列方式很像一根链条,链表是由很多节点组成的,每个节点包含三个属性,previous,Value,next
previous存储着上一个节点,next指向下一个节点,value存储着当前节点的数据,链表就是使用这样的方式将每个节点连接在一
起,像链条一样,环环相扣
使用链表的好处在于,它的插入和修改的效率很高,这个和数组不同,拿数组来说
[1,3,4,6,8,9,2]
例如我们想在下标1的位置插入一个元素,那么就需要将后面元素的都整体向后移动一位
但如果使用链表的话,就不需要这么麻烦,在java中,我们向链表的某个位置插入一个元素时,它底层实现是这样的
例如我想在第3个节点后面插入一个新节点,那么它只需要将第3个节点的next属性指向新节点,在将第4个节点的previous属性也指向新节点,然后将新节点的previous属性指向节点3,在将新节点的next属性指向节点4,这样就将新节点放到节点3和节点四之间了。也就是将节点3的下一个节点的位置指向新节点,在将节点4的上一个节点的位置指向新节点,然后将新节点的previous和next属性分别指向节点3和节点4。
那么删除也是一样的,例如想删除节点3,那么只需要让节点2的next不在指向节点3,节点4的previous也不在指向节点3,那么这样就把节点3删除了。
链表的增删改的效率很高,但是查询的效率很低,从它结构就能看的出,它需要访问每个节点的next属性才能到下一个节点中去,所以查询很慢
好了,链表节点的介绍就到这里,下面贴出js实现的链表集合源码
/**
* 链表中的每个节点
* @param {*} element 本节点的数据
* @param {*} previous 上一个节点
* @param {*} next 下一个节点
*/
function Node(element){
this.previous = null;
this.element = element;
this.next = null;
}
Node.prototype.getElement=function(){
return this.element;
}
/**
* 链表 构造函数
* @param {*} first 头部节点
* @param {*} last 尾部节点
* @param {*} size 链表集合的长度
*/
function LinkedList(){
this.first = null;
this.last = null;
this.size = 0;
}
//[a,b,c]
/**
* [a,b,c,d,e,f,g]
* 往链表中添加元素
* @param element 插入的元素
*/
LinkedList.prototype.add = function(element){
this.size++;
let node = new Node(element);
if(this.first==null){
this.first = node;
this.last = node;
}else{
this.last.next = node;
node.previous = this.last;
this.last = node;
}
}
LinkedList.prototype.insert = function(element,index){
let currentNode = this.get(index);
let node = new Node(element);
if(index==0){
currentNode.previous = node;
node.next = currentNode;
this.first = node;
}else if(index==this.size-1){
currentNode.next = node;
node.previous = currentNode;
this.last = node;
}else{
currentNode.previous.next = node;
node.next = currentNode;
node.previous = currentNode.previous;
currentNode.previous = node;
}
this.size++;
}
/**
* 打印链表中的元素
*/
LinkedList.prototype.toString = function(){
let str = "["+this.first.element;
let nextNode = this.first.next;
for(let i=0;i<this.size;i++){
if(nextNode==null){
break;
}
str+=","+nextNode.element;
nextNode = nextNode.next;
}
str+="]"
console.log(str);
}
/**
* 返回链表的长度
*/
LinkedList.prototype.getSize = function(){
return this.size;
}
/**
* 返回指定下标的元素
* @param index 下标
*/
LinkedList.prototype.get = function(index){
if(!this.checkIndex(index)){
return "下标错误";
}
if(index==0){
return this.first;
}
let nextNode = this.first.next;
for(let i=1;i<this.size;i++){
if(nextNode==null){
break;
}
if(i==index){
return nextNode;
}
nextNode = nextNode.next;
}
}
/**
* 删除链表中的某个元素,删除成功返回true
* 删除失败返回false
*/
LinkedList.prototype.remove = function(index){
if(!this.checkIndex(index)){
return false;
}
let nodes = this.get(index);
if(index==0){
nodes.next.previous = null;
this.first = nodes.next;
}else if(index==this.size-1){
nodes.previous.next = null;
this.last = nodes.previous;
}else{
let prevNodes = nodes.previous;
nodes.previous.next =nodes.next;
nodes.next.previous = prevNodes;
}
this.size--;
return true;
}
/**
* 为空则返回true 否则返回false
*/
LinkedList.prototype.isEmpty = function(){
return this.size>0 ? false : true;
}
/**
* 检测下标是否合法
*/
LinkedList.prototype.checkIndex = function(index){
if(index<0||index>=this.size){
return false;
}else{
return true;
}
}
现在可以将js文件引入,试试效果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>LinkedList</title>
</head>
<body>
<script type="text/javascript" src="./LinkedList.js"></script>
<script type="text/javascript">
/**
* 插入添加删除元素效率高
* 但是读取元素效率低
*/
let linkedlist = new LinkedList();
//往链表里添加数据
linkedlist.add("a");
linkedlist.add("b");
linkedlist.add("c");
linkedlist.add("d");
linkedlist.add("e");
linkedlist.add("f");
linkedlist.add("g");
//打印链表集合
linkedlist.toString();
//输出链表长度
console.log("返回集合长度",linkedlist.getSize());
//根据下标返回指定位置的节点
console.log("获取下标6的元素",linkedlist.get(linkedlist.getSize()-1).getElement());
//判断集合是否为空
console.log("集合是否为空",linkedlist.isEmpty());
//删除集合中指定下标的元素
console.log("删除下标6",linkedlist.remove(6));
linkedlist.toString()
//在指定下标位置插入元素
linkedlist.insert("A",0);
console.log("在集合开头插入元素A");
linkedlist.toString();
linkedlist.insert("B",linkedlist.getSize()-1);
console.log("在集合末尾插入元素B");
linkedlist.toString();
linkedlist.insert("G",4);
console.log("在集合下标为4的位置插入元素G");
linkedlist.toString();
//利用for循环打印链表集合中的每个元素
for(let i=0;i<linkedlist.getSize();i++){
console.log(linkedlist.get(i));
}
</script>
</body>
</html>
看看效果
没有问题
当然这里是用js的方式实现的,用java去手写一个链表用的方式也大同小异。
当然了,这只是写着玩而已,js自带的数组就够我们用的了,写个链表只是想深入理解一下链表的底层实现原理