JavaScript中数据结构与算法(三):链表


Posted in Javascript onJune 19, 2015

我们可以看到在javascript概念中的队列与栈都是一种特殊的线性表的结构,也是一种比较简单的基于数组的顺序存储结构。由于javascript的解释器针对数组都做了直接的优化,不会存在在很多编程语言中数组固定长度的问题(当数组填满后再添加就比较困难了,包括添加删除,都是需要把数组中所有的元素全部都变换位置的,javascript的的数组确实直接给优化好了,如push,pop,shift,unshift,split方法等等…)

线性表的顺序存储结构,最大的缺点就是改变其中一个元素的排列时都会引起整个合集的变化,其原因就是在内存中的存储本来就是连贯没有间隙的,删除一个自然就要补上。针对这种结构的优化之后就出现了链式存储结构,换个思路,我们完全不关心数据的排列,我们只需要在每一个元素的内部把下一个的数据的位置给记录就可以了,所以用链接方式存储的线性表简称为链表,在链式结构中,数据=(信息+地址)

链式结构中,我们把地址也可以称为“链”,一个数据单元就是一个节点,那么可以说链表就是一组节点组成的合集。每一个节点都有一个数据块引用指向它的下一个节点

JavaScript中数据结构与算法(三):链表

数组元素是靠位置关系做逻辑引用,链表则是靠每一个数据元保存引用指针关系进行引用

这种结构上的优势就非常明显的,插入一个数据完全不需要关心其排列情况,只要把“链”的指向衔接上

这样做链表的思路就不会局限在数组上了,我们可以用对象了,只要对象之间存在引用关系即可

链表一般有,单链表、静态链表、循环链表、双向链表

单链表:就是很单一的向下传递,每一个节点只记录下一个节点的信息,就跟无间道中的梁朝伟一样做卧底都是通过中间人上线与下线联系,一旦中间人断了,那么就无法证明自己的身份了,所以片尾有一句话:"我是好人,谁知道呢?”

静态链表:就是用数组描述的链表。也就是数组中每一个下表都是一个“节”包含了数据与指向

循环链表:由于单链表的只会往后方传递,所以到达尾部的时候,要回溯到首部会非常麻烦,所以把尾部节的链与头连接起来形成循环

双向链表:针对单链表的优化,让每一个节都能知道前后是谁,所以除了后指针域还会存在一个前指针域,这样提高了查找的效率,不过带来了一些在设计上的复杂度,总体来说就是空间换时间了

综合下,其实链表就是线性表中针对顺序存储结构的一种优化手段,但是在javascript语言中由于数组的特殊性(自动更新引用位置),所以我们可以采用对象的方式做链表存储的结构

单链表

我们实现一个最简单的链表关系

function createLinkList() {
  var _this = {},
    prev = null;
  return {
    add: function(val) {
      //保存当前的引用
      prev = {
        data: val,
        next: prev || null
      }
    }
  }
}
var linksList = createLinkList(); 
linksList.add("arron1"); 
linksList.add("arron2"); 
linksList.add("arron3");
//node节的next链就是 -arron3-arron2-arron1

通过node对象的next去直引用下一个node对象,初步是实现了通过链表的引用,这种链式思路jQuery的异步deferred中的then方法,还有日本的cho45的jsderferre中都有用到。这个实现上还有一个最关键的问题,我们怎么动态插入数据到执行的节之后?

所以我们必须 要设计一个遍历的方法,用来搜索这个node链上的数据,然后找出这个对应的数据把新的节插入到当前的链中,并改写位置记录

//在链表中找到对应的节
var findNode = function createFindNode(currNode) {
  return function(key){
    //循环找到执行的节,如果没有返回本身
    while (currNode.data != key) {
      currNode = currNode.next;
    }
    return currNode;        
  }
}(headNode);

这就是一个查找当前节的一个方法,通过传递原始的头部headNode节去一直往下查找next,直到找到对应的节信息

这里是用curry方法实现的

那么插入节的的时候,针对链表地址的换算关系这是这样

a-b-c-d的链表中,如果要在c(c.next->d)后面插入一个f

a-b-c-f-d ,那么c,next->f , f.next-d

通过insert方法增加节

//创建节
function createNode(data) {
  this.data = data;
  this.next = null;
}
//初始化头部节
//从headNode开始形成一条链条
//通过next衔接
var headNode = new createNode("head");

//在链表中找到对应的节
var findNode = function createFindNode(currNode) {
  return function(key){
    //循环找到执行的节,如果没有返回本身
    while (currNode.data != key) {
      currNode = currNode.next;
    }
    return currNode;        
  }
}(headNode);

//插入一个新节
this.insert = function(data, key) {
  //创建一个新节
  var newNode = new createNode(data);
  //在链条中找到对应的数据节
  //然后把新加入的挂进去
  var current = findNode(key);
  //插入新的接,更改引用关系
  //1:a-b-c-d
  //2:a-b-n-c-d
  newNode.next = current.next;
  current.next = newNode;
};

首先分离出createNode节的构建,在初始化的时候先创建一个头部节对象用来做节开头的初始化对象

在insert增加节方法中,通过对headNode链的一个查找,找到对应的节,把新的节给加后之后,最后就是修改一下链的关系

如何从链表中删除一个节点?

由于链表的特殊性,我们a->b->c->d  ,如果要删除c那么就必须修改b.next->c为 b.next->d,所以找到前一个节修改其链表next地址,这个有点像dom操作中removeChild找到其父节点调用移除子节点

同样的我们也在remove方法的设计中,需要设计一个遍历往上回溯一个父节点即可

//找到前一个节
var findPrevious = function(currNode) {
  return function(key){
    while (!(currNode.next == null) &&
      (currNode.next.data != key)) {
      currNode = currNode.next;
    }
    return currNode;      
  }
}(headNode);

//插入方法
this.remove = function(key) {
  var prevNode = findPrevious(key);
  if (!(prevNode.next == null)) {
    //修改链表关系
    prevNode.next = prevNode.next.next;
  }
};

测试代码:

<!doctype html><button id="test1">插入多条数据</button>
<button id="test2">删除Russellville数据</button><div id="listshow"><br /></div><script type="text/javascript">
 //////////
 //创建链表 //
 //////////
 function createLinkList() {

 //创建节
 function createNode(data) {
  this.data = data;
  this.next = null;
 }

 //初始化头部节
 //从headNode开始形成一条链条
 //通过next衔接
 var headNode = new createNode("head");

 //在链表中找到对应的节
 var findNode = function createFindNode(currNode) {
  return function(key) {
  //循环找到执行的节,如果没有返回本身
  while (currNode.data != key) {
   currNode = currNode.next;
  }
  return currNode;
  }
 }(headNode);

 //找到前一个节
 var findPrevious = function(currNode) {
  return function(key) {
  while (!(currNode.next == null) &&
   (currNode.next.data != key)) {
   currNode = currNode.next;
  }
  return currNode;
  }
 }(headNode);


 //插入一个新节
 this.insert = function(data, key) {
  //创建一个新节
  var newNode = new createNode(data);
  //在链条中找到对应的数据节
  //然后把新加入的挂进去
  var current = findNode(key);
  //插入新的接,更改引用关系
  //1:a-b-c-d
  //2:a-b-n-c-d
  newNode.next = current.next;
  current.next = newNode;
 };

 //插入方法
 this.remove = function(key) {
  var prevNode = findPrevious(key);
  if (!(prevNode.next == null)) {
  //修改链表关系
  prevNode.next = prevNode.next.next;
  }
 };


 this.display = function(fn) {
  var currNode = headNode;
  while (!(currNode.next == null)) {
  currNode = currNode.next;
  fn(currNode.data)
  }
 };

 }


 var cities = new createLinkList();

 function create() {
 var text = '';
 cities.display(function(data) {
  text += '-' + data;
 });
 var div = document.createElement('div')
 div.innerHTML = text;
 document.getElementById("listshow").appendChild(div)
 }

 document.getElementById("test1").onclick = function() {
 cities.insert("Conway", "head");
 cities.insert("Russellville", "Conway");
 cities.insert("Carlisle", "Russellville");
 cities.insert("Alma", "Carlisle");
 create();
 }

 document.getElementById("test2").onclick = function() {
 cities.remove("Russellville");
 create()
 }

</script>

双链表

原理跟单链表是一样的,无非就是给每一个节增加一个前链表的指针

JavaScript中数据结构与算法(三):链表

增加节

//插入一个新节
this.insert = function(data, key) {
  //创建一个新节
  var newNode = new createNode(data);
  //在链条中找到对应的数据节
  //然后把新加入的挂进去
  var current = findNode(headNode,key);
  //插入新的接,更改引用关系
  newNode.next   = current.next;
  newNode.previous = current
  current.next   = newNode;
};

删除节

this.remove = function(key) {
  var currNode = findNode(headNode,key);
  if (!(currNode.next == null)) {
    currNode.previous.next = currNode.next;
    currNode.next.previous = currNode.previous;
    currNode.next     = null;
    currNode.previous   = null;
  }
};

在删除操作中有一个明显的优化:不需要找到父节了,因为双链表的双向引用所以效率比单链要高

测试代码:

<!doctype html><button id="test1">插入多条数据</button>
<button id="test2">删除Russellville数据</button><div id="listshow"><br /></div><script type="text/javascript">

 //////////
 //创建链表 //
 //////////
 function createLinkList() {

 //创建节
 function createNode(data) {
  this.data = data;
  this.next = null;
  this.previous = null
 }

   //初始化头部节
 //从headNode开始形成一条链条
 //通过next衔接
 var headNode = new createNode("head");

 //在链表中找到对应的数据
 var findNode = function(currNode, key) {
  //循环找到执行的节,如果没有返回本身
  while (currNode.data != key) {
  currNode = currNode.next;
  }
  return currNode;
 }

 //插入一个新节
 this.insert = function(data, key) {
  //创建一个新节
  var newNode = new createNode(data);
  //在链条中找到对应的数据节
  //然后把新加入的挂进去
  var current = findNode(headNode,key);
  //插入新的接,更改引用关系
  newNode.next   = current.next;
  newNode.previous = current
  current.next   = newNode;
 };

 this.remove = function(key) {
  var currNode = findNode(headNode,key);
  if (!(currNode.next == null)) {
  currNode.previous.next = currNode.next;
  currNode.next.previous = currNode.previous;
  currNode.next     = null;
  currNode.previous   = null;
  }
 };

 this.display = function(fn) {
  var currNode = headNode;
  while (!(currNode.next == null)) {
  currNode = currNode.next;
  fn(currNode.data)
  }
 };

 }


 var cities = new createLinkList();

 function create() {
 var text = '';
 cities.display(function(data) {
  text += '-' + data;
 });
 var div = document.createElement('div')
 div.innerHTML = text;
 document.getElementById("listshow").appendChild(div)
 }

 document.getElementById("test1").onclick = function() {
 cities.insert("Conway", "head");
 cities.insert("Russellville", "Conway");
 cities.insert("Carlisle", "Russellville");
 cities.insert("Alma", "Carlisle");
 create();
 }

 document.getElementById("test2").onclick = function() {
 cities.remove("Russellville");
 create()
 }


</script>

git代码下载:https://github.com/JsAaron/data_structure.git

Javascript 相关文章推荐
javascript之可拖动的iframe效果代码
Aug 01 Javascript
基于jquery实现控制经纬度显示地图与卫星
May 20 Javascript
js中同步与异步处理的方法和区别总结
Dec 25 Javascript
JQuery勾选指定name的复选框集合并显示的方法
May 18 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
Oct 12 Javascript
详解Javascript继承的实现
Mar 25 Javascript
jQuery 获取跨域XML(RSS)数据的相关总结分析
May 18 Javascript
用jQuery.ajaxSetup实现对请求和响应数据的过滤
Dec 20 Javascript
Node.js如何响应Ajax的POST请求并且保存为JSON文件详解
Mar 10 Javascript
基于Vue实现支持按周切换的日历
Sep 24 Javascript
jquery.param()实现数组或对象的序列化方法
Oct 08 jQuery
LayUi数据表格自定义赋值方式
Oct 26 Javascript
js结合正则实现国内手机号段校验
Jun 19 #Javascript
JavaScript中数据结构与算法(二):队列
Jun 19 #Javascript
JavaScript中数据结构与算法(一):栈
Jun 19 #Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
Jun 19 #Javascript
c#程序员对TypeScript的认识过程
Jun 19 #Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
Jun 19 #Javascript
TypeScript 中接口详解
Jun 19 #Javascript
You might like
destoon实现底部添加你是第几位访问者的方法
2014/07/15 PHP
php微信公众开发之获取周边酒店信息的方法
2014/12/22 PHP
PHP+Ajax实时自动检测是否联网的方法
2015/07/01 PHP
PHP实现简易计算器功能
2020/08/28 PHP
laravel 实现上传图片到本地和前台访问示例
2019/10/21 PHP
javascript中的有名函数和无名函数
2007/10/17 Javascript
前端开发的开始---基于面向对象的Ajax类
2010/09/17 Javascript
javascript对JSON数据排序的3个例子
2014/04/12 Javascript
三种检测iPhone/iPad设备方向的方法
2014/04/23 Javascript
使用jQuery中的wrap()函数操作HTML元素的教程
2016/05/24 Javascript
js判断输入字符串是否为空、空格、null的方法总结
2016/06/14 Javascript
jQuery+CSS3实现仿花瓣网固定顶部位置带悬浮效果的导航菜单
2016/09/21 Javascript
又一款MVVM组件 构建自己的Vue组件(2)
2017/03/13 Javascript
基于 Vue.js 2.0 酷炫自适应背景视频登录页面实现方式
2018/01/17 Javascript
vue-cli axios请求方式及跨域处理问题
2018/03/28 Javascript
微信小程序form表单组件示例代码
2018/07/15 Javascript
小程序点赞收藏功能的实现代码示例
2018/09/07 Javascript
原生js+ajax分页组件
2020/01/30 Javascript
Vue解析剪切板图片并实现发送功能
2020/02/04 Javascript
JS面试题中深拷贝的实现讲解
2020/05/07 Javascript
JS判断数组四种实现方法详解
2020/06/29 Javascript
[02:41]DOTA2英雄基础教程 冥魂大帝
2014/01/16 DOTA
Python读写unicode文件的方法
2015/07/10 Python
Python输入二维数组方法
2018/04/13 Python
python查看模块安装位置的方法
2018/10/16 Python
python 同时运行多个程序的实例
2019/01/07 Python
Python 序列化和反序列化库 MarshMallow 的用法实例代码
2020/02/25 Python
Python-openCV开运算实例
2020/07/05 Python
阿根廷在线宠物商店:Puppis
2018/03/23 全球购物
J2EE面试题集锦(附答案)
2013/08/16 面试题
部队学习十八大感言
2014/01/11 职场文书
写给老婆的检讨书
2014/02/21 职场文书
工程技术员岗位职责
2014/03/02 职场文书
会计人员岗位职责
2015/02/03 职场文书
张丽莉事迹观后感
2015/06/16 职场文书
html5移动端禁止长按图片保存的实现
2021/04/20 HTML / CSS