JavaScript数据结构与算法之链表


Posted in Javascript onJanuary 29, 2016

链表简介

链表是一种常见的数据结构,也属于线性表,但不会按线性的顺序来储存数据。而是在每一个节点中,储存了下一个节点的指针。可以看图理解。(有C语言基础的可能比较好理解)。
使用链表结构可以克服数组需要预先知道数据大小的缺点(C语言的数组需要预先定义长度),链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
接下来就是介绍两种常见的链表: 单向链表,双向链表在JavaScript中的实现。

单向链表

链表中最简单的形式就是单向链表,链表中的节点都包含两个部分,第一部分储存着自身信息,第二部分则储存有指向下一节点的指针。最后一个节点则指向NULL:

JavaScipt中单向链表的实现

首先,创建一个构造函数。

/**
 * 单向链表构造函数
 */
function LinkedList() {
 /**
  * 单向链表中节点的构造函数
  * @param {Any} element 要传入链表的节点
  */
 var Node = function(element) {
  this.element = element;
  //下个节点的地址
  this.next = null;
 }

 //单向链表的长度
 var length = 0;
 //单向链表的头结点,初始化为NULL
 var head = null;
}

不难看出,单向链表构造函数比栈与队列要复杂许多。
单向链表需要有如下的方法:

  1. append(element): 添加元素到链表尾部
  2. insert(position,element): 向单向链表中某个位置插入元素
  3. indexOf(element): 寻找某个元素在单向链表中的位置
  4. remove(element): 移除给定的元素
  5. removeAt(position): 移除单向链表中某个位置的元素
  6. getHead(): 获取单向链表的头部
  7. isAmpty(): 检查单向链表是否为空,为空则返回true
  8. toString(): 将链表所有内容以字符串输出
  9. size(): 返回单向链表长度

append方法:

说明: 向单向链表尾部添加元素。
实现:

/**
 * 向单向链表尾部添加元素
 * @param {Any} element 要加入链表的节点
 */
this.append = function(element) {
 var node = new Node(element);
 var current;

 if (head == null) {
  head = node;
 } else {
  // 当前项等于链表头部元素.
  // while循环到最后一个,从而将该节点加入链表尾部。
  current = head;
  // 当next为null时,判定为false。退出循环。
  while (current.next) {
   current = current.next;
  }
  current.next = node;
 }
 length++;
};

insert方法:

说明: 向单向链表中某个位置插入元素。
实现:

/**
 * 向单向链表中插入某个元素
 * @param {Number} position 要插入的位置
 * @param {Any} element 要插入的元素
 * @return {Boolean}     插入成功返回true,失败返回false
 */
this.insert = function(position, element) {
 if (position >= 0 && position <= length) {
  var node = new Node(element);
  var current = head;
  var previous;
  var index = 0;

  if (position == 0) {
   node.next = current;
   head = node;
  } else {
   while (index++ < position) {
    previous = current;
    current = current.next;
   }

   previous.next = node;
   node.next = current;
  }

  length++;
  return true;
 } else {
  return false;
 }
};

indexOf方法:

说明:寻找某个元素在单向链表中的位置。
实现:

/**
 * 寻找某个元素在单向链表中的位置
 * @param {Any} element 要寻找的元素
 * @return {Number}     返回值>=0则代表找到相应位置
 */
this.indexOf = function(element) {
 var current = head;
 var index = -1;

 while (current) {
  if (element === current.element) {
   return index;
  }
  index++;
  current = current.next;
 }

 return -1;
};

remove方法:

说明: 移除给定的元素。
实现:

/**
 * 移除给定的元素
 * @param {Any} element 要移除的元素
 * @return {Number}     返回值>=0表示移除成功
 */
this.remove = function(element) {
 var index = this.indexOf(element);
 return this.removeAt(index);
};

removeAt方法:

说明:移除单向链表中某个位置的元素。
实现:

/**
 * 移除单向链表中某一个元素
 * @param {Number} position 要移除元素的位置
 * @return {Any}     移除成功返回被移除的元素,不成功则返回NULL
 */
this.removeAt = function(position) {
 if (position > -1 && position < length) {
  var current = head;
  var previous;
  var index = 0;

  if (position == 0) {
   // 因为之前head指向第一个元素,现在把head修改为指向第二个元素。
   // 核心概念在于链表前后全靠指针链接,而非数组一般。
   // 所以只需要改变head的元素。
   head = current.next;
  } else {
   while (index++ < position) {
    // previous指要操作元素位置之前的那个元素,current表示之后的那个元素。
    previous = current;
    current = current.next;
   }

   previous.next = current.next;
  }

  length--;

  return current.element;
 } else {
  return null;
 }
};

getHead方法:

说明:获取单向链表的头部。
实现:

/**
 * 获取单向链表的头部
 * @return {Any} 单向链表的头部
 */
this.getHead = function() {
 return head;
}

isAmpty、toString、size方法

实现:

/**
 * 判断单向链表是否为空
 * @return {Boolean} 为空则返回true,不为空则返回false
 */
this.isAmpty = function() {
 return length === 0
};

/**
 * 将链表所有内容以字符串输出
 * @return {String} 要输出的字符串
 */
this.toString = function() {
 var current = head;
 var string = '';

 while (current) {
  string += current.element;
  current = current.next;
 }
 return string;
};

/**
 * 返回单向链表长度
 * @return {Number} 单向链表的长度
 */
this.size = function() {
 return length;
};

双向链表

双向链表与单向链表很是相像。在单向链表中,只有指向下一个节点的链接。但在双向链表中,还有指向上一个节点的链接,是双向的。

JavaScipt中双向链表的实现

首先,依然是构造函数:

/**
 * 双向链表的构造函数
 */
function DoublyLinkedList() {
 /**
  * 双向链表中节点的构造函数
  * @param {Any} element 要传入链表的元素
  */
 var Node = function(element) {
  this.element = element;
  this.prev = null;
  this.next = null;
 }

 //双向链表的长度
 var length = 0;
 //双向链表的头结点,初始化为NULL
 var head = null;
 //双向链表的尾结点,初始化为NULL
 var tail = null;
}

双向链表需要有如下的方法:

  1. append(element): 添加元素到双向链表尾部
  2. insert(position,element): 向双向链表中某个位置插入元素
  3. removeAt(position): 移除双向链表中某个位置的元素
  4. showHead(): 获取双向链表的头部
  5. showLength(): 获取双向链表长度
  6. showTail(): 获取双向链表尾部

append方法:

说明: 添加元素到双向链表尾部
实现:

/**
 * 向链表尾部添加元素
 * @param {Any} element 要加入链表的节点
 * @return {Any}     加入链表的节点
 */
this.append = function(element) {
 var node = new Node(element);

 if (head === null) {
  head = node;
  tail = node;
 } else {
  var previous;
  var current = head;

  while (current.next) {
   current = current.next;
  }

  current.next = node;
  node.prev = current;
  tail = node;
 }

 length++;
 return node;
};

insert方法:

说明: 向双向链表中某个位置插入元素。
实现:

/**
 * 向链表中插入某个元素
 * @param {Number} position 要插入的位置
 * @return {Boolean}     插入成功返回true,失败返回false
 */
this.insert = function(position, element) {
 if (position >= 0 && position <= length) {

  var node = new Node(element);
  var index = 0;
  var previous;
  var current = head;

  if (position === 0) {

   if (head === null) {
    head = node;
    tail = node;
   } else {
    current.prev = node;
    node.next = current;
    head = node;
   }
  } else if (position === length) {

   current = tail;
   current.next = node;
   node.prev = current;
   tail = node;
  } else {

   while (index++ < position) {
    previous = current;
    current = current.next;
   }

   previous.next = node;
   node.prev = previous;
   current.prev = node;
   node.next = current;
  }

  length++;
  return true;
 } else {
  return false;
 }
};

removeAt方法:

说明:移除双向链表中某个位置的元素。
实现:

/**
 * 移除链表中某一个元素
 * @param {Number} position 要移除元素的位置
 * @return {Any}       移除成功返回被移除的元素,不成功则返回false
 */
this.removeAt = function(position) {
 if (position > -1 && position < length) {
  var current = head;
  var index = 0;
  var previous;

  if (position === 0) {
   head = current.next;

   if (length === 1) {
    tail = null;
    head.prev = null;
   }
  } else if (position === length - 1) {
   current = tail;
   tail = current.prev;
   tail.next = null;
  } else {
   while (index++ < position) {
    previous = current.prev;
    current = current.next;
   }
   previous.next = current.next;
   current.next.prev = previous;
  }

  length--;
  return current.element;
 } else {
  return false;
 }
};

showHead、showLength、showTail方法

实现:

/**
 * 获取链表的头部
 * @return {Any} 链表的头部
 */
this.showHead = function() {
 return head;
};

/**
 * 获取链表长度
 * @return {Number} 链表长度
 */
this.showLength = function() {
 return length;
};

/**
 * 获取链表尾部
 * @return {Any} 链表尾部
 */
this.showTail = function() {
 return tail;
};

感想

链表这一节,基本全部都是先按需求写代码,写完后再和书上对比。发现简直被瞬间秒成渣。自己写的很多暗坑,逻辑也很混乱。看来还是太年轻了。

Javascript 相关文章推荐
DHTML 中的绝对定位
Nov 26 Javascript
一个多次搜索+多次传值的解决方案
Jan 20 Javascript
JavaScript中URL编码函数代码
Jan 11 Javascript
基于jquery的代码显示区域自动拉长效果
Dec 07 Javascript
JavaScript入门基础
Aug 12 Javascript
JS实现的简洁二级导航菜单雏形效果
Oct 13 Javascript
果断收藏9个Javascript代码高亮脚本
Jan 06 Javascript
使用jquery.qrcode.min.js实现中文转化二维码
Mar 11 Javascript
jQuery表格的维护和删除操作
Feb 03 Javascript
简单谈谈原生js的math对象
Jun 27 Javascript
js实现圆形显示鼠标单击位置
Feb 11 Javascript
Openlayers学习之加载鹰眼控件
Sep 28 Javascript
动态创建按钮的JavaScript代码
Jan 29 #Javascript
JavaScript数据结构与算法之栈与队列
Jan 29 #Javascript
javascript实现表单验证
Jan 29 #Javascript
jQuery实现横向带缓冲的水平运动效果(附demo源码下载)
Jan 29 #Javascript
JavaScript判断DIV内容是否为空的方法
Jan 29 #Javascript
基于javascript实现listbox左右移动
Jan 29 #Javascript
关于获取DIV内部内容报错的原因分析及解决办法
Jan 29 #Javascript
You might like
php curl 登录163邮箱并抓取邮箱好友列表的代码(经测试)
2011/04/07 PHP
thinkphp5 加载静态资源路径与常量的方法
2017/12/24 PHP
基于Unit PNG Fix.js有时候在ie6下不正常的解决办法
2013/06/26 Javascript
关于jQuery中.attr()和.prop()的问题探讨
2013/09/06 Javascript
如何设置iframe高度自适应在跨域情况下的可用方法
2013/09/06 Javascript
javascript简单判断输入内容是否合法的方法
2016/05/11 Javascript
JQuery和HTML5 Canvas实现弹幕效果
2017/01/04 Javascript
微信小程序 用户数据解密详细介绍
2017/01/09 Javascript
jq给页面添加覆盖层遮罩的实例
2017/02/16 Javascript
react实现pure render时bind(this)隐患需注意!
2017/03/09 Javascript
vue小图标favicon不显示的解决方案
2017/09/19 Javascript
javascript实现遮罩层动态效果实例
2019/05/14 Javascript
VUE 自定义组件模板的方法详解
2019/08/30 Javascript
layui 根据后台数据动态创建下拉框并同时默认选中的实例
2019/09/02 Javascript
JavaScript Blob对象原理及用法详解
2020/10/14 Javascript
Python正则表达式教程之三:贪婪/非贪婪特性
2017/03/02 Python
python通过pip更新所有已安装的包实现方法
2017/05/19 Python
解决nohup重定向python输出到文件不成功的问题
2018/05/11 Python
Django + Uwsgi + Nginx 实现生产环境部署的方法
2018/06/20 Python
python数据挖掘需要学的内容
2019/06/23 Python
基于python traceback实现异常的获取与处理
2019/12/13 Python
python GUI库图形界面开发之PyQt5 MDI(多文档窗口)QMidArea详细使用方法与实例
2020/03/05 Python
使用python实现时间序列白噪声检验方式
2020/06/03 Python
Python实现异步IO的示例
2020/11/05 Python
推荐10个HTML5响应式框架
2016/02/25 HTML / CSS
中国跨境在线时尚零售商:Bellelily
2018/04/06 全球购物
SOKOLOV官网:俄罗斯珠宝首饰品牌
2021/01/02 全球购物
可靠的数据流传输TCP
2016/03/15 面试题
大学辅导员事迹材料
2014/02/05 职场文书
2014新课程改革心得体会
2014/03/10 职场文书
高中运动会广播稿
2014/09/16 职场文书
经典搞笑版检讨书
2015/02/19 职场文书
2015年度工程师评职称工作总结
2015/10/14 职场文书
2019XX公司员工考核管理制度!
2019/08/07 职场文书
AI:如何训练机器学习的模型
2021/04/16 Python
vue中利用mqtt服务端实现即时通讯的步骤记录
2021/07/01 Vue.js