JavaScript--数据结构算法之链表
数组的缺点:数组的长度固定,增删时比较困难要移动元素,而且数据填满再添加元素比较复杂。
js:数组有split(),可以任意的分割。不存在上述问题。
主要问题是:js数组都被实现成了对象,和其他语言的数组比较而言效率低。
一.单向链表:
有一组节点组成的集合,每一个节点都使用一个对象的引用指向它的后继。指向另一个节点的引用叫做链。
插入元素:只需修改前驱节点,使其指向新的节点,而新加入的节点指向原来前去指向的节点。
删除元素:将待删除元素的前驱节点指向待删除元素的后继节点。
插入,删除性能更高。
设计原理:
包含两个类:Node表示节点,LinkList类提供插入,删除,显示链表元素等方法,以及一些辅助方法。
~(function() {//创建节点,设置两个属性
function Node(ele) {
this.ele = ele;
this.next = null;//初始化时null,当有新元素插入时,next会指向新的元素。
}
function LinkList() {
this.head = new Node("head");//使用Node对象保存改链表的头节点
/!*this.find = find;
this.insert = insert;
this.remove = remove;
this.display = display;*!/
}
LinkList.prototype.find = function () {};//查找指定的元素,遍历链表
LinkList.prototype.insert = function () {};
LinkList.prototype.remove = function () {};
LinkList.prototype.findPrevious = function () {};
LinkList.prototype.display = function () {};//显示链表的元素
/!*1.插入元素:insert();向链表中插入一个节点。
在已知一个节点在后面插入元素时,首先要找到后面的节点,创建一个find()方法遍历链表查找节点。
*!/
LinkList.prototype.find =function (item) {
var currNode = this.head;
while(currNode.ele != item) {
currNode = currNode.next;
}
return currNode;
};//在链表上的移动,创建一个新节点,将链表的头节点赋给新创建的节点,然后在链表上循环当前的ele属性是否与item相当,否则当前结点移动到下一个节点,成功返回,否则null。
LinkList.prototype.insert = function (newEle,item) {
var newNode = new Node(newEle);
var current = this.find(item);
newNode.next = current.next;
current.next = newNode;
};//找到后面的元素,插入链表,将新节点的next属性设置为后面节点的next对应值。然后设置后面节点的next属性指向新的节点。
LinkList.prototype.display = function () {
var currNode = this.head;//变量记录头节点
while(!(currNode.next == null)) {//当前节点不为空遍历链表
console.log(currNode.next.ele);
currNode = currNode.next;//指向下一个节点
}
};
var cities = new LinkList();
cities.insert("BeiJing","head");
cities.insert("ShangHai","BeiJing");
cities.insert("ShenZhen","ShangHai");
cities.insert("GuangZhou","ShenZhen");
cities.insert("ChengDou","GuangZhou");
cities.display();
console.log("===================");
/!*2.删除元素:remove();向链表中删除一个节点。
* 删除节点时:需要找到待删除节点的前驱。只需修改他的后继next指向待删除的下一个节点。定义一个findPrevious()方法。遍历链表的节点,检查每一个节点的下一个节点是否存储这待删除的数据,如果有,返回该节点的前驱。
* *!/
LinkList.prototype.findPrevious = function (item) {
var currNode = this.head;
while(!(currNode.next == null) && (currNode.next.ele != item )) {//当前节点的后继不为空或者后继节点不为所要查找的元素时
currNode = currNode.next;//修改后继链
}
return currNode;//找到时返回
};
LinkList.prototype.remove = function(item) {
var prevNode = this.findPrevious(item);//找到删除元素的前一个元素
if(!(prevNode.next == null)) {//待删除元素不为空
prevNode.next = prevNode.next.next;//待删除元素的前驱的后继修改为待删除元素的后继的后继
}
};
cities.remove("GuangZhou");
cities.display();
console.log("===================");
})();
二.双向链表
通过给Node节点添加一个前驱属性previous,指向前驱节点的链接。但插入时需要指明正确的前驱和后继。删除时就不再需要查找待删除节点的前驱节点。
~(function() {
function Node(ele) {
this.ele = ele;
this.next = null;
this.previous =null;
}
function LList() {
this.head = new Node("head");
}
LList.prototype.find = function () {};
LList.prototype.insert = function () {};
LList.prototype.display = function () {};
LList.prototype.remove = function () {};
LList.prototype.findLast = function () {};
LList.prototype.reverse = function () {};
//1.双向链表的插入
LList.prototype.find =function (item) {
var currNode = this.head;
while(currNode.ele != item) {
currNode = currNode.next;
}
return currNode;
};
LList.prototype.insert = function (newEle,item) {
var newNode = new Node(newEle);
var current = this.find(item);
newNode.next = current.next;//当前节点的后继给新节点的后继
//newNode = current.next.previous; //?当前元素的后继的前驱元素没有指定
newNode.previous = current;//当前节点给新节点的前驱
current.next = newNode;//新节点给当前节点的后继
};
LList.prototype.display = function () {
var currNode = this.head;//变量记录头节点
while(!(currNode.next == null)) {//当前节点不为空遍历链表
console.log(currNode.next.ele);
currNode = currNode.next;//指向下一个节点
}
};
var cities = new LList();
cities.insert("Beijing","head");
cities.insert("Shanghai","Beijing");
cities.insert("Guangzhou","Shanghai");
cities.insert("Chengdu","Guangzhou");
cities.display();
//2.双向链表的删除
//比单向链表的效率更高,不需要找前驱,首先先找到待删除的节点,修改前驱与后继就可以。
LList.prototype.remove = function (item) {
var currNode = this.find(item);
if(!(currNode.next == null)) {
currNode.previous.next = currNode.next;
currNode.next.previous = currNode.previous;
currNode.next = null;
currNode.previous = null;//待删除的元素的前驱和后继都设置null
}
};
console.log("----------------------");
cities.remove("Beijing");
cities.display();
console.log("----------------------");
cities.remove("Guangzhou");
cities.display();
//3.逆秩访问disReverse()
//完成反序显示链表,需要查找到最后一个节点findLast(),这样免除了从前往后遍历带来的麻烦。
LList.prototype.findLast = function () {
var currNode = this.head;
while(!(currNode.next == null)) {
currNode = currNode.next;
}
return currNode;
};
LList.prototype.reverse = function () {
var currNode = this.head;
currNode = this.findLast();
while(!(currNode.previous == null)) {
console.log(currNode.ele);
currNode = currNode.previous;//改变前驱
}
};
console.log("----------------------");
cities.reverse();
})();
三.循环链表
与单向链表是相似的,节点类型一样。区别是:让其头节点的next属性指向本身,head.next = head,这样会传导至链表的每一个节点,使得每一个节点的next属性指向链表的头节点,这样使得链表的尾节点指向了头节点,形成了一个环。性能高于双向链表;
~(function() {
function Node(ele) {
this.ele = ele;
this.next = null;
}
function LList() {
this.head = new Node("head");
this.head.next = this.head;
}
/*LList.prototype.find = function() {};
LList.prototype.insert = function() {};
LList.prototype.display = function() {};
LList.prototype.findPrevious = function() {};
LList.prototype.remove = function() {};*/
//与单向链表相似,只需修改display()方法即可;不然会成为死循环
LList.prototype.find =function (item) {
var currNode = this.head;
while(currNode.ele != item) {
currNode = currNode.next;
}
return currNode;
};
LList.prototype.insert = function (newEle,item) {
var newNode = new Node(newEle);
var current = this.find(item);
newNode.next = current.next;
current.next = newNode;
};
LList.prototype.display = function() {
var currNode = this.head;
while(!(currNode.next == null) && !(currNode.next.ele == "head")) {//这点有区别当前节点的后继不为空或者当前节点的后继元素不为头节点
console.log(currNode.next.ele);
currNode = currNode.next;
}
};
LList.prototype.findPrevious = function (item) {
var currNode = this.head;
while(!(currNode.next == null) && (currNode.next.ele != item )) {//当前节点的后继不为空或者后继节点不为所要查找的元素时
currNode = currNode.next;//修改后继链
}
return currNode;//找到时返回
};
LList.prototype.remove = function(item) {
var prevNode = this.findPrevious(item);//找到删除元素的前一个元素
if(!(prevNode.next == null)) {//待删除元素不为空
prevNode.next = prevNode.next.next;//待删除元素的前驱的后继修改为待删除元素的后继的后继
}
};
var cities = new LList();
cities.insert("Beijing","head");
cities.insert("Shanghai","Beijing");
cities.insert("Guangzhou","Shanghai");
cities.insert("Chengdu","Guangzhou");
cities.display();
console.log("--------------------");
cities.remove("Guangzhou");
cities.display();
})();
JavaScript--数据结构算法之链表的更多相关文章
- javascript数据结构之单链表
下面是用javascript实现的单链表,但是在输出的时候insert方法中存在问题,chrome的console报错说不能读取空的属性,调试了很久都没有通过,先在这里存着,以后再来修改一下. //数 ...
- 数据结构&算法-单链表
1.引言 工作一年了,感觉越来越懒散,把很多基础性的东西都慢慢遗忘了,最近想趁着还没忘完,回顾一下,整理了点笔记,分享一下. 如有错的地方,欢迎大家怒喷. 2.学习 我们就从最简单的链表开始吧. 链表 ...
- 指针与数据结构算法_链表(C语言)
一.变量: 声明一个变量系统是没有给这个变量分配内存空间的: 例: int j;//编译的时候是没有分配内存空间的 ;//计算机在编译的时候就会给这个i分配4个字节的内存空间 二.malloc动态分配 ...
- JavaScript 数据结构与算法之美 - 线性表(数组、栈、队列、链表)
前言 基础知识就像是一座大楼的地基,它决定了我们的技术高度. 我们应该多掌握一些可移值的技术或者再过十几年应该都不会过时的技术,数据结构与算法就是其中之一. 栈.队列.链表.堆 是数据结构与算法中的基 ...
- 为什么我要放弃javaScript数据结构与算法(第五章)—— 链表
这一章你将会学会如何实现和使用链表这种动态的数据结构,这意味着我们可以从中任意添加或移除项,它会按需进行扩张. 本章内容 链表数据结构 向链表添加元素 从链表移除元素 使用 LinkedList 类 ...
- JavaScript数据结构与算法-链表练习
链表的实现 一. 单向链表 // Node类 function Node (element) { this.element = element; this.next = null; } // Link ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)
定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构 ...
- JavaScript数据结构——链表
链表:存储有序的元素集合,但不同于数组,链表中的元素在内存中不是连续放置的.每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成. 好处:可以添加或移除任意项,它会按需扩容 ...
- 为什么我要放弃javaScript数据结构与算法(第九章)—— 图
本章中,将学习另外一种非线性数据结构--图.这是学习的最后一种数据结构,后面将学习排序和搜索算法. 第九章 图 图的相关术语 图是网络结构的抽象模型.图是一组由边连接的节点(或顶点).学习图是重要的, ...
- 为什么我要放弃javaScript数据结构与算法(第八章)—— 树
之前介绍了一些顺序数据结构,介绍的第一个非顺序数据结构是散列表.本章才会学习另一种非顺序数据结构--树,它对于存储需要快速寻找的数据非常有用. 本章内容 树的相关术语 创建树数据结构 树的遍历 添加和 ...
随机推荐
- windows, fast-rcnn CPU版本的安装配置
一:安装准备 1:caffe的安装配置,本人用的是happynear大神的caffe版本,具体链接https://github.com/happynear/caffe-windows,编译时需要用到p ...
- ASP.NET 部分视图
ASP.NET MVC 里的部分视图,相当于 Web Form 里的 User Control.我们的页面往往会有许多重用的地方,可以进行封装重用. 使用部分视图有以下优点: 1. 可以简写代码. ...
- Benelux Algorithm Programming Contest 2014 Final(第二场)
B:Button Bashing You recently acquired a new microwave, and noticed that it provides a large number ...
- request.getxxxxxx()的使用方法
request.getSchema() 可以返回当前页面使用的协议,http 或是 https; request.getServerName() 可以返回当前页面所在的服务器的名字; request. ...
- Spring EL表达式和资源调用
Spring EL表达式 Spring EL-Spring表达式语言,支持在xml和注解中使用表达式,类似于在jsp的EL表达式语言. Spring 开发中经常涉及调用各种资源的情况, ...
- 【UVa 116】Unidirectional TSP
[Link]:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_probl ...
- vim 插件之 surround.vim
surround.vim-这个插件主要是用来插入一些特殊符号的(成对出现) 下载地址 http://www.vim.org/scripts/script.php?script_id=1697 http ...
- Task Scheduler
https://technet.microsoft.com/en-us/library/cc748993(v=ws.11).aspx#BKMK_winui If Task Scheduler is n ...
- UltraEdit Companion Utility
UltraEdit Companion Utility 配色组件 http://www.danielwmoore.com/extras/index.php?action=downloads;sa=vi ...
- Docker学习总结(6)——通过 Docker 化一个博客网站来开启我们的 Docker 之旅
通过 Docker 化一个博客网站来开启我们的 Docker 之旅 这篇文章包含 Docker 的基本概念,以及如何通过创建一个定制的 Dockerfile 来 Docker 化Dockerize一个 ...