线性表是最基本的数据结构,线性表是指的数据一对一的关系,其数据组织的形式是线性的,线性指的是逻辑上结构。数组和链表是最常用两个线性表,但是它们的物理结构是不同的,也就是说数组和链表在内存中的表示并不相同,数组属于顺序结构,链表属于链式结构,关于线性表的更多内容可以查看:线性表、栈和队列实现原理,这篇文章有深入的解析。
文章图片
一、JavaScript线性表:数组
文章图片
如上图是一个数组在内存中的表示,数组的元素地址在内存中是连续的,数组存储多个相同类型的数据。因为数组元素地址是连续的,所以这使得数组更容易结算元素的地址,可以通过一个地址(首地址)作为基本地址,通过地址的偏移值快速访问元素。
数组的索引类型:索引0,数组的第一个元素使用0标记;索引1,通常1不是数组的第一个元素;索引n,表示数组中的任意位置,范围为:0到n-1(最后一个元素不是n)。
使用数组的优点:数组允许随机访问元素,这使得访问数据更快。数组具有更好的缓存局部性,这可以在性能上产生很大的差异。
二、JavaScript链表的介绍
文章图片
和数组一样,链表也是一个线性的数据结构,但是和数组不同,链表的元素不是连续储存的,如上图是链表一般的形式。
为什么使用链表呢?数组可以用于储存相同或相似数据类型的线性数据,但是数组有以下限制:
- 数组的大小是固定的:所以我必须知道元素数量的上界,而且分配给数组的内存大小通常都等于元素数量的上界。
- 插入或删除数组中的一个元素比较耗时,例如插入一个元素都位置i上,则在插入之前需要预先在i位置上空出位置,这需要花费O(n)的时候移动元素。
- 不允许随机访问,必须进行顺序访问,所以链表不允许进行二分搜索。
- 链表需要使用额外的空间储存指针。
- 缓存不友好,数组元素是连续的位置,但是在链表中是不存在的。
- 数组项data
- 指针或引用,指向下一个结点
三、数组和链表操作和实现源码下面是数组和链表的常规操作:
- add():插入操作,在链表中时间复杂度为O(1),数组为O(n)。
- delete():删除操作,数组和链表的时间复杂度分别为:O(n)、O(1)。
- search():遍历操作,搜索数组或链表中的一个元素。
- contain(),检查数组或链表中是否存在某个元素。
- reverse(),逆序遍历。
// 链表结点,双链表
function Node(key, value){
this.key = key;
this.value = http://www.srcmini.com/value;
this.next = null;
this.prev = null;
}// 链表
function LinkedList(){
this.head = null;
this.tail = null;
this.size = 0;
}// 链表操作// 检查链表是否已空
LinkedList.prototype.isEmpty = function(){
return this.size == 0;
}// 从表头添加数据
LinkedList.prototype.pushBack = function(key, value){
var node = new Node(key, value);
if(this.size == 0){
node.prev = node.next = node;
this.head = this.tail = node;
this.size++;
}
else{
var head = this.head;
head.prev = node;
node.next = head;
var tail = this.tail;
tail.next = node;
node.prev = tail;
this.tail = node;
this.size++;
}
}// 从表尾添加数据
LinkedList.prototype.pushFront = function(key, value){
var node = new Node(key, value);
if(this.size == 0){
node.prev = node.next = node;
this.head = this.tail = node;
this.size++;
}
else{
var head = this.head;
var tail = this.tail;
head.prev = node;
node.next = head;
tail.next = node;
node.prev = tail;
this.head = node;
this.size++;
}
}// 从表尾删除一个数据
LinkedList.prototype.popBack = function(){
if(this.size == 0)
return null;
var tail = this.tail;
var head = this.head;
tail.prev.next = head;
head.prev = tail.prev;
this.tail = tail.prev;
this.size--;
var value = tail.value;
tail = null;
return value;
}// 从表头删除一个数据
LinkedList.prototype.popFront = function(){
if(this.size == 0)
return null;
var head = this.head;
var tail = this.tail;
tail.next = head.next;
head.next.prev = tail;
this.head = head.next;
this.size--;
var value = head.value;
head = null;
return value;
}// 根据特定的数据key删除数据
LinkedList.prototype.delete = function(key){
if(this.size == 0)
return;
var node = this.head;
do{
if(node.key = key){
var prev = node.prev;
var next = node.next;
prev.next = next;
next.prev = prev;
if(node == this.head)
this.head = next;
if(node == this.tail);
this.tail = prev;
node = null;
this.size--;
return;
}
node = node.next;
}while(node != this.head);
}// 使用深度优先DFS搜索数据
LinkedList.prototype.find = function(node, key){
if(!node)
return null;
if(node.key == key)
return node.value;
else
return this.find(node.next, key);
}// 根据key搜索指定数据
LinkedList.prototype.search = function(key){
if(this.size == 0)
return null;
this.tail.next = null;
var value = this.find(this.head, key);
this.tail.next = this.head;
return value;
}// 检查链表是否包含指定key的元素
LinkedList.prototype.contain = function(key){
if(this.size == 0)
return false;
return this.search(key) != null;
}// 顺序遍历并输出链表
LinkedList.prototype.traverse = function(){
if(this.size == 0)
return;
var node = this.head;
var content ="";
do{
content += node.key + " ";
node = node.next;
}while(node != this.head);
console.log(content);
}// 逆序遍历输出链表
LinkedList.prototype.reverse = function(){
if(this.size == 0)
return;
var node = this.tail;
var content = "";
do{
content += node.key + " ";
node = node.prev;
}while(node != this.tail);
console.log(content);
}// 1、一般链表操作
var u1 = {"key": 12, "name": "JavaScript"};
var u2 = {"key": 20, "name": "C++"};
var u3 = {"key": 30, "name": "Python"};
var list = new LinkedList();
list.pushFront(u1.key, u1);
list.pushFront(u2.key, u2);
list.pushFront(u3.key, u3);
list.traverse();
list.reverse();
console.log("list contains 20: " + list.contain(20));
console.log("list contains 40: " + list.contain(40));
list.delete(30);
console.log("list contains 30: " + list.contain(30));
list.traverse();
console.log();
// 2、模拟栈
var stack = new LinkedList();
stack.pushFront(u1.key, u1);
stack.pushFront(u2.key, u2);
stack.pushFront(u3.key, u3);
var value;
var str = "";
while(!stack.isEmpty()){
value = http://www.srcmini.com/stack.popFront();
str += value.key +" ";
}
console.log(str);
console.log();
// 3、模拟队列
var queue = new LinkedList();
queue.pushBack(u1.key, u1);
queue.pushBack(u2.key, u2);
queue.pushBack(u3.key, u3);
value = http://www.srcmini.com/null;
str ="";
while(!queue.isEmpty()){
value = http://www.srcmini.com/queue.popFront();
str += value.key +" ";
}
console.log(str);
推荐阅读
- JS如何实现二叉堆(JavaScript实现最小二叉堆和优先队列)
- 数组和链表有什么区别(哪个更快?有什么优缺点?有哪些应用场景?)
- JavaScript常用数据结构(栈(Stack)详解)
- JavaScript基本数据结构(队列基本原理和实现)
- 倒排索引(反向索引)详细介绍
- CSS 动画制作详细指南和代码示例
- Java中的数据类型经典指南
- CSS env()函数用法解析和代码示例
- PHP | gethostnamel()函数用法介绍