本篇文章未完结,将会持续更新…
前言
这几天天天刷到面试相关的文章,然后里面的内容千变一律的是讲计算机基础、数据结构与算法。
没办法,我的基础的确是差,为了前(qian)途着想,我只好立刻马上迅速的滚去学一波数据结构与算法。
以下内容均参考:
数据结构
数据结构就是在计算机中 组织存储数据 的一种方式。它可以高效的 访问与修改 数据。确切的说,数据结构是数据值的一个集合,他们之间的关系、函数或操作可以直接应用于数据。
栈
栈是一个先进后出的结构(FILO), 新添加的元素在栈顶,最先添加的元素在栈底。栈就相当于一摞书一本本添加上去,拿也是一本本(栈顶)拿出来,不能直接拿最下面(栈底)拿出来。
实例代码:
class Stack{
constructor{
this.items = [];
}
push(item){
this.items.push(item);
}
pop(){
return this.items.shift();
}
}
队列
队列是一个先进先出的结构(FIFO), 新添加的元素在队列最后,最先添加的元素在队列最前。队列相当于食堂打饭排队,你先排队就在前面,你打完饭,后面一位才能接着打。
实例代码:
class Queue{
constructor(){
this.items = [];
}
push(item){
this.items.push(item);
}
shift(){
this.items.shift();
}
}
优先队列
优先队列就是医院排队了,毕竟病人是有轻重缓急的,所以病重的要优先处理。所以要给每一个元素一个优先级,添加时,判断优先级,优先级越高的就排在前面。
class Queue{
...
push(item, priority){
const queueElement = {item, priority};
const index = this.items.findIndex(item => {
return queueElement.priority < item.priority;
})
if( index > -1 )this.items.splice(index, 0, queueElement);
else this.items.push(queueElement);
}
...
}
循环队列
循环队列是为了克服“假溢出”现象的。将一个向量空间想象成一个首尾相接的圆环,称为这个向量为循环向量。存储在这里面的队列就是一个循环队列。这个循环队列可以是单链表、队列来实现。
class Queue{
...
getIndex(index){
const length = this.items.length;
return index > length ? index % length : index;
}
find(index){
return !this.isEmpty ? this.items[this.getIndex(index)] : null;
}
get isEmpty(){
return !this.items.lenght;
}
...
}
链表
存储多个元素,使用数组(列表)是最常用的数据结构。
每种语言都实现了数组,这种数据结构清晰简便。
但是,这个数据结构有一个缺点,数据的大小是固定的,所以在数组的起点或中间插入或移除项的成本很高,例如:移除第一项,你就需要把第一项其后的每一项往前移一位。
虽然有Array
提供的api,但是其背后的执行机制同样如此。
所以,链表出现了。链表存储元素在内存中不是连续放置的,每个元素由一个存储元素本身的节点和一个指向下一个节点的引用组成。
{val: 1, nextNode: {val: 2, nextNode: null}}
使用链表的好处就是,添加和移除元素的时候不需要移动其他的元素,只需要改变某两个(或一个)的下一个节点的引用就行。
值得注意的是,如果想要获取链表中间的元素,必须重头开始。
双向链表
双向链表不同于链表的是,他的链条是双向的,不仅可以链向下一个,也可以链向上一个。
这样的链表就有两种迭代列表的方式,要么重头到尾,要么反过来。
也可以得到特定的节点后,上一个或者下一个元素。
class Node{
constructor(val){
this.val = val;
this.prev = null;
this.next = null;
}
}
class DoublyLinkedList{
constructor(){
this.head = null;
this.tail = null;
this.length = 0;
}
// 在指定位置插入节点
insert(position, val){
if( !(position >= 0 && this.length >= position) ) return false;
const node = new Node(val);
let
current = this.head,
index = 0;
if( position == 0 ) {
if( this.length == 0 ){
this.head = node;
this.tail = node;
} else{
node.next = current;
current.prev = node;
current = node;
}
}
else if(position === this.length){
current = this.tail;
current.next = node;
node.prev = current;
this.tail = node;
}
else{
while(index++ < position){
current = current.next;
}
node.next = current;
node.prev = current.prev;
current.prev = node;
}
this.length++;
return true;
}
// 删除指定位置的节点
removeAt(position){
if( !(position > -1 && this.length > position) ) return false;
let
current = this.head,
index = 0;
if( position === 0 ){
this.length === 1 && (this.tail = null);
this.head = current.next;
this.head.prev = null;
}
else if( position === this.length ){
this.tail = this.tail.prev;
this.tail.next = null;
}
else{
while(index++ < position){
current = current.next;
}
current.next.prev = current.prev;
current.prev.next = current.next;
}
this.length--;
return true;
}
}
循环链表
循环链表大致和双向链表相同,唯一的区别就是最后一个元素的next
指向了第一个元素而不是指向null
(这个是单向循环链表)
若是第一个节点prev
定向最后一个节点,那么这个是一个双向循环链表。
优点
链表相对于数组的最重要的优点就是,无需移动链表中所有的元素,就可以轻松的添加和移除元素。
所以如果有大量需要添加移除元素时,最好的选着是链表而不是数组。
集合
集合就是由一组无需并且唯一的项组成的,这个数据结构使用了与有限集合相同的数据概念。
一个集合大概是这样的:let N = { 0,1,2,3,4,5,... }
,集合是一个{}
。