1 栈
先进后出
1.1 栈的操作
push:添加一个元素到栈顶
pop:移除栈顶元素,同时返回被移除的元素
peek:返回栈顶元素
isEmpty:如果栈没有任何元素就返回true
size:栈元素的个数
toString:将栈结构内容以字符串形式返回
1.2 栈结构的实现
function Stack(){
this.items=[]
Stack.prototype.push=function(element){
this,items.push(element)
}
Stack.prototype.pop=function(){
return this.items.pop()
}
Stack.prototype.peek=function(){
return this.items[this.items.length-1]
}
Stack.prototype.isEmpty=fucntion(){
return this.items.length===0
}
Stack.prototype.size=function(){
return this.items.length
}
Stack.prototype.toString=function(){
return this.items.join('')
}
}
2 队列
先进先出,只允许在表前端进行删除,在表后端进行插入
2.1 队列的操作
enqueue:向队列尾部添加一个元素
dequeue:移除队列的第一项,并返回被移除的元素
front:返回队列的第一个元素
2.2 队列的实现
function queue(){
this.items=[]
queue.prototype.enqueue=function(element){
this.items.push(element)
}
queue.prototype.dequeue=function(){
return this.items.splice(0,1)
}
queue.prototype.front=function(){
return this.items[0]
}
}
2.3 实现击鼓传花
function passGame(group,num){
var queue=new Queue()
group.forEach(p=>queue.enqueue(p))
var queue=new Queue()
group.forEach(p=>{
queue.enqueue(p)
})
let count=0
while(queue.size()>1){
if(count===num-1){
queue.dequeue()
count=0
}
queue.enqueue(queue.dequeue())
count++
}
}
2.4 优先级队列
function PriorityQueue(){
function QueueElement(element,priority){
this.element=element
this.priority=priority
}
this.items=[]
PriorityQueue.prototype.enqueue=function(element,priority){
var queueElement=new QueueElement(element,priority)
if (this.items.length===0){
this.items.push(queueElement)
} else{
var add=false
for(let i=0;i<this.items.length;i++){
if(queueElement.priority<this.items[i].priority){
this.items.splice(i,0,queueElement)
add=true
break
}
}
if(!add){
this.items.push(queueElement)
}
}
}
PriorityQueue.prototype.dequeue=function(){
return this.items.shift()
}
PriorityQueue.prototype.front=function(){
return this.items[0]
}
PriorityQueue.prototype.isEmpty=function(){
return this.items.length===0
}
PriorityQueue.prototype.size=function(){
return this.items.length
}
PriorityQueue.prototype.toString=function(){
return this.items.join('')
}
}
3 链表
链表的每一个元素由一个存储元素本身的节点和一个指向下一个元素的引用组成
缺点:
1、链表访问任何位置的元素时,都需要从头开始访问
2、无法通过下标访问元素
3.1 链表常见操作
append:向链表尾部添加一个元素
insert:向链表的特定位置插入一个新的项
get:获取对应位置的数据
indexOf:返回元素在列表中的索引
update:修改某个位置的元素
removeAt:从链表特定位置移除一项
remove:移除一项
3.2 链表的实现
function Linklist(){
function Node(data){
this.data=data
this.next=null
}
this.head=null
this.length=0
Linklist.prototype.append=function(data){
var newNode=new Node(data)
if(this.length===0){
this.head=newNode
}else{
var current=this.head
while(current.next){
current=current.next
}
current.next=newNode
}
this.length++
}
Linklist.prototype.toString=function(){
var current =this.head
var res=""
while(current){
res+=current.data
current=current.next
}
return res
}
Linklist.prototype.insert=function(index,data){
if(index<0||index>this.length)return false
var current=this.head
var prev=null
var newNode=new Node(data)
if(index===0){
this.head=newNode
newNode.next=current
this.length++
}else{
let i=0
while(i++<index){
prev=current
current=current.next
}
newNode.next=current
prev.next=newNode
this.length++
}
}
Linklist.prototype.get=function(index){
if(index<0||index>this.length-1)return false
var current=this.head
var i=0
while(i++<index){
current=current.next
}
return current
}
Linklist.prototype.indexOf=function(data){
var current=this.head
let i=0
while(i<this.length){
if(current.data===data){
return i
}
current=current.next
i++
}
return false
}
Linklist.prototype.update=function(index,data){
if(index<0||index>this.length-1)return 0
var current=this.head
let i=0
while(i++<index){
current=current.next
}
current.data=data
}
Linklist.prototype.removeAt=function(index){
if(index<0||index>this.length-1)return false
if(index===0){
this.head=this.head.next
this.length--
}
var current=this.head
var prev=null
let i=0
while(i++<index){
prev=current
current=current.next
}
prev.next=current.next
this.length--
}
Linklist.prototype.remove=function(data){
var current=this.head
var prev=null
let i=0
while(i<this.length){
if(current.data===data){
if(i===0){
this.head=current.next
this.length--
break
}else{
prev.next=current.next
this.length--
}
}
prev=current
current=current.next
i++
}
}
Linklist.prototype.size=function(){
return this.length
}
Linklist.prototype.isEmpty=function(){
return this.length===0
}
}
4 集合
add:向集合添加一个新元素
remove:向集合移除一个值
has:如果值在集合中,返回true
clear:清除集合中所有项
size:返回集合包含的所有元素
values:返回一个包含集合中所有值的数组
4.1 集合的实现
function Set(){
this.items={}
Set.prototype.add=function(value){
if(this.has(value)){
return false
}
this.items[value]=value
}
Set.prototype.has=function(value){
return this.items.hasOwnProperty(value)
}
Set.prototype.remove=function(value){
if(this.has(value)){
delete this.items[value]
return true
}
return false
}
Set.prototype.clear=function(){
this.items={}
}
Set.prototype.size=function(){
return Object.keys(this.items).length
}
Set.prototype.values=function(){
let arr=[]
Object.keys(this.items).forEach(key=>arr.push(this.items[key]))
return arr
}
//求并集
Set.prototype.union=function(otherSet){
var union=new Set()
var values=this.values()
values.forEach(val=>union.add(val))
otherSet.values().forEach(val=>{
if(!union.has(val)){
union.add(val)
}
})
return union.values()
}
//求交集
Set.prototype.inter=function(otherSet){
var union=new Set()
var values=this.values()
values.forEach(val=>{
if(otherSet.has(val)){
union.add(val)
}
})
return union.values()
}
//求差集
Set.prototype.diff=function(otherSet){
otherSet.values().forEach(val=>{
if(this.has(val)){
this.remove(val)
}
})
return this.items
}
//求子集
Set.prototype.subset=function(otherSet){
let flag=true
otherSet.values().forEach(val=>{
if(!this.has(val)){
flag=false
}
})
return flag
}
}
5 哈希表
哈希化:将大数字转化成数组范围内下标的过程
哈希冲突:将字符串转化成下标的过程出现重复,导致有两个字符串对应于同一个位置
解决哈希冲突的方法:链地址法、开放地址法
哈希表的长度最好使用质数,质数使分布更加均匀
5.1 哈希表的实现
function HashTable(){
this.storage=[]
this.count=0
this.limit=7
HashTable.prototype.hashFunc=function(str,size){
var hashcode=0
for(let i=0;i<str.length;i++){
hashcode=37*hashcode+str.charCodeAt(i)
}
index=hashcode%size
return index
}
HashTable.prototype.insert=function(key,value){
var index=this.hashFunc(key,this.limit)
var bucket=this.storage[index]
if(bucket==null){
bucket=[]
this.storage[index]=bucket
}
for(let i=0;i<bucket.length;i++){
var tuple=bucket[i]
if(tuple[0]===key){
tuple[1]=value
return
}
}
bucket.push([key,value])
this.count++
if(this.count>this.limit*0.75){
var newSize=this.limit*2
var newPrime=this.getPrime(newSize)
this.resize(newPrime)
}
}
HashTable.prototype.get=function(key){
var index=this.hashFunc(key,this.limit)
var bucket=this.storage[index]
if(bucket==null)return null
for(let i=0;i<bucket.length;i++){
var tuple=bucket[i]
if(tuple[0]===key)return tuple[1]
}
return null
}
HashTable.prototype.remove=function(key){
var index=this.hashFunc(key,this.limit)
var bucket=this.storage[index]
if(bucket==null)return false
for(let i=0;i<bucket.length;i++){
var tuple=bucket[i]
if(tuple[0]===key){
bucket.splice(i,1)
this.count--
if(this.limit<7 &&this.count<this.limit*0.25){
var newSize=Math.floor(this.limit/2)
var newPrime=this.getPrime(newSize)
this.resize(newPrime)
}
return tuple[1]
}
}
}
HashTable.prototype.isEmpty=function(){
return this.count==0
}
HashTable.prototype.size=function(){
return this.count
}
HashTable.prototype.resize=function(newLimit){
var oldStorage=this.storage
this.storage=[]
this.count=0
this.limit=newLimit
for(let i=0;i<oldStorage.length;i++){
var bucket=oldStorage[i]
if(bucket===null){
continue
}
for(let j=0;j<bucket.length;j++){
var tuple=bucket[j]
this.insert(tuple[0],tuple[1])
this.count++
}
}
}
HashTable.prototype.isPrime=function(num){
for(let i=2;i<=parseInt(Math.sqrt(num));i++){
if(num%i===0){
return false
}
}
return true
}
HashTable.prototype.getPrime=function(num){
while(!this.isPrime(num)){
num++
}
return num
}
}
5.2 判断一个数是质数
只能被1和自己整除
function isPrime(num){
for(let i=2;i<=parseInt(Math.sqrt(num));i++){
if(num%i===0){
return false
}
}
return true
}
6 树
二叉搜索树:非空左子树的键值小于根节点的键值,非空右子树的所有键值大于根节点的键值
二叉搜索树的常见操作:
insert:向书中插入一个新的键
search:查找一个键
inOrderTraverse:通过中序遍历方式遍历所有节点
preOrderTraverse:通过先序遍历方式遍历所有节点
postOrderTraverse:通过后序遍历方式遍历所有节点
remove:从树中移除某个键
(1)先序遍历
先访问根节点,在先序遍历左子树,再先序遍历右子树
(2)中序遍历
先中序遍历左子树,访问根节点,再中序遍历右子树
(3)后序遍历
先后序遍历左子树,再后序遍历右子树,最后访问根节点
6.1 二叉搜索树的实现
function BinarySearchTree(){
function Node(key){
this.key=key
this.left=null
this.right=null
}
this.root=null
BinarySearchTree.prototype.insert=function(key){
var node=new Node(key)
if(!this.root){
this.root=node
}else{
this.insertNode(this.root,node)
}
}
BinarySearchTree.prototype.insertNode=function(node,newNode){
if(newNode.key<node.key){
if(!node.left){
node.left=newNode
}else{
this.insertNode(node.left,newNode)
}
}else{
if(!node.right){
node.right=newNode
}else{
this.insertNode(node.right,newNode)
}
}
}
BinarySearchTree.prototype.preorderTraverse=function(handler){
this.preorderTraverseNode(this.root,handler)
}
BinarySearchTree.prototype.preorderTraverseNode=function(node,handler){
if(!node)return null
handler(node.key)
node.left&&this.preorderTraverseNode(node.left,handler)
node.right&&this.preorderTraverseNode(node.right,handler)
}
BinarySearchTree.prototype.inorderTraverse=function(handler){
this.inorderTraverseNode(this.root,handler)
}
BinarySearchTree.prototype.inorderTraverseNode=function(node,handler){
if(!node)return null
node.left&&this.inorderTraverseNode(node.left,handler)
handler(node.key)
node.right&&this.inorderTraverseNode(node.right,handler)
}
BinarySearchTree.prototype.postorderTraverse=function(handler){
this.postorderTraverseNode(this.root,handler)
}
BinarySearchTree.prototype.postorderTraverseNode=function(node,handler){
if(!node)return null
node.left&&this.inorderTraverseNode(node.left,handler)
node.right&&this.inorderTraverseNode(node.right,handler)
handler(node.key)
}
BinarySearchTree.prototype.max=function(){
var node=this.root
while(node.right){
node=node.right
}
return node.key?node.key:null
}
BinarySearchTree.prototype.min=function(){
var node=this.root
while(node.left){
node=node.left
}
return node.key?node.key:null
}
BinarySearchTree.prototype.search=function(key){
var node=this.root
while(node){
if(node.key>key){
node=node.left
}else if(node.key<key){
node=node.right
}else{
return true
}
}
return false
}
BinarySearchTree.prototype.remove=function(key){
//先查找节点
var node=this.root
var isLeftChild=true
var parent=null
while(node.key!==key){
parent=node
if(key<node.key){
node=node.left
isLeftChild=true
}else if(key>node.key){
node=node.right
isLeftChild=false
}
if(node===null)return false
}
// 找到节点后判断子节点
if(!node.left&&!node.right){
if(node==this.root){
this.root=null
}else{
// 子节点不存在,直接删除,并让父节点指向为空
if(isLeftChild){
parent.left=null
}else{
parent.right=null
}
}
}else if(!node.left||!node.right){
if(node==this.root){
this.root=node.left||node.right
}else{
// 只存在一个子节点
if(isLeftChild){
parent.left=node.left||node.right
}else{
parent.right=node.left||node.right
}
}
}else{
// 存在两个子节点
// 从左子树中找最大的,右子树中找最小的
var delNode=this.getSuccess(node)
if(node==this.root){
this.root=delNode
}else{
if(isLeftChild){
parent.left=delNode
}else{
parent.right=delNode
}
}
delNode.left=node.left
}
}
BinarySearchTree.prototype.getSuccess=function(delNode){
var replaceNode=delNode
var current=delNode.right
var replaceNodeParent=delNode
while(!current){
replaceNodeParent=replaceNode
replaceNode=current
current=current.left
}
if(replaceNode!==delNode.right){
replaceNodeParent.left=delNode.right
replaceNode.right=delNode.right
}
return replaceNode
}
}
7 图论
function Graph(){
this.vertexes=[]
this.edges=new Dictionary()
Graph.prototype.addVertex=function(v){
this.vertexes.push(v)
this.edges.set(v,[])
}
Graph.prototype.addEdge=function(v1,v2){
this.edges.get(v1).push(v2)
this.edges.get(v2).push(v1)
}
Graph.prototype.initializeColor=function(){
var color=[]
for(let i=0;i<this.vertexes.length;i++){
color[this.vertexes[i]]='white'
}
return color
}
Graph.prototype.bfs=function(initV){
var queue=new Queue()
var color=this.initializeColor()
let res=''
queue.enqueue(initV)
while(!queue.isEmpty()){
let v=queue.dequeue()
let vList=this.edges.get(v)
color[v]='grey'
for(let i=0;i<vList.length;i++){
let e=vList[i]
if(color[e]=='white'){
queue.enqueue(e)
color[e]='grey'
}
}
res+=v
color[v]='black'
}
return res
}
Graph.prototype.dfs=function(initV){
let color=this.initializeColor()
let res=[]
const dfsVisit=function(v,color){
color[v]='grey'
let vList=this.edges[v]
for(let i=0;i<vList.length;i++)
{
let e=vList[i]
if(color[e]=='white'){
dfsVisit(e,color)
}
}
color[v]='black'
}
dfsVisit(initV,color)
}
}