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 相关文章推荐
jQuery select控制插件
Aug 17 Javascript
js将iframe中控件的值传到主页面控件中的实现方法
Mar 11 Javascript
jquery弹出框的用法示例(2)
Aug 26 Javascript
解决bootstrap中modal遇到Esc键无法关闭页面
Mar 09 Javascript
js实现完美兼容各大浏览器的人民币大小写相互转换
Oct 29 Javascript
BootStrap的两种模态框方式
May 10 Javascript
微信小程序 页面滑动事件的实例详解
Oct 12 Javascript
jQuery实现的粘性滚动导航栏效果实例【附源码下载】
Oct 19 jQuery
javaScript 连接打印机,打印小票的实例
Dec 29 Javascript
javascript中call()、apply()的区别
Mar 21 Javascript
页面内锚点定位及跳转方法总结(推荐)
Apr 24 Javascript
VUE异步更新DOM - 用$nextTick解决DOM视图的问题
Nov 06 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版(4)
2006/10/09 PHP
在PHP中读取和写入WORD文档的代码
2008/04/09 PHP
javascript+css 网页每次加载不同样式的实现方法
2009/12/27 Javascript
JavaScript 题型问答有答案参考
2010/02/17 Javascript
img onload事件绑定各浏览器均可执行
2012/12/19 Javascript
JS远程获取网页源代码实例
2013/09/05 Javascript
JS+JSP checkBox 全选具体实现
2014/01/02 Javascript
关于img的href和src取变量及赋值的方法
2014/04/28 Javascript
15款jQuery分布引导插件分享
2015/02/04 Javascript
JS+CSS模拟可以无刷新显示内容的留言板实例
2015/03/03 Javascript
JavaScript制作windows经典扫雷小游戏
2015/03/31 Javascript
JavaScript实现Iterator模式实例分析
2015/06/09 Javascript
jQuery日历插件datepicker用法详解
2016/03/03 Javascript
JS实现给json数组动态赋值的方法示例
2020/03/19 Javascript
实例教学如何写vue插件
2017/11/30 Javascript
详解如何使用 vue-cli 开发多页应用
2017/12/16 Javascript
微信小程序云开发实现数据添加、查询和分页
2019/05/17 Javascript
Angular中innerHTML标签的样式不起作用的原因解析
2019/06/18 Javascript
vue 监听 Treeselect 选择项的改变操作
2020/08/31 Javascript
[02:28]DOTA2英雄基础教程 灰烬之灵
2013/12/19 DOTA
python使用urlparse分析网址中域名的方法
2015/04/15 Python
Python捕捉和模拟鼠标事件的方法
2015/06/03 Python
详解Python 数据库的Connection、Cursor两大对象
2018/06/25 Python
Pyqt5 实现跳转界面并关闭当前界面的方法
2019/06/19 Python
pytorch构建多模型实例
2020/01/15 Python
纯CSS3实现的阴影效果
2014/12/24 HTML / CSS
CSS实现雨滴动画效果的实例代码
2019/10/08 HTML / CSS
canvas 绘图时位置偏离的问题解决
2020/09/16 HTML / CSS
公司端午节活动方案
2014/02/04 职场文书
校园演讲稿汇总
2014/05/21 职场文书
地理信息科学专业推荐信
2014/09/08 职场文书
2015年清明节扫墓演讲稿
2015/03/18 职场文书
学校捐书倡议书
2015/04/27 职场文书
民事起诉状范文
2015/05/19 职场文书
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android
MySQL详细讲解变量variables的用法
2022/06/21 MySQL